1

I have created an ASP.NET Core 8.0 MVC app.

Here is my AppDbContext class:

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    public DbSet<User> Users { get; set; }
}

DbRepo class:

public class DbRepo
{
    internal readonly AppDbContext db;
    internal DbRepo(AppDbContext context)
    {
        this.db = context;
    }
}

Here's a user repository where I need to use DbContext, there could be multiple repositories:

public class UserRepository : DbRepo, IUser<User, User, string>
{
    public async Task<bool> Create(User user)
    {
        try
        {
            if (!await IsExist(user))
            {
                await db.AddAsync(user);
            }
            return db.SaveChanges() > 0;
        }
        catch (Exception)
        {
            return false;
        }
    }
}

This is the DataAccessFactory class for loosely coupled dependencies:

public class DataAccessFactory
{
    public static IUser<User, User, string> UserData()
    {
        return new UserRepository();
    }
}

Now service will call to the DataAccessFactory to repository methods:

public class UserService
{
    public static Task<bool> CreateUser(UserDTO newUser)
    {
        User user = UserMapping(newUser);
        return DataAccessFactory.UserData().Create(user);
    }
}

And then this service will call from controller. Here is the problem, there should to give an argument for inherit DbRepo class for every repo.

How could I solve it?

I added this in Program.cs:

builder.Services.AddScoped<DbRepo>();
New contributor
Reyad is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1
  • 1
    The IUser interface is unclear. What is it? Why does the repo implement it? UserRepository is what returns a user. How is it also a user? Commented Jul 5 at 18:30

1 Answer 1

2

There's a confusing detail which is that UserRepository implements an interface called IUser. The class name describes something that stores or retrieves users. It wouldn't be a user. But I'm going to sidestep that because it's possible to answer the question anyway.

This will interfere with inversion of control:

public class UserService
{
    public static Task<bool> CreateUser(UserDTO newUser)
    {
        User user = UserMapping(newUser);
        return DataAccessFactory.UserData().Create(user);
    }
}

UserService depends on UserRepository, but it controls how it is created. It's going to call DataAccessFactory.UserData().Create(user); which calls new UserRepository().

The way it's written there's no way to change that dependency. You could get rid of DataAccessFactory and just directly call new UserRepository() and the result would be identical. There's a fixed dependency on UserRepository.

Instead of UserService making that decision, you can change it like this:

public class UserService
{
    private readonly UserRepository _userRepository;

    public UserService(UserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    // no longer static
    public Task<bool> CreateUser(UserDTO newUser)
    {
        User user = UserMapping(newUser);
        return _userRepository.CreateUser
    }
}

Now UserService doesn't control creation of UserRepository. Something else must create that repository and pass it to the UserService constructor.

As a result, now the argument doesn't strictly have to be a UserRepository. It can also be any type that inherits from UserRepository. That means the class is no longer coupled to that exact type.

That's an improvement, but this is where we often add an interface like IUserRepository. That's not strictly necessary but there are good reasons for it.

public interface IUserRepository
{
    Task<bool> Create(User user);
}

UserRepository would implement that interface.

Then we would change UserService to depend on the interface instead of the concrete class.

public class UserService
{
    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    // no longer static
    public Task<bool> CreateUser(UserDTO newUser)
    {
        User user = UserMapping(newUser);
        return _userRepository.CreateUser
    }
}

Now UserService only knows about the interface. We can pass to it any class that implements it. That's useful for testing. We can pass a fake or mocked implementation of IUserRepository that always returns some expected value so we can verify that UserService does what we expect in response to various results it receives.

At runtime (when the application actually runs) you probably want the implementation of IUserRepository to be a specific concrete class like UserRepository. Assuming that you're using Microsoft's dependency injection container, you'll have code in Startup or Program that registers dependencies with an IServiceCollection. (I can't be more specific without knowing how your app is configured.)

In that method you'll configure the application to create various dependencies. For example, if you've modified UserService to depend on IUserRepository, that code might look like this:

services.AddScoped<IUserRepository, UserRepository>();

That means

When you have to supply an IUserRepository to some class's constructor, create an instance of UserRepository.

I've glossed over dependency injection some. You can read more here.

But as it directly relates to your question, this change implements inversion of control. Instead of UserService saying

I need a repository so I'm going to create one (thereby controlling how it gets created)

it says

Please inject an IUserRepository into my constructor when you create me. I don't control how it is created. The application controls that.

Not the answer you're looking for? Browse other questions tagged or ask your own question.