Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/263.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在WPF MVVM应用程序中管理DbContext_C#_Wpf_Entity Framework_Mvvm_Architecture - Fatal编程技术网

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在my
ViewModel中的用法与上面相同

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
对象,还是最好有一个实用类,比如my
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);
[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...
}