【问题标题】:How to avoid using lots of dbcontexts in .NET using MVVM?如何避免使用 MVVM 在 .NET 中使用大量 dbcontexts?
【发布时间】:2018-05-23 16:56:48
【问题描述】:

我尝试学习使用 MVVM 模式在 .NET 中构建 Web 应用程序。我看了一些教程,有一点我不明白。

每个ViewModel 包含:

public class IndexModel : PageModel
{
    private readonly ApplicationDbContext _dbContext;

    public IndexModel(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    ..
}

当您有几页(最多 10 页)时,复制粘贴它很好,但是我的应用程序开始增长,并且使用这种方法开始感觉非常多余。

我找不到另一个帖子来回答我的问题,但我想知道是否有任何其他方法可以在每个 ViewModel 上使用 _dbContext 而不必使用这种冗余?我对设计模式不是很熟悉,但我的创业公司可以选择单例模式吗?

【问题讨论】:

  • 让你的所有视图模型都派生自一个通用的抽象基类
  • 什么版本的.Net?正如其他地方所提到的,依赖注入是关键(你推测的单例),但“如何”因框架而异。
  • 我正在使用最新版本的 .NET,但抽象基类可能是要走的路
  • 我不同意数据库上下文的抽象基类。 MVVM 的模型 (VM) 部分不应该知道您的数据库,它应该只包含绑定其关联视图 (V) 所需的字段。
  • @PmanAce 在 Razor Pages 应用程序中,视图模型类基本上也是控制器。视图页面 (.cshtml) 只能访问视图模型类的公共属性,并且这些属性应该在页面被调用时被填充。为视图模型类提供 DbContext 非常好。

标签: c# mvvm asp.net-core razor-pages


【解决方案1】:

添加一个继承自 PageModel 的自定义基类。然后从您的自定义基类继承您的所有模型。

public abstract class MyBasePageModel : PageModel
{
   protected readonly ApplicationDbContext _dbContext;
   public MyBaseModel(ApplicationDbContext dbContext)
   {
      _dbContext = dbContext;
   }
}


public class IndexModel : MyBasePageModel 
{
    public IndexModel(ApplicationDbContext dbContext):base(dbContext)
    {
      // 
    }
}

【讨论】:

  • 不错的选择,但是派生类仍然必须在自己的构造函数中调用基构造函数,所以仍然存在一些重复。
  • 这是构造函数注入,这可以使用任何 DI 容器自动化。如果上下文注册到它的容器中,MVC 会自动执行此操作。
  • 这是一个不同的概念(但完全正确)。我的观察是,当前形式的 this 代码不会编译,因为派生类需要调用基构造函数。 DI 容器解决了不同的问题。
  • 即使使用unity DI,继承的类也必须调用基础构造函数,从而传递ApplicationDbContext。
  • @Alejandro 我同意,更新了代码。我只是懒惰。
【解决方案2】:

这不是合理的设计模式。您可以做的是使用依赖注入将业务接口作为注入依赖项传递给您的视图模型(Unity 示例)。然后,注入的业务服务可以向其中注入数据服务接口,在该接口中可以找到数据上下文。

将数据层放在表示层中被认为是不好的做法。这是一个关于如何分离代码层的示例方法。

下面是 IDataService 的简单示例(注意我只处理接口,数据上下文保留在数据服务中):

public class DataService : ServiceBase, IDataService
{
    public DataService(IMapper mapper) : base(mapper) { }

    public IList<UserDto> GetUsers(bool runSafeMode = true)
    {
        Func<IList<UserDto>> action = () =>
        {
            return GetUsers(_ => true);
        };

        return ExecutorHandler(action, runSafeMode);
    }

    ...

    private IList<UserDto> GetUsers(Expression<Func<User, bool>> predicate, bool runSafeMode = true)
    {
        Func<IList<UserDto>> action = () =>
        {
            using (var ymse = YMSEntities.Create())
            {
                var users = ymse.User
                    .Include(u => u.UserUserProfile)
                    .Include(m => m.UserUserProfile.Select(uup => uup.UserProfile))
                    .Include(m => m.UserUserProfile.Select(uup => uup.User))
                    .Include(m => m.UserUserProfile.Select(uup => uup.UserProfile.UserProfileModule))
                    .Where(predicate).OrderBy(u => u.UserName).ToList();

                return MappingEngine.Map<IList<UserDto>>(users);
            }
        };

        return ExecutorHandler(action, runSafeMode);
    }
}

这会被注入到业务服务中,而业务服务又会被注入到我的虚拟机中:

public class DocksViewModel : ViewModelBase
{
    public DocksViewModel(IConfigService configService, IEventService eventService, INotificationService notificationService)
    {
        ...
    }
}

简单的关注点分离,一切都可以独立测试。在这种情况下,我的 IDataService 位于 BaseViewModel 中,因为根据我的应用程序是否具有 Internet 连接,我会在 sql server 和本地 json 文件之间切换实现以实现数据持久性。例如,以下是使用 Unity 连接依赖项的方法:

        var unityContainer = new UnityContainer();

        ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(unityContainer));

        unityContainer.RegisterType<IServiceLocator, UnityServiceLocator>(new ContainerControlledLifetimeManager());

        // automapper
        var config = new MapperConfiguration(cfg =>
           cfg.AddProfile(new AutoMapperBootstrap())
       );

        unityContainer.RegisterType<IMapper>(new InjectionFactory(_ => config.CreateMapper()));

        // factories
        unityContainer.RegisterType<IWelcomeGateViewFactory, WelcomeGateViewFactory>();
        unityContainer.RegisterType<ITrailerPictureViewFactory, TrailerPictureViewFactory>();

        // services
        unityContainer.RegisterType<IDataService, OfflineDataService>("OfflineDataService", new ContainerControlledLifetimeManager(), new InjectionConstructor(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), ServiceLocator.Current.GetInstance<IMapper>()));
        unityContainer.RegisterType<IDataService, DataService>(new ContainerControlledLifetimeManager());
        unityContainer.RegisterType<ITestDataService, TestDataService>(new ContainerControlledLifetimeManager()); 

        ...

【讨论】:

  • 是的,我一直让控制器构建视图模型,而不是视图模型自己构建它们。我认为让虚拟机了解数据层是一种不好的做法。它可能会在Single Responsibility Principle 周围产生各种令人头疼的问题
  • 这就是为什么您将业务服务传递给您的虚拟机以获取您的数据或做任何事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 2021-08-03
相关资源
最近更新 更多