我已经花了好几天的时间来思考这个问题了,我仍然无法决定哪一种方法是正确的。
这个问题专门针对WPF,因为与web应用程序不同,许多在线帖子和文章推荐的是按view-model计算的context方法,而不是每个request的context。
我有一个WPF MVVM应用程序,它使用Entity-Framework DB first模型。
下面是我的应用程序中使用的两个模型的示例(由EF设计器创建):
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)为每个entity创建一个repository。看起来与上面的选项相同(无论是否存在using困境),但是每个存储库只包含与其entity相关的方法。
Afaik在我的ViewModel中的使用与上面的相同。
4)创建一个按需传递适当Repository的Unit-Of-Work类:
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.中找到了以下段落:
使用Windows演示文稿基础(WPF)或Windows窗体时,请按每个窗体使用上下文实例。这允许您使用上下文提供的更改跟踪功能。
然而,它提出了一个问题:我是应该在我的DbContext中创建一个view-model对象,还是最好有一个实用工具类,比如我的DAL类并引用它。
发布于 2015-07-29 00:49:05
这就是设计依赖注入框架来解决的问题。是的,这是添加到您的项目中的另一种技术,但是一旦您开始使用DI,您将永远不会回头。
这里真正的问题是,当您真正应该使用控制反转并将决策提高到更高的位置时,您就会试图在您的视图模型中做出这个决定。WPF/MVVM应用程序将需要每个表单的上下文,以便只在用户完成编辑后提交更改,并给用户取消更改的机会。我知道你没有在web应用程序中使用这个,但是一个设计良好的体系结构意味着你应该能够,在这种情况下,你需要每个请求的上下文。您可能希望编写一个使用静态数据填充数据库的控制台应用程序实用程序,在这种情况下,您可能需要一个性能和易用性的全局/单例上下文。最后,单元测试也需要模拟上下文,可能是在每个测试的基础上。所有这些情况都应该建立在您的注入框架中,您的视图模型不应该知道或关心它们中的任何一个。
下面是一个例子。我个人使用尼尼微,它是专门为.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);这将设置ISession的作用域,这是与上下文类相同的NHibernate。我的存储库类管理内存中的数据库对象,包含对它们关联的会话的引用:
public class RepositoryManager : IRepositoryManager
{
[Inject]
public ISession Session { get; set; }
... etc...
{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:使用依赖注入和范围,您的上下文只需一种形式.
发布于 2022-05-10 01:24:35
在我早期在WPF中使用MVVM时,我使用的是每个VM的开放上下文,但是一旦应用程序进化到更好地使用异步,我很快就会遇到DBContexts的线程安全问题。
虽然开发开销更大,但我现在使用依赖项注入来提供DBContextFactory (而不是DBContext本身)。我在使用语句( witihn )中旋出上下文,通过EF向observableCollections填充plinq调用。此方法的另一个性能优势是使用AsNoTracking()运行查询。烦人的部分是管理新的或修改的对象重新附着到短暂的上下文中:
shortDBContext.Attach(myEntity).State = EntityState.Added; // or modified
await shortDBContext.SaveChangesAsync();https://stackoverflow.com/questions/26571274
复制相似问题