几天来我一直在思考这个问题,但仍然无法决定哪种方法是正确的。
这个问题针对 WPF
特别是因为与网络应用程序相反,许多在线帖子和文章推荐 context
每view-model
方法而不是 context
每request
.
我有一个 WPF MVVM
使用 Entity-Framework DB first
的应用程序模型。
这是我的应用程序中使用的两个模型的示例(由 EF
Designer 创建):
public partial class User
{
public User()
{
this.Role = new HashSet<Role>();
}
public string ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Role> Role { get; set; }
}
public class Role
{
public Role()
{
this.User = new HashSet<User>();
}
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> User { get; set; }
}
我已将处理此问题的选项缩小到以下范围:
1) 创建 DataAccess
创建和处理 DbContext
的类在每个方法调用上:
public class Dal
{
public User GetUserById(object userId)
{
using (var db = new DbEntities())
{
return db.User.Find(userId);
db.SaveChanges();
}
}
public void RemoveUser(User userToRemove)
{
using (var db = new DbEntities())
{
db.User.Remove(userToRemove);
db.SaveChanges();
}
}
}
我可以在我的 ViewModel
中使用它如下:
public class UserManagerViewModel : ObservableObject
{
private readonly Dal dal = new Dal();
// models...
//commands...
}
2) 与方法 1 类似,但没有 Using
声明:
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) 创建一个 repository
对于每个 entity
.看起来与上面的选项相同(也有有或没有 using
的困境),但是每个存储库只包含与其 entity
相关的方法.
Afaik 在我的 ViewModel
中使用与上面相同.
4) 创建 Unit-Of-Work
将通过适当的类 Repository
按需:
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 UnitOfWork unit = new UnitOfWork();
// models...
//commands...
}
就数据并发性、更好的抽象和分层以及整体性能而言,以上哪种方法(如果有)是首选?
编辑 - 在 this article. 中找到以下段落:
When working with Windows Presentation Foundation (WPF) or Windows Forms, use a context instance per form. This lets you use change-tracking functionality that context provides.
但是,它提出了我是否应该创建一个 DbContext
的问题我的对象 view-model
还是有一个实用类更好,比如我的 DAL
类并引用它。
最佳答案
这就是依赖注入(inject)框架旨在解决的问题。是的,这是添加到您的项目中的另一种技术,但是一旦您开始使用 DI,您就再也不会回头了。
这里真正的问题是,当您真的应该使用控制反转并在更高的位置做出决定时,您却试图在 View 模型中做出这个决定。 WPF/MVVM 应用程序将需要每个表单的上下文,以便仅在用户完成编辑后才提交更改,并为用户提供取消更改的机会。我知道您不会在 Web 应用程序中使用它,但设计良好的架构意味着您应该能够这样做,在这种情况下,您需要每个请求都有一个上下文。您可能想要编写一个控制台应用程序实用程序,用静态数据填充数据库,在这种情况下,您可能需要一个全局/单例上下文以提高性能和易用性。最后,您的单元测试还需要模拟上下文,可能是在每次测试的基础上。所有这四种情况都应该在你的注入(inject)框架中设置,你的 View 模型不应该知道或关心它们中的任何一个。
这是一个例子。我个人使用专为 .NET 设计的 Ninject。我也更喜欢 NHibernate,尽管 ORM 的选择在这里无关紧要。我有具有不同范围要求的 session 对象,这在初始化我的 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);
这为 ISession 设置了作用域,ISession 是上下文类的 NHibernate 等价物。我的存储库类管理内存中的数据库对象,包含对它们关联的 session 的引用:
public class RepositoryManager : IRepositoryManager
{
[Inject]
public ISession Session { get; set; }
... etc...
{
[Inject] 属性告诉 Ninject 使用我设置的范围规则自动填充此字段。到目前为止,这一切都发生在我的域类中,但它也扩展到我的 View 模型层。在我的范围规则中,我传入了一个名为“ScreenScope”的对象,虽然我不会在这里深入探讨,但它基本上意味着任何时候我在我的 ScreenViewModel 或它作为成员的任何 View 模型中请求 session 对象(包括他们自己的 child )相同的 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...
}
这些服务类都包含一个已注入(inject)的 RepositoryManager,并且由于它们都在 ScreenViewModel 中,所以 ISession 对象将是相同的,至少在我的 WPF 构建中是这样。如果我切换到我的 MVC 构建,它们对于为给定请求创建的所有 View 模型都是相同的,如果我切换到控制台构建,它对整个程序中的所有内容都使用相同的 ISession。
TL;DR:使用依赖注入(inject)并将上下文范围限定为一个表单。
关于c# - 在 WPF MVVM 应用程序中管理 DbContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26571274/