【问题标题】:Persistence with EntityFramework in ASP.NET MVC application在 ASP.NET MVC 应用程序中使用 EntityFramework 的持久性
【发布时间】:2011-03-22 18:52:39
【问题描述】:

在我的 ASP.NET MVC 应用程序中,我需要实现数据的持久性。我选择 Entity Framework 是因为它能够从实体模型创建类、数据库表和查询​​,这样我就不必手动编写 SQL 表创建或 Linq to SQL 查询。所以简单是我的目标

我的方法是创建模型,而不是创建一个自定义 HttpModule,它在每个请求的 and 处被调用,并且在上下文中只调用 SaveChanges()。这让我的生活变得非常艰难——实体框架不断抛出非常奇怪的异常。有时它起作用了——也不例外,但有时它没有。一开始我试图一个一个地解决问题,但当我遇到另一个问题时,我意识到我的一般方法可能是错误的。

那么这是在 ASP.NET MVC 应用程序中实现持久性的一般做法吗?每次更改后我只打电话给saveChanges 吗?是不是有点低效?而且我不知道如何使用服务模式来做到这一点(服务与实体一起工作,所以我必须将上下文实例传递给它们,以便它们在进行某些更改时可以保存更改)。

我们也感谢您提供一些指向学习材料或教程的链接。


注意:这个问题要求编程练习。我请那些认为它含糊不清的人记住,它仍在解决我非常特殊的问题,正确的技术将在投票结束之前为我节省很多技术问题。

【问题讨论】:

  • 通过阅读您的描述,我感觉您正在使用上下文的单个共享实例,并且在 http 模块的最终请求期间对该实例调用 SaveChanges
  • @Ladislav Mrnka 你的坏感觉是对的,但现在我正在重建它以使用存储库模式。只需要弄清楚如何正确使用该模式。
  • 这里描述了为什么您当前的解决方案不起作用:stackoverflow.com/questions/3653009/…
  • @Ladislav Mrnka 我认为这不是原因。我很清楚“EF 在每个上下文中只加载单个实体一次”。但就我而言,只有我的应用程序连接到数据库。因此,没有数据会在外部发生变化的风险,因此当我为整个应用程序使用单个静态上下文时,我将使用非实际实体。并不是说这是一个好的模式。
  • 该描述有两个部分。您描述了第一个 - 身份映射。更大的问题可以是共享工作单元,其中保存更改可以保存来自并发请求处理的不完整更新。

标签: c# .net asp.net-mvc entity-framework


【解决方案1】:

您只需要确保在您的请求完成之前调用SaveChanges。控制器动作的底部是一个理想的位置。我的控制器操作通常如下所示:

public ActionResult SomeAction(...) 
{
    _repository.DoSomething();
    ...
    _repository.DoSomethingElse();
    ...
    _repository.SaveChanges();
    return View(...);
}

这还有一个额外的好处,即如果抛出异常,则不会调用 SaveChanges。您可以在操作中或在Controller.OnException 中处理异常。

【讨论】:

  • 如何将该存储库保存更改放入全局操作过滤器中,以便它被称为automatically,而不必在每个操作中手动编写它?那会有什么缺点吗?
  • 问题是如果(在我的示例中)DoSomething() 完成并向存储库添加有效更改。但是随后DoSomethingElse() 失败了,所以此时我不再想保存任何更改?如果无论如何在所有请求之后自动调用 SaveChanges,则可能会导致数据损坏。基本上上面的模式是穷人的交易。在真正的事务中真正运行所有这些事情会更好。
  • @Matt Greer 是否有某种方法可以检查在动作过滤器执行动作期间是否有异常?这将解决您正在解决的问题,它仍然是“自动的”......
  • @drasto - 我确定有。如果您真的想自动调用 SaveChanges,您可能会设计一些可行的方法。像在Controller.OnException 中设置一个标志,如果设置了该标志,请不要调用 SaveChanges。 IMO 这将比它的价值更多的工作。但话又说回来,我一般也不喜欢发生这种情况的“魔法”,我喜欢在大多数情况下保持明确的控制。
  • 对我来说,我返回IEnumerable,因为我认为控制器不应该按摩数据。它应该向 repo 提出问题,得到答案,然后将答案传递给正确的视图。控制器不应该知道或关心 repo 刚刚提供的数据是在内存中还是仍在数据库中。但这只是我的看法,其他人喜欢返回 IQueryable 并让控制器在实际的数据库查询发生之前过滤/更改/任何数据。
【解决方案2】:

它不会比多次调用存储过程效率更高或更低(相对于需要建立的连接数)。

名义上,您将对对象集进行所有更改,然后 SaveChanges 提交所有这些更改。

所以不要这样做:

mySet.Objects.Add(someObject);
mySet.SaveChanges();
mySet.OtherObjects.Add(someOtherObject);
mySet.SaveChanges();

你只需要这样做:

mySet.Objects.Add(someObject);
mySet.OtherObjects.Add(someOtherObject);
mySet.SaveChanges();
// Commits Both Changes

【讨论】:

    【解决方案3】:

    通常,您的数据访问是由一个实现 repsitory 模式的对象包装的。然后在存储库上调用 Save() 方法。

    类似

    var customer = customerRepository.Get(id);
    customer.FirstName = firstName;
    customer.LastName = lastName;
    customerRepository.SaveChanges();
    

    然后可以通过服务层包装存储库以提供视图模型对象或 DTO

    是不是有点低效?

    不要过早地进行优化。当您遇到性能问题时,请分析性能,找出原因,然后进行优化。重复。

    更新

    存储库包装数据访问,通常是单个实体。服务层包装业务逻辑,可以通过多个存储库访问多个实体。它通常处理“超薄”模型或 DTO。

    例如获取客户的发票列表

    public Customer GetCustomerWithInvoices(int id) {
    
      var customer = customerRepository.Get(id);
      var invoiceList = invoiceRepository.GetAllInvoicesFor(id);
    
      return new Customer {
        Customer = customer,
        Invoices = invoiceList
      };
    
    }
    

    【讨论】:

    • 你能举个例子说明服务层如何包装存储库层吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-02
    • 2014-02-05
    • 2012-08-17
    • 2014-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多