在Linq to SQL中覆盖实体缓存/更改跟踪行为

时间:2010-02-02 18:42:43

标签: .net linq-to-sql caching change-tracking

我认为也许最初的问题太过冗长,有太多不必要的细节,所以这是我试图简化。

我正在寻找一种方法来执行以下任何的操作。我只需要做一个,而不是全部。如果有人知道其中一个的答案,请回复。那么,是否可以在Linq to SQL中执行以下任何操作:

  • 通过DataContextExecuteQueryExecuteMethodCall中提取实体而不跟踪这些实体?

  • 调用ExecuteQueryExecuteMethodCall并保证我始终收到从数据库中检索到的结果的新副本,即使这些实体已被检索并且已经在身份缓存中?

  • 指示Linq to SQL不对特定实体类型执行任何更改跟踪 - 但仍允许对其他类型进行更改跟踪?

限制:

  • Refresh方法是不可能的;实体数量非常大,这将成为一场性能灾难。

  • 我不能简单地将ObjectTrackingEnabled设置为false,因为DataContext在执行查询后不允许将其设置回true,而我确实需要一些要跟踪的实体。

  • 我也不能丢弃原来的DataContext并使用新的DataContext;我需要能够在交易过程中做到这一点。


这开始成为一个严重的问题,我真的认为默认行为是错误的。如果我执行临时查询或存储过程,我希望我收到的结果是所述查询返回的确切结果。它才有意义;如果我想要旧的陈旧实体,为什么我会回到数据库来获取它们?

目前,我的解决方法是(a)专门为查询创建一个新的[Table]并覆盖事务隔离级别,或者(b)使返回类型成为与“DTO”相同的“DTO”实体,但没有{{1}}属性,并使用AutoMapper将其映射到原始实体。这两件事看起来都像是可怕的黑客。

非常感谢任何人对这个难题的任何建议。

2 个答案:

答案 0 :(得分:3)

我已经设法为这个问题提出了一个可行的长期解决方法。它并不是非常理想,但到目前为止它的应用相对来说并不那么容易,而且远不如替代品那么可怕。

由于这些查询无论如何都是纯SQL - 它们对于内联SQL都是ExecuteQuery或对于存储过程都是ExecuteMethodCall - 我决定只使用“原始”ADO.NET实例当我不希望DataContext知道某些实体时。

当然,必须处理来自IDbCommand的一堆IDataReader个实例和手动映射会很可怕,所以我今天早上花了几个小时来编写一个库以完成大部分工作。对我来说很重要,为IDbCommand暴露了一个“流利的”(我使用松散的术语)包装,一个使用LinqDataReaderMapper的自动MetaModel所以我可以使用我现有的实体修改和一堆重载的扩展方法,可以更快地编写更简单的查询。

在一天结束时,我写的是这样的:

var results = context.Connection
    .Command("SELECT Column1, Column2 FROM Table " +
             "WHERE FilterColumn1 = @Param1 AND FilterColumn2 = @Param2")
    .Parameters(
        p => p.Name("Param1").Value(someValue),
        p => p.Name("Param2").Value(someOtherValue))
    .ExecuteReader()
    .MapWith(context.Mapping).To<MyEntity>();

或者只是这个:

var results = context
    .ExecuteQueryRaw<MyEntity>(CommandType.StoredProcedure, "SomeProc",
        new { Param1 = someValue, Param2 = someOtherValue });

我不会为它发布整个代码 - 它很长,而且我认为此时它只是一个很大的 tl; dr - 但主要的想法是这两者都是给我一个IEnumerable<MyEntity>,因为它们是直接从IDataReader复制的,所以它们本质上是分离的实体 - DataContext对它们没有直接的了解,因此它们都不能截取结果也不跟踪它们。

所以,问题部分解决了,尽管如果我能让DataContext表现得更好,那仍然会更好。

答案 1 :(得分:0)

如果从LINQ查询为结果构造新对象,则不会对其进行更改跟踪。因此,您可以使用现有的DataContext来加载您不需要更新的值,例如here中的代码):

using (NorthwindDataContext context = new NorthwindDataContext())
{
  var a = from c in context.Categories
  select new Category
  {
    CategoryID = c.CategoryID,
    CategoryName = c.CategoryName,
    Description = c.Description
  };
}

我认为这基本上就是你在答案的第二个例子中的内容,我只想确认这是有效的,并不是一个坏主意。

另外,我的理解是你不应该在任何情况下使用单个大量的DataContext; DataContext实际上是一个工作单元级别集合,而不是整个世界。因此,为您的只读类型数据和可更新数据使用单独的DataContexts非常有意义(除非您无法预测哪个是提前的,我想)。