如何在存储库中使用数据库上下文来实现 ASP .NET Core MVC 中的依赖倒置主体

问题描述 投票:0回答:1

我在.Net 8.0上创建了一个asp .net core mvc,

在这里,我创建了AppDbContext类:

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

DbRepo 类:

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

这是一个用户存储库,我需要在其中使用数据库上下文,可能有多个存储库:

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;
        }
    }
}

这里是用于松散耦合依赖项的 DataAccessFactory 类:

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

现在服务将调用 dataAccessfactory 来存储库方法:

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

然后该服务将从控制器调用。问题是,应该为每个存储库提供一个继承 DbRepo 类的参数。

我该如何解决。

我在 Program.cs 中添加了这个

builder.Services.AddScoped<DbRepo>();
c# asp.net .net asp.net-mvc solid-principles
1个回答
0
投票

有一个令人困惑的细节,那就是

UserRepository
实现了一个名为
IUser
的接口。类名描述了存储或检索用户的内容。它不会是“用户”。但我要回避这个问题,因为无论如何都有可能回答这个问题。 这会干扰控制反转:

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

UserService

取决于

UserRepository
,但它控制它的创建方式。它将调用
DataAccessFactory.UserData().Create(user);
,它又调用
new UserRepository()
按照它的写法,没有办法改变这种依赖关系。您可以摆脱 

DataAccessFactory

并直接调用

new UserRepository()
,结果将是相同的。对
UserRepository
有固定的依赖。
您可以像这样更改它,而不是

UserService

做出该决定:

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
    }
}

现在
UserService

不控制

UserRepository
的创建。必须有其他东西创建该存储库并将其传递给
UserService
构造函数。
因此,现在参数不一定必须是 

UserRepository

。它也可以是继承自

UserRepository
的任何类型。这意味着该类不再与该确切类型耦合。
这是一个改进,但这就是我们经常添加像

IUserRepository

这样的界面的地方。这并不是绝对必要的,但有充分的理由。

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

UserRepository

将实现该接口。

然后我们将更改 

UserService

以依赖于接口而不是具体类。

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
    }
}

现在
UserService

只知道界面。我们可以将任何实现它的类传递给它。这对于测试很有用。我们可以传递

IUserRepository
的伪造或模拟实现,它总是返回一些预期值,这样我们就可以验证
UserService
是否按照我们期望的方式响应它收到的各种结果。
在运行时(当应用程序实际运行时),您可能希望 

IUserRepository

的实现成为像

UserRepository
这样的特定具体类。假设您使用 Microsoft 的依赖项注入容器,您将在
Startup
Program
中拥有代码,用于使用
IServiceCollection
注册依赖项。 (如果不知道您的应用程序是如何配置的,我无法更具体。)
在该方法中,您将配置应用程序以创建各种依赖项。例如,如果您已修改 

UserService

以依赖于

IUserRepository
,则该代码可能如下所示:
services.AddScoped<IUserRepository, UserRepository>();

这意味着

当您必须向某个类的构造函数提供
IUserRepository

时,请创建

UserRepository
的实例。

我掩盖了一些依赖注入。您可以在
这里

阅读更多内容。 但由于它与您的问题直接相关,因此此更改实现了控制反转。而不是

UserService


我需要一个存储库,因此我将创建一个存储库(从而控制它的创建方式)

它说

请在创建我时将
IUserRepository

注入到我的构造函数中。我无法控制它的创建方式。应用程序控制它。


    

© www.soinside.com 2019 - 2024. All rights reserved.