实体框架和WPF最佳实践

BBa*_*r42 13 wpf entity-framework

直接与上下文合作是不是一个好主意?例如,假设我有一个客户数据库,用户可以按名称搜索,显示列表,选择一个,然后编辑该客户的属性.

我似乎应该使用上下文来获取客户列表(映射到POCO或CustomerViewModels),然后立即关闭上下文.然后,当用户选择CustomerViewModels列表中的一个时,UI的客户属性部分就会填充.

接下来,他们可以更改名称,类型,网站地址,公司规模等.点击保存按钮后,我打开一个新的上下文,使用该ID CustomerViewModel来检索该客户记录,并更新其每个属性.最后,我调用SaveChanges()并关闭上下文.这是一项很多工作.

我的问题是为什么不直接与上下文一起工作呢?我读过使用相同的上下文,生命周期很长,非常糟糕,不可避免地会引发问题.我的假设是,如果应用程序仅由一个人使用,我可以保持上下文打开并执行所有操作.但是,如果有很多用户,我想维护一个简洁的工作单元,从而根据每个请求打开和关闭上下文.

有什么建议?谢谢.


@PGallagher - 感谢您的全面解答.
@Brice - 您的输入也很有帮助

然而,@ Manos D."冗余代码的缩影"评论让我有点担忧.让我举一个例子.假设我将客户存储在数据库中,而我的一个客户属性是CommunicationMethod.

[Flags]
public enum CommunicationMethod
{
    None = 0,
    Print = 1,
    Email = 2,
    Fax = 4
}
Run Code Online (Sandbox Code Playgroud)

WPF中我的管理客户页面的UI将包含客户通信方法(打印,电子邮件,传真)下的三个复选框.我无法将每个复选框绑定到该枚举,这没有意义.此外,如果用户点击该客户,起床并去吃午餐......上下文会在那里停留数小时,这很糟糕.相反,这是我的思考过程.

最终用户从列表中选择一个客户.我新建了一个上下文,找到该客户并返回一个CustomerViewModel,然后关闭了上下文(为了简单起见,我已经将存储库留在了这里).

using(MyContext ctx = new MyContext())
{
    CurrentCustomerVM = new CustomerViewModel(ctx.Customers.Find(customerId));
}
Run Code Online (Sandbox Code Playgroud)

现在,用户可以选中/取消选中打印,电子邮件,传真按钮,因为它们绑定到CustomerViewModel中的三个bool属性,该属性也有Save()方法.开始.

public class CustomerViewModel : ViewModelBase
{
    Customer _customer;

    public CustomerViewModel(Customer customer)
    {
        _customer = customer;
    }


    public bool CommunicateViaEmail
    {
        get { return _customer.CommunicationMethod.HasFlag(CommunicationMethod.Email); }
        set
        {
            if (value == _customer.CommunicationMethod.HasFlag(CommunicationMethod.Email)) return;

            if (value)
                _customer.CommunicationMethod |= CommunicationMethod.Email;
            else
                _customer.CommunicationMethod &= ~CommunicationMethod.Email;
        }
    }
    public bool CommunicateViaFax
    {
        get { return _customer.CommunicationMethod.HasFlag(CommunicationMethod.Fax); }
        set
        {
            if (value == _customer.CommunicationMethod.HasFlag(CommunicationMethod.Fax)) return;

            if (value)
                _customer.CommunicationMethod |= CommunicationMethod.Fax;
            else
                _customer.CommunicationMethod &= ~CommunicationMethod.Fax;
        }
    }
    public bool CommunicateViaPrint
    {
        get { return _customer.CommunicateViaPrint.HasFlag(CommunicationMethod.Print); }
        set
        {
            if (value == _customer.CommunicateViaPrint.HasFlag(CommunicationMethod.Print)) return;

            if (value)
                _customer.CommunicateViaPrint |= CommunicationMethod.Print;
            else
                _customer.CommunicateViaPrint &= ~CommunicationMethod.Print;
        }
    }

    public void Save()
    {
        using (MyContext ctx = new MyContext())
        {
            var toUpdate = ctx.Customers.Find(_customer.Id);
            toUpdate.CommunicateViaEmail = _customer.CommunicateViaEmail;
            toUpdate.CommunicateViaFax = _customer.CommunicateViaFax;
            toUpdate.CommunicateViaPrint = _customer.CommunicateViaPrint;

            ctx.SaveChanges();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你觉得这有什么问题吗?

bri*_*lam 17

可以使用长时间运行的上下文; 你只需要知道其含义.

上下文代表一个工作单元.无论何时调用SaveChanges,所跟踪实体的所有挂起更改都将保存到数据库中.因此,您需要将每个上下文的范围限定为有意义的内容.例如,如果您有一个用于管理客户的选项卡和另一个用于管理产品的选项卡,则可以为每个选项使用一个上下文,以便当用户单击客户选项卡上的保存时,他们对产品所做的所有更改也不会保存.

上下文跟踪了很多实体也可能会降低DetectChanges的速度.缓解此问题的一种方法是使用更改跟踪代理.

由于加载实体和保存该实体之间的时间可能相当长,因此点击乐观并发异常的可能性大于短期上下文.当在加载和保存实体之间进行外部更改时,会发生这些异常.处理这些异常非常简单,但它仍然需要注意.

使用WPF中的长期上下文可以做的一件很酷的事情是绑定到DbSet.Local属性(例如context.Customers.Local).这是一个ObservableCollection,其中包含未标记为删除的所有跟踪实体.

希望这能为您提供更多信息,帮助您确定哪种方法可以提供帮助.