Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with Injection of DAO #4244

Open
RuralNative opened this issue Feb 15, 2024 · 1 comment
Open

Problem with Injection of DAO #4244

RuralNative opened this issue Feb 15, 2024 · 1 comment

Comments

@RuralNative
Copy link

First off, I will provide the codes for my Room Database, DAO, Repository, DI, ViewModel, and Composable

Database:
`package com.ruralnative.handsy.data

import android.content.Context
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.Database
import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.entities.AlphabetLesson
import com.ruralnative.handsy.data.entities.PhrasesLesson
import com.ruralnative.handsy.data.entities.User
import dagger.Binds
import javax.inject.Inject
import javax.inject.Singleton

@database(
entities = [
User::class,
AlphabetLesson::class,
PhrasesLesson::class
],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun alphabetLessonDao(): AlphabetLessonDao
abstract fun phrasesLessonDao(): PhrasesLessonDao
}`

UserDAO:
``
package com.ruralnative.handsy.data.dao

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.ruralnative.handsy.data.entities.PhrasesLesson
import com.ruralnative.handsy.data.entities.User
import kotlinx.coroutines.flow.Flow

@dao
interface UserDao {

@Query("SELECT * from user")
fun selectAllUsers(): Flow<List<User>>

@Query("SELECT * from user WHERE id = :userID")
fun selectUserById(userID: Int): Flow<User>

@Query("SELECT COUNT(*) from user")
fun countUsers(): Flow<Int>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)

@Update
suspend fun updateUser(user: User)

@Query("UPDATE user SET user_name = :userName WHERE id = :userID")
suspend fun updateUserName(userName: String?, userID: Int)

@Query("UPDATE user SET is_new_user = :boolValue WHERE id = :userID")
suspend fun updateUserStatus(boolValue: Int, userID: Int)

@Query("UPDATE user SET progression_level = :userLevel WHERE id = :userID")
suspend fun updateUserProgressionLevel(userLevel: Int, userID: Int)

@Delete
suspend fun deleteUser(user: User)

}
``

Repository:
`package com.ruralnative.handsy.data.repository

import androidx.annotation.WorkerThread
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.entities.User
import com.ruralnative.handsy.di.qualifiers.UserDAO
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import javax.inject.Inject
import javax.inject.Singleton

class UserRepository @Inject constructor(
private val dao: UserDao
) {
val allUsers: Flow<List> = dao.selectAllUsers()

fun getUserByID(userID: Int): Flow<User> {
    return dao.selectUserById(userID)
}

fun isThereNoUser(): Flow<Boolean> = flow {
    val numberOfUsers: Int? = dao.countUsers().firstOrNull()
    var isUserEmpty = true

    if (numberOfUsers == 0) {
        isUserEmpty = true
    } else if (numberOfUsers != 0) {
        isUserEmpty = false
    }
    emit(isUserEmpty)
}

suspend fun insertUser(user: User) {
    dao.insertUser(user)
}

suspend fun insertNewUser(id: Int, name: String) {
    val user = User(
        id,
        name,
        0,
        1
    )
    dao.insertUser(user)
}

suspend fun updateUser(user: User) {
    dao.updateUser(user)
}

suspend fun updateUserNameWithID(userName: String?, userID: Int) {
    dao.updateUserName(userName, userID)
}

suspend fun updateUserStatusWithID(boolValue: Int, userID: Int) {
    dao.updateUserStatus(boolValue, userID)
}

suspend fun updateUserLevelWithID(userLevel: Int, userID: Int) {
    dao.updateUserProgressionLevel(userLevel, userID)
}

suspend fun deleteUser(user: User) {
    dao.deleteUser(user)
}

}`

ViewModel:
`package com.ruralnative.handsy.ui.entryUI

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ruralnative.handsy.data.repository.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class EntryViewModel @Inject constructor(
private val repository: UserRepository
): ViewModel() {

fun checkUserCountAndNavigate(
    navigateToInitial: () -> Unit,
    navigateToMain: () -> Unit
) {
    Log.d("ENTRY_SCREEN", "checkUserCountAndNavigate() EXECUTED")
    viewModelScope.launch {
        delay(5000)
        val isThereNoUser: Boolean = repository.isThereNoUser().first()
        if (!isThereNoUser) {
            navigateToInitial()
        } else {
            navigateToMain()
        }
    }
}

}`

DatabaseModule (Hilt):
`package com.ruralnative.handsy.di

import android.content.Context
import androidx.room.Room
import com.ruralnative.handsy.data.AppDatabase
import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.repository.AlphabetLessonRepository
import com.ruralnative.handsy.data.repository.PhrasesLessonRepository
import com.ruralnative.handsy.data.repository.UserRepository
import com.ruralnative.handsy.di.qualifiers.AlphabetDAO
import com.ruralnative.handsy.di.qualifiers.AlphabetRepo
import com.ruralnative.handsy.di.qualifiers.Database
import com.ruralnative.handsy.di.qualifiers.PhrasesDAO
import com.ruralnative.handsy.di.qualifiers.PhrasesRepo
import com.ruralnative.handsy.di.qualifiers.UserDAO
import com.ruralnative.handsy.di.qualifiers.UserRepo
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@module
@Installin(SingletonComponent::class)
object DatabaseModule {

@Singleton
@Provides
@Database
fun provideLocalDatabase(
    @ApplicationContext context: Context
): AppDatabase {
    return Room.databaseBuilder(
        context.applicationContext,
        AppDatabase::class.java,
        "app_database.db"
    )
        .fallbackToDestructiveMigration()
        .createFromAsset("database.db")
        .build()
}

@Singleton
@Provides
@UserDAO
fun provideUserDao(
    @Database appDatabase: AppDatabase
): UserDao {
    return appDatabase.userDao()
}

@Singleton
@Provides
@AlphabetDAO
fun provideAlphabetDao(
    @Database appDatabase: AppDatabase
): AlphabetLessonDao {
    return appDatabase.alphabetLessonDao()
}

@Singleton
@Provides
@PhrasesDAO
fun providePhrasesDao(
    @Database appDatabase: AppDatabase
): PhrasesLessonDao {
    return appDatabase.phrasesLessonDao()
}

}
`

ViewModelModule:
`package com.ruralnative.handsy.di

import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.repository.AlphabetLessonRepository
import com.ruralnative.handsy.data.repository.PhrasesLessonRepository
import com.ruralnative.handsy.data.repository.UserRepository
import com.ruralnative.handsy.di.qualifiers.AlphabetDAO
import com.ruralnative.handsy.di.qualifiers.AlphabetRepo
import com.ruralnative.handsy.di.qualifiers.PhrasesDAO
import com.ruralnative.handsy.di.qualifiers.PhrasesRepo
import com.ruralnative.handsy.di.qualifiers.UserDAO
import com.ruralnative.handsy.di.qualifiers.UserRepo
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped

@module
@Installin(ViewModelComponent::class)
object ViewModelModule {

@Provides
@ViewModelScoped
@UserRepo
fun provideUserRepository(
    @UserDAO dao: UserDao
): UserRepository {
    return UserRepository(dao)
}

@Provides
@ViewModelScoped
@AlphabetRepo
fun provideAlphabetRepository(
    @AlphabetDAO dao: AlphabetLessonDao
): AlphabetLessonRepository {
    return AlphabetLessonRepository(dao)
}

@Provides
@ViewModelScoped
@PhrasesRepo
fun providePhrasesRepository(
    @PhrasesDAO dao: PhrasesLessonDao
): PhrasesLessonRepository {
    return PhrasesLessonRepository(dao)
}

}`

Here is the error:
`F:\Programming\Handsy\app\build\generated\hilt\component_sources\debug\com\ruralnative\handsy\Application_HiltComponents.java:141: error: [Dagger/MissingBinding] com.ruralnative.handsy.data.dao.UserDao cannot be provided without an @Provides-annotated method.
public abstract static class SingletonC implements Application_GeneratedInjector,
^

Missing binding usage:
com.ruralnative.handsy.data.dao.UserDao is injected at
com.ruralnative.handsy.data.repository.UserRepository(dao)
com.ruralnative.handsy.data.repository.UserRepository is injected at
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel(repository)
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel is injected at
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel_HiltModules.BindsModule.binds(vm)
@dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>> is requested at
dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.ruralnative.handsy.Application_HiltComponents.SingletonC ? com.ruralnative.handsy.Application_HiltComponents.ActivityRetainedC ? com.ruralnative.handsy.Application_HiltComponents.ViewModelC]`

Already stuck with this error for 3 weeks. I tried everything, nothing at all helped. Please help

@Chang-Eric
Copy link
Member

Generally questions like this are better for StackOverflow.

However, I believe the issue is you are providing your UserDAO using a qualifier here:

@Singleton
@Provides
@UserDAO
fun provideUserDao(
    @Database appDatabase: AppDatabase
): UserDao {
    return appDatabase.userDao()
}

but are injecting it without the @UserDao qualifier here:

class UserRepository @Inject constructor(
private val dao: UserDao
) {

How to fix it is a bit up to you. You could either add a qualifier where you use it in UserRepository or you could just remove the qualifier from the @Provides. The reason I mention the latter is that qualifiers are used for differentiating bindings, so it'd usually make sense to use one if you had two different UserDaos, like @Red UserDao and @Blue UserDao. Something like @UserDAO UserDao doesn't really differentiate much, so you might not need the qualifier in the first place if you only plan on having one UserDao binding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants