gpt4 book ai didi

c# - 如何根据 rowversion/timestamp 值查询 Code First 实体?

转载 作者:IT王子 更新时间:2023-10-29 04:44:05 25 4
gpt4 key购买 nike

我遇到过这样的情况,在 LINQ to SQL 中工作得很好的东西在 Entity Framework 中似乎非常迟钝(或者可能是不可能的)。具体来说,我有一个包含 rowversion 属性的实体(用于版本控制和并发控制)。像这样的东西:

public class Foo
{
[Key]
[MaxLength(50)]
public string FooId { get; set; }

[Timestamp]
[ConcurrencyCheck]
public byte[] Version { get; set; }
}

我希望能够将一个实体作为输入,并找到最近更新的所有其他实体。像这样的东西:

Foo lastFoo = GetSomeFoo();
var recent = MyContext.Foos.Where(f => f.Version > lastFoo.Version);

现在,在数据库中这会起作用:两个 rowversion 值可以毫无问题地相互比较。而我在使用LINQ to SQL之前也做过类似的事情,将rowversion映射到System.Data.Linq.Binary,可以进行比较。 (至少在表达式树可以映射回数据库的范围内。)

但是在Code First中,属性的类型必须是byte[]。并且两个数组不能用常规比较运算符进行比较。有没有其他方法可以编写 LINQ to Entities 可以理解的数组比较?或者将数组强制转换为其他类型,以便比较可以通过编译器?

最佳答案

找到了一个完美的解决方法!在 Entity Framework 6.1.3 上测试。

无法使用 <运算符与字节数组,因为 C# 类型系统阻止了这种情况(它应该这样做)。但是您可以做的是使用表达式构建完全相同的语法,并且有一个漏洞可以让您实现这一目标。

第一步

如果您不想要完整的解释,可以跳到解决方案部分。

如果您不熟悉表达式,这里是 MSDN's crash course .

基本上,当您输入 queryable.Where(obj => obj.Id == 1)编译器实际上输出的内容与您输入的内容相同:

var objParam = Expression.Parameter(typeof(ObjType));
queryable.Where(Expression.Lambda<Func<ObjType, bool>>(
Expression.Equal(
Expression.Property(objParam, "Id"),
Expression.Constant(1)),
objParam))

并且该表达式是数据库提供程序解析以创建您的查询的内容。这显然比原来的要冗长得多,但它也允许您像进行反射一样进行元编程。冗长是这种方法的唯一缺点。与此处的其他答案相比,这是一个更好的缺点,例如必须编写原始 SQL 或无法使用参数。

在我的例子中,我已经在使用表达式,但在你的例子中,第一步是使用表达式重写你的查询:

Foo lastFoo = GetSomeFoo();
var fooParam = Expression.Parameter(typeof(Foo));
var recent = MyContext.Foos.Where(Expression.Lambda<Func<Foo, bool>>(
Expression.LessThan(
Expression.Property(fooParam, nameof(Foo.Version)),
Expression.Constant(lastFoo.Version)),
fooParam));

这就是我们如何避免在尝试使用 < 时遇到的编译器错误在 byte[]对象。现在不是编译器错误,而是运行时异常,因为 Expression.LessThan试图找到 byte[].op_LessThan并在运行时失败。 这就是漏洞所在。

漏洞

为了消除运行时错误,我们将告诉 Expression.LessThan使用什么方法,这样它就不会尝试找到不存在的默认方法 ( byte[].op_LessThan):

var recent = MyContext.Foos.Where(Expression.Lambda<Func<Foo, bool>>(
Expression.LessThan(
Expression.Property(fooParam, nameof(Foo.Version)),
Expression.Constant(lastFoo.Version),
false,
someMethodThatWeWrote), // So that Expression.LessThan doesn't try to find the non-existent default operator method
fooParam));

太棒了!现在我们只需要 MethodInfo someMethodThatWeWrote从带有签名的静态方法创建 bool (byte[], byte[])以便类型在运行时与我们的其他表达式匹配。

解决方案

你需要一个小DbFunctionExpressions.cs .这是一个截断的版本:

public static class DbFunctionExpressions
{
private static readonly MethodInfo BinaryDummyMethodInfo = typeof(DbFunctionExpressions).GetMethod(nameof(BinaryDummyMethod), BindingFlags.Static | BindingFlags.NonPublic);
private static bool BinaryDummyMethod(byte[] left, byte[] right)
{
throw new NotImplementedException();
}

public static Expression BinaryLessThan(Expression left, Expression right)
{
return Expression.LessThan(left, right, false, BinaryDummyMethodInfo);
}
}

用法

var recent = MyContext.Foos.Where(Expression.Lambda<Func<Foo, bool>>(
DbFunctionExpressions.BinaryLessThan(
Expression.Property(fooParam, nameof(Foo.Version)),
Expression.Constant(lastFoo.Version)),
fooParam));
  • 享受吧。

注意事项

不适用于 Entity Framework Core 1.0.0,但我 opened an issue那里有更全面的支持,无论如何都不需要表达。 (EF Core 不起作用,因为它经历了一个阶段,它使用 LessThanleft 参数复制 right 表达式,但不复制我们用于漏洞的 MethodInfo 参数。)

关于c# - 如何根据 rowversion/timestamp 值查询 Code First 实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7437970/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com