Yoa*_*oav 9 c# architecture wpf entity-framework mvvm
这几天我一直在用脑子敲打我的脑袋,仍然无法确定哪种方法正确.
这个问题是针对WPF因为而不是具体到一个web应用,许多文章和文章在网上推荐了一个context每view-model方法,而不是context每request.
我有一个WPF MVVM使用Entity-Framework DB first模型的应用程序.
这是我的应用程序中使用的两个模型的示例(由EFDesigner 创建):
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; }
}
Run Code Online (Sandbox Code Playgroud)
我已经缩小了如何处理以下内容的选项:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以使用ViewModel以下内容:
public class UserManagerViewModel : ObservableObject
{
private readonly Dal dal = new Dal();
// models...
//commands...
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
里面的用法是一样的 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();
}
}
Run Code Online (Sandbox Code Playgroud)
并在我的内部使用ViewModel如下:
public class UserManagerViewModel : ObservableObject
{
private readonly UnitOfWork unit = new UnitOfWork();
// models...
//commands...
}
Run Code Online (Sandbox Code Playgroud)
就数据并发性,更好的抽象和分层以及整体性能而言,上述哪种方法(如果有的话)是首选的?
编辑 -在本文中找到以下段落.:
使用Windows Presentation Foundation(WPF)或Windows窗体时,请为每个窗体使用上下文实例.这使您可以使用上下文提供的更改跟踪功能.
但是,它提出了一个问题,即我是否应该DbContext在我的对象中创建一个对象,view-model或者更好地拥有一个像我的DAL类这样的实用程序类并引用它.
这就是依赖注入框架旨在解决的问题。是的,这是添加到您的项目中的另一种技术,但是一旦您开始使用 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);
Run Code Online (Sandbox Code Playgroud)
这设置了 ISession 的范围,它相当于 NHibernate 的上下文类。我的存储库类管理内存中的数据库对象,包含对它们关联的会话的引用:
public class RepositoryManager : IRepositoryManager
{
[Inject]
public ISession Session { get; set; }
... etc...
{
Run Code Online (Sandbox Code Playgroud)
[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...
}
Run Code Online (Sandbox Code Playgroud)
这些服务类都包含已注入的 RepositoryManager,并且由于它们都在 ScreenViewModel 中,因此 ISession 对象将是相同的,至少在我的 WPF 构建中是这样。如果我切换到 MVC 构建,它们对于为给定请求创建的所有视图模型都是相同的,如果我切换到控制台构建,它会对整个程序中的所有内容使用相同的 ISession。
TL;DR:使用依赖注入并将上下文范围限定为每个表单一个。
| 归档时间: |
|
| 查看次数: |
6506 次 |
| 最近记录: |