C# 在WPF MVVM应用程序中管理DbContext
这几天来我一直在绞尽脑汁,仍然无法决定哪种方法是正确的。C# 在WPF MVVM应用程序中管理DbContext,c#,wpf,entity-framework,mvvm,architecture,C#,Wpf,Entity Framework,Mvvm,Architecture,这几天来我一直在绞尽脑汁,仍然无法决定哪种方法是正确的。 这个问题特别针对WPF,因为与web应用程序相反,许多在线帖子和文章建议使用contextper查看模型方法,而不是contextper请求 我有一个wpfmvvm应用程序,它首先使用实体框架DB模型。 以下是我的应用程序中使用的两个模型的示例(由EFDesigner创建): 我可以在我的视图模型中使用它,如下所示: public class UserManagerViewModel : ObservableObject { pr
这个问题特别针对
WPF
,因为与web应用程序相反,许多在线帖子和文章建议使用context
per查看模型方法,而不是context
per请求
我有一个wpfmvvm
应用程序,它首先使用实体框架DB
模型。
以下是我的应用程序中使用的两个模型的示例(由EF
Designer创建):
我可以在我的视图模型中使用它,如下所示:
public class UserManagerViewModel : ObservableObject
{
private readonly Dal dal = new Dal();
// models...
//commands...
}
public class UserManagerViewModel : ObservableObject
{
private readonly UnitOfWork unit = new UnitOfWork();
// models...
//commands...
}
2)类似于方法1,但没有使用语句的:
public class Dal : IDisposable
{
private readonly DbEntities db = new DbEntities();
public User GetUserById(object userId)
{
return db.User.Find(userId);
db.SaveChanges();
}
public void RemoveUser(User userToRemove)
{
db.User.Remove(userToRemove);
db.SaveChanges();
}
public void Dispose()
{
db.SaveChanges();
}
}
在ViewModel
3)为每个实体创建存储库。看起来与上述选项相同(也有带或不带使用
困境),但是每个存储库只包含与其实体相关的方法
Afaik在myViewModel中的用法与上面相同
4)创建一个工作单元
类,该类将根据需要通过相应的存储库
:
public class UnitOfWork : IDisposable
{
private DbEntities db = new DbEntities();
private IUserRepository userRepository;
public IUserRepository UserRepository
{
get
{
return userRepository ?? new UsersRepository(db);
}
}
public void Save()
{
db.SaveChanges();
}
public void Dispose()
{
db.Dispose();
}
}
并在我的ViewModel
中使用它,如下所示:
public class UserManagerViewModel : ObservableObject
{
private readonly Dal dal = new Dal();
// models...
//commands...
}
public class UserManagerViewModel : ObservableObject
{
private readonly UnitOfWork unit = new UnitOfWork();
// models...
//commands...
}
就数据并发性、更好的抽象和分层以及总体性能而言,上述哪种方法(如果有)是首选方法?
编辑-在中找到以下段落:
当使用Windows演示基础(WPF)或Windows窗体时,使用每个窗体的上下文实例。这允许您使用上下文提供的更改跟踪功能
但是,它提出了这样一个问题:我是应该在我的视图模型中创建DbContext
对象,还是最好有一个实用类,比如myDAL
类并引用它 依赖注入框架就是为了解决这个问题而设计的。是的,这是要添加到项目中的另一项技术,但一旦开始使用DI,您就再也不会回头了
这里真正的问题是,您正试图在视图模型中做出此决策,而实际上您应该采用控制反转,并在更高层做出决策。WPF/MVVM应用程序希望每个表单都有一个上下文,这样用户完成编辑后才能提交更改,并且还可以给用户取消更改的机会。我知道您没有在web应用程序中使用它,但设计良好的体系结构意味着您应该能够使用它,在这种情况下,您需要每个请求都有一个上下文。您可能需要编写一个控制台应用程序实用程序,用静态数据填充数据库,在这种情况下,您可能需要一个全局/单例上下文,以提高性能和易用性。最后,单元测试还需要模拟上下文,可能是基于每个测试。所有这四种情况都应该在您的注入框架中设置,并且您的视图模型不应该知道或关心其中任何一种情况
这里有一个例子。我个人使用Ninject,它是专门为.NET设计的。我也更喜欢NHibernate,尽管ORM的选择在这里并不重要。我的会话对象具有不同的作用域要求,这是在初始化我的ORM类的Ninject模块中设置的:
var sessionBinding = Bind<ISession>().ToMethod(ctx =>
{
var session = ctx.Kernel.Get<INHibernateSessionFactoryBuilder>()
.GetSessionFactory()
.OpenSession();
return session;
});
if (this.SingleSession)
sessionBinding.InSingletonScope();
else if (this.WebSession)
sessionBinding.InRequestScope();
else
sessionBinding.InScope(ScreenScope);
[Inject]属性告诉Ninject使用我设置的范围规则自动填充此字段。到目前为止,这一切都发生在我的域类中,但它也扩展到了我的视图模型层。在我的范围规则中,我传入了一个名为“ScreenScope”的对象,虽然我在这里不再赘述,但这基本上意味着,每当我在ScreenViewModel中请求会话对象,或者它作为成员(包括其自己的孩子)的任何视图模型时,都会自动创建相同的ISession对象,并将其传递给所有人。通过使用DI作用域,我甚至不必考虑它,我只需使用[Inject]属性声明成员,它就会发生:
public class ScreenViewModel
{
[Inject] public CustomerService CustomerService { get; set; }
[Inject] public SalesService SalesService { get; set; }
[Inject] public BillService BillService { get; set; }
...etc...
}
这些服务类都包含一个已注入的RepositoryManager,由于它们都在ScreenViewModel中,ISession对象将是相同的,至少在我的WPF构建中是如此。如果我切换到MVC构建,它们对于为给定请求创建的所有视图模型都是相同的,如果我切换到控制台构建,它对整个程序中的所有内容都使用相同的ISession
TL;DR:使用依赖注入,并将上下文的作用域设置为每个表单一个。EF有一个优秀的UoW(上下文)+存储库(DbSet)。为什么要创建自己的?这些额外的层几乎没有任何帮助。它们往往导致以数据为中心的应用程序将业务逻辑拉入客户机,而以任务为中心的应用程序将用例封装在接近EF模型的服务中。这是对数据的抽象,而不是对任务的抽象(抽象意味着隐藏持久性实现)。由于我特别询问的是wpfmvvm
,我想知道是否应该使用长寿命DbContext
(每个视图模型使用context
),还是应该像选项1(使用使用:)那样限制context
的使用寿命?我们已经尝试了这两种方法,两者都有优点和缺点。没有一刀切的解决方案,你只需要找出哪一个最适合你。也就是说,我们决定采用第二种方法,尽可能缩短上下文的生命周期,因为这样可以避免在有多个视图(想想选项卡或窗口)时出现问题具有多个视图模型,因此同时打开多个上下文。通常在这两者之间有某种业务逻辑层,看起来像您的Dal,由视图模型调用。每个业务逻辑操作都会创建一个新的上下文,并使用该上下文完成所有必须做的事情
public class ScreenViewModel
{
[Inject] public CustomerService CustomerService { get; set; }
[Inject] public SalesService SalesService { get; set; }
[Inject] public BillService BillService { get; set; }
...etc...
}