Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/asp.net-mvc-3/4.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
Asp.net mvc 3 EF和存储库模式-最终在一个控制器中有多个DBContext-有任何问题(性能、数据完整性)?_Asp.net Mvc 3_Entity Framework 4_Repository Pattern_Dbcontext - Fatal编程技术网

Asp.net mvc 3 EF和存储库模式-最终在一个控制器中有多个DBContext-有任何问题(性能、数据完整性)?

Asp.net mvc 3 EF和存储库模式-最终在一个控制器中有多个DBContext-有任何问题(性能、数据完整性)?,asp.net-mvc-3,entity-framework-4,repository-pattern,dbcontext,Asp.net Mvc 3,Entity Framework 4,Repository Pattern,Dbcontext,我对ASP.NET MVC 3的大部分知识来自于阅读亚当·弗里曼和史蒂文·森德森的《Pro ASP.NET MVC 3框架》一书。在我的测试应用程序中,我试图非常严格地遵循他们的示例。我使用的是repository模式加上Ninject和Moq,这意味着单元测试工作得相当好(即不需要从数据库中提取数据) 在书籍中,存储库的使用方式如下: public class EFDbTestChildRepository { private EFDbContext context = new EFD

我对ASP.NET MVC 3的大部分知识来自于阅读亚当·弗里曼和史蒂文·森德森的《Pro ASP.NET MVC 3框架》一书。在我的测试应用程序中,我试图非常严格地遵循他们的示例。我使用的是repository模式加上Ninject和Moq,这意味着单元测试工作得相当好(即不需要从数据库中提取数据)

在书籍中,存储库的使用方式如下:

public class EFDbTestChildRepository
{
    private EFDbContext context = new EFDbContext();

    public IQueryable<TestChild> TestChildren
    {
        get { return context.TestChildren; }
    }

    public void SaveTestChild(TestChild testChild)
    {
        if (testChild.TestChildID == 0)
        {
            context.TestChildren.Add(testChild);
        }
        else
        {
            context.Entry(testChild).State = EntityState.Modified;
        }
        context.SaveChanges();
    }
}
web应用程序包含一个允许用户创建新TestChild的页面。上面有一个选择框,其中包含可供选择的可用测试父级列表。这就是我的控制器的外观:

public class ChildController : Controller
{
    private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
    private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();

    public ActionResult List()
    {
        return View(testChildRepository.TestChildren);
    }

    public ViewResult Edit(int testChildID)
    {
        ChildViewModel cvm = new ChildViewModel();
        cvm.TestChild = testChildRepository.TestChildren.First(tc => tc.TestChildID == testChildID);
        cvm.TestParents = testParentRepository.TestParents;
        return View(cvm);
    }

    public ViewResult Create()
    {
        ChildViewModel cvm = new ChildViewModel();
        cvm.TestChild = new TestChild();
        cvm.TestParents = testParentRepository.TestParents;
        return View("Edit", cvm);
    }

    [HttpPost]
    public ActionResult Edit(TestChild testChild)
    {
        try
        {
            if (ModelState.IsValid)
            {
                testChildRepository.SaveTestChild(testChild);
                TempData["message"] = string.Format("Changes to test child have been saved: {0} (ID = {1})",
                                                        testChild.Name,
                                                        testChild.TestChildID);
                return RedirectToAction("List");
            }
        }
        catch (DataException)
        {
            //Log the error (add a variable name after DataException)
            ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
        }

        // something wrong with the data values
        return View(testChild);
    }
}
仅有一个可用的EFDbTestChildRepository是不够的,但我还需要一个EFDbTestParentRepository。它们都被分配给控制器的私有变量——瞧,在我看来,已经创建了两个DBContext。或者这是不对的

为了避免这个问题,我尝试使用EFDbTestChildRepository访问TestParents。但这显然只会带来那些已经连接到至少一个TestChild的测试,所以这不是我想要的

以下是视图模型的代码:

public class ChildViewModel
{
    public TestChild TestChild { get; set; }
    public IQueryable<TestParent> TestParents { get; set; }
}
公共类ChildViewModel
{
公共TestChild TestChild{get;set;}
公共IQueryable TestParents{get;set;}
}
如果我忘记包含一些相关代码,请告诉我。非常感谢你的建议

我是否有可能在一个请求中使用多个DBContext

对。存储库的每个实例都将实例化自己的DbContexts实例。根据应用程序的大小和使用情况,这可能不是问题,尽管它不是一种非常可扩展的方法。不过,有几种处理方法。在我的web项目中,我将DbContext添加到请求的Context.Item集合中,这样所有需要它的类都可以使用它。我使用Autofac(类似于Ninject)来控制在特定场景中创建的DBContext以及它们的存储方式,例如,我有一个不同于Http上下文的WCF上下文“会话管理器”

这会导致显著的性能开销吗

是的,但如果应用程序相对较小,则也不会太大。但随着时间的增长,您可能会注意到开销

上下文和任何上下文之间的潜在冲突如何 对数据完整性的影响


使用这样的ORM的原因之一是,可以在DbContext中维护更改。如果您正在为每个请求实例化多个上下文实例,那么您将失去这一优势。除非以异步方式处理大量更新,否则您不会注意到冲突或完整性本身的任何影响。

不会出现性能问题(除非我们讨论的是纳秒,实例化上下文非常便宜),也不会损坏数据完整性(在此之前,您会遇到异常)

但这种方法非常有限,只能在非常简单的情况下使用。在许多场景中,多个上下文会导致问题。例如:假设您要为现有父级创建一个新的子级,并使用以下代码进行尝试:

var parent = parentRepo.TestParents.Single(p => p.Id == 1);
var child = new Child { TestParent = parent };
childrenRepo.SaveTestChild(child);
此简单代码不起作用,因为
parent
已附加到
parentRepo
中的上下文,但
childrenRepo。SaveTestChild
将尝试将其附加到
childrenRepo
中的上下文,这将导致异常,因为实体不能附加到其他上下文。(这里实际上是一个解决方法,因为您可以设置FK属性,而不是加载
父级
child.TestParentID=1
。但是如果没有FK属性,这将是一个问题。)

如何解决这样的问题

一种方法是通过新属性扩展
EFDbTestChildRepository

public IQueryable<TestParent> TestParents
{
    get { return context.TestParents; }
}
这为每个控制器提供了一个上下文,可以在多个存储库中使用

下一步可能是通过依赖项注入为每个请求创建一个上下文,如

private readonly MyContext _context;
public MyController(MyContext context)
{
    _context = context;
}

…然后配置IOC容器以创建一个上下文实例,该实例可能会被注入多个控制器。

正如我在发布解决方案时承诺的那样

我遇到了您的问题,因为我在IIS应用程序池内存增长超出限制方面遇到了问题,而我怀疑有多个DBContext。回想起来,公平地说,我的麻烦还有其他原因。然而,它挑战我为我的存储库找到更好的基于层的设计

我发现了这个很棒的博客:引领我走向正确的方向。重新设计基于UnitOfWork模式。它使我能够为所有控制器只使用一个构造函数参数,而不是“永不结束的构造函数参数”。在那之后,我也能够引入主动缓存,这解决了前面提到的许多问题

现在我只有这些课程:

  • 工夫
  • 工作单位
  • IGenericRepository
  • 通用存储
有关这些类的完整信息和实现,请参阅参考博客。举个例子,IUnitOfWork包含我需要的所有实体的存储库定义,如:

namespace MyWebApp.Domain.Abstract
{
  public interface IUnitOfWork : IDisposable
  {
    IGenericRepository<AAAAA> AAAAARepository { get; }
    IGenericRepository<BBBBB> BBBBBRepository { get; }
    IGenericRepository<CCCCC> CCCCCRepository { get; }
    IGenericRepository<DDDDD> DDDDDRepository { get; } 
    // etc.

    string Commit();
  }
}
在控制器中,如果需要,我可以使用_上下文访问所有存储库。它的优点在于,只需一个Commit()-调用即可为所有存储库保存更改的数据:

_context.Commit();

非常非常感谢你在回答中提供了这么多细节。从概念上讲,我更喜欢第二种方法,即不将dbContext存储为存储库的私有属性。我几周前就试过了,但最后还是卡住了。如果我没记错的话,我无法在DbContext上进行依赖项注入。埋葬
private readonly MyContext _context;
public MyController(MyContext context)
{
    _context = context;
}
namespace MyWebApp.Domain.Abstract
{
  public interface IUnitOfWork : IDisposable
  {
    IGenericRepository<AAAAA> AAAAARepository { get; }
    IGenericRepository<BBBBB> BBBBBRepository { get; }
    IGenericRepository<CCCCC> CCCCCRepository { get; }
    IGenericRepository<DDDDD> DDDDDRepository { get; } 
    // etc.

    string Commit();
  }
}
ninjectKernel.Bind<IUnitOfWork>().To<EFUnitOfWork>();
public class MyController : BaseController
{
  private MyModel mdl = new MyModel();

  private IUnitOfWork _context; 

  public MyController(IUnitOfWork unitOfWork)
  {
    _context = unitOfWork;

    // intialize whatever needs to be exposed to the View:
    mdl.whatever = unitOfWork.SomeRepository.AsQueryable(); 
  }

 // etc.
_context.Commit();