Linq to Sql - ASP.NET MVC 应用程序中的 DataContext 设计问题和注意事项
我在 ASP.NET MVC 应用程序中使用 Linq to Sql 时使用“每个原子操作单个数据上下文”方法。
到目前为止,我一直在使用单例数据上下文,我了解到它有很多问题,因此我重构了代码以在每个原子操作中使用单个数据上下文。
控制器操作的示例如下(重构并未改变这一点):
public ActionResult List()
{
List<Request> requests = this.repository.AllRequests();
return View(requests);
}
存储库的类型为 IRepository。我希望保留这个抽象,以便能够切换到不同的数据层(根据我最近使用 Linq to Sql 的经验,这可能很快就会发生:))
LinqRepository 实现 AllRequests() 方法如下:(
public List<Request> AllRequests()
{
using (DataModelDataContext connection = GetContext())
{
return connection.Requests.ToList();
}
}
仅供参考,以前 DataContext 实例是 LinqRepository 的一个字段,并且 LinqRepository 作为单个静态实例保存)
DataContext 在方法返回之前被释放。
现在,视图代码在访问延迟属性时会抛出 ObjectDispose 异常:
<%= Html.Encode(request.Branch.Name) %> //throws
我了解到可能不需要处置 DataContext(此处 我什么时候应该处置数据上下文)
当我不处置DataContext(删除using)时,就没有ObjectDisposeException。
即:我将方法更改如下:
public List<Request> AllRequests()
{
DataModelDataContext connection = GetContext();
return connection.Requests.ToList();
}
但我想知道,在这种情况下不处置 DataContext 实例会产生什么影响?
我知道我应该在 DataContext 被释放之前从实体实例中读取所有数据(包括延迟属性),但我不想引入另一个抽象(另一个 Request 类,将所有属性复制到它)。
我的问题:
实体对象是否拥有对其父 DataContext 的强引用,以防止其被 GC 处理? (我想是的,只是想 100% 确定)
当使用 Linq to Sql 同时需要保留数据层抽象时,您能否提供有关推荐方法的建议? (包括部分属性更新)
是否有一个开源项目使用存储库抽象在 ASP.NET MVC 中实现 Linq to Sql?
I am using "Single data context per atomic operation" approach while using Linq to Sql in an ASP.NET MVC application.
So far, I have been using a singleton datacontext, which I learnt has many issues, so I refactored the code to use single datacontext per atomic operation.
An example of a controller action is now as follows (refactoring has not changed this):
public ActionResult List()
{
List<Request> requests = this.repository.AllRequests();
return View(requests);
}
repository is of type IRepository. I want to keep this abstraction to be able to switch to a different data layer (based on my recent experience with Linq to Sql, this may happen very soon :))
The LinqRepository implements the AllRequests() method as follows:
public List<Request> AllRequests()
{
using (DataModelDataContext connection = GetContext())
{
return connection.Requests.ToList();
}
}
(just for reference, previously DataContext instance was a field of the LinqRepository and LinqRepository was held as a single static instance)
The DataContext is disposed before the method returns.
The view code now throws ObjectDisposed exceptions when accessing deferred properties:
<%= Html.Encode(request.Branch.Name) %> //throws
I learnt disposing of the DataContext may not be needed (here When should I dispose of a data context)
When I do not dispose the DataContext (remove the using), there is no ObjectDisposedException.
i.e.: I altered the method as follows:
public List<Request> AllRequests()
{
DataModelDataContext connection = GetContext();
return connection.Requests.ToList();
}
But I am wondering, what are the implications of not disposing DataContext instance in this scenario?
I know I am supposed to read all the data from the entity instance (including deferred properties) before the DataContext is disposed, but I would not like to introduce another abstraction (another Request class, copying all the properties over to it).
My questions:
Does the entity object hold a strong reference to its parent DataContext, preventing it from being GC-ed? (I guess it does, juts want to be 100% sure)
Can you provide advice on recommended approaches when using Linq to Sql while data layer abstraction needs to be retained? (including partial property updates)
Is there an open source project using a repository abstraction implementing Linq to Sql in ASP.NET MVC?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,让我们考虑一下您身上发生了什么。
您正在:
该方案旨在发挥作用。然而,为了使其始终有效,所有数据都需要立即加载。
默认情况下,LinqToSql 将延迟(也称为延迟)加载它认为合适的内容。在这种情况下,Request.Branch 或至少 Request.Branch.Name 似乎正在被延迟加载。对于正确延迟加载的内容,数据上下文必须仍然存在,但在您的场景中,它不再存在。
以下是一些让您的方案发挥作用的选项:
选项 1:
以某种方式“强制”在处置数据上下文之前加载 Request.Branch.Name。使用 LinqToSql 可以实现这一点,尽管不一定像您希望的那样简单或方便。这应该允许您继续使用“每个原子操作单个数据上下文”模式,尽管该方法还有其他缺点。数据上下文支持围绕更改跟踪、缓存和其他场景的重要功能,虽然您希望尽快处理数据上下文,但保持其开放有显着的优势,因此您应该在保留数据方面保持开放的态度上下文打开的时间比您当前保持打开的时间长。实现选项 1 的一种方法是使用 DTO,正如您在说“另一个请求”时提到的那样class”,但在这种情况下这对你来说可能有点过分了。
选项 2:
当用户开始操作时打开数据上下文,并在用户结束操作时将其释放。这称为 UnitOfWork 模式。这使得延迟加载能够正常工作,并为您提供更改跟踪和其他不错的 ORM 功能。实施起来可能有点困难,但它最有可能在大多数情况下发挥作用。
Tvanfosson 的答案介于选项 1 和选项 2 之间。 2 并且有很多中间立场的机会来解决您的问题。所有 ORM 都有一个学习曲线,LinqToSql 也不例外。大多数 ORM 都具有与 LinqToSql 的数据上下文类似的内容,并且从 LinqToSql 切换出去并不能让您免于了解数据上下文如何以及为何如此工作。
您永远不想在不释放数据上下文的情况下打开它。这是资源泄漏,非常糟糕。
我很确定 NerdDinner MVC 示例应用程序同时使用 LinqToSql 和存储库模式,但我不记得它是否实现了 UnitOfWork 来进行数据上下文管理。
First, let's consider what is happening to you.
You are:
That scenario is designed to work. However, for that to always work, all data would have needed to be eager loaded.
By default, LinqToSql will lazy (aka defer) load things as it sees fit. In this case, it appears that Request.Branch or at least Request.Branch.Name is being lazy loaded. For something to lazy load properly, the data context must still be around, which in your scenario, it no longer is.
Here are some options to get your scenario working:
Option 1:
Somehow "force" Request.Branch.Name to be loaded before disposing your data context. That is possible with LinqToSql, although not necessarily as easy or convenient as you'd want it to be. This should allow you to continue using your "single data context per atomic operation" pattern, although there are other downsides with that approach. The data context enables important features around change tracking, caching, and other scenarios and while you want to dispose of the data context as soon as possible, there are significant advantages to keeping it open so you should be open minded in regards to keeping your data context open longer than you currently are keeping it open. One way to implement option 1 is to use DTOs as you allude to when you say "another Request class", but that may be overkill for you here in this scenario.
Option 2:
Open the data context when your user starts their action and dispose it when the user ends their action. This is called the UnitOfWork pattern. This allows lazy loading to work properly as well as giving you change tracking and other nice ORM features. It can be a little more challenging to implement, but it's the most likely to work in the most scenarios.
Tvanfosson's answer lies somewhere between Options 1 & 2 and there are plenty of middle ground opportunities to solve your problem. All ORMs have a learning curve and LinqToSql is no different. Most ORMs have something similar to LinqToSql's data context and switching away from LinqToSql won't absolve you from having to understand how and why the data context works as it does.
You never want to open a data context without disposing it. That is a resource leak and it's very bad.
I'm pretty sure the NerdDinner MVC sample application uses both LinqToSql and the repository pattern, but I can't remember if it implements UnitOfWork for data context management.
我的解决方案是让存储库实现 IDisposable。然后,我将在 OnActionExecuting 的构造函数中将存储库实例化为控制器中的实例变量。然后我会在 OnResultExecuted 中处理存储库。当您到达 OnResultExecuted 时,结果已经呈现并且所需的任何数据都已被消耗。存储库将类似地在其构造函数中实例化 DataContext,并在释放时释放它。
My solution would be to have the Repository implement IDisposable. Then I would instantiate the Repository as an instance variable in the controller in the constructor in OnActionExecuting. Then I would dispose of the repository in OnResultExecuted. By the time you get to OnResultExecuted the result has already been rendered and any data needed has been consumed. The repository would similarly instantiate the DataContext in it's constructor and dispose of it when it is disposed.