我有一个具有以下属性的类:
[NotMapped]
public string Key
{
get
{
return string.Format("{0}_{1}", Process.Name, LocalSequenceNumber);
}
}
本地序列号是由高速缓存支持的并发字典形式的计算整数。
我希望在LINQ查询中使用上面的Key属性,但会收到异常:
LINQ to Entities不支持指定的类型成员'Key'。仅支持初始化程序,实体成员和实体导航属性。
我了解为什么会收到此错误,但是我不太确定如何解决该错误。当前,Key属性为我不想提供的类提供了很好的封装。关于库的任何建议,或者解决此问题的简单模式?
编辑:这是引发异常的查询:
db.Cars.SingleOrDefault(c => c.Id == id && c.Key == key);
答案 0 :(得分:2)
DelegateDecompiler软件包https://github.com/hazzik/DelegateDecompiler处理这种情况。
使用Computed
属性装饰属性,然后在添加Decompile
方法的情况下进行如下查询:
db.Cars.Decompile().SingleOrDefault(c => c.Id == id && c.Key == key)
答案 1 :(得分:2)
有许多第三方软件包可以解决此问题。我也相信EF.Core中有一些方法可以提供帮助,但是,我将建议2个“纯实体框架6”解决方案。
db.Cars.Where(c => c.Id == id).ToList().SingleOrDefault(c => c.Key == key)
这仍将您的逻辑封装在类中,但是您没有从SQL执行中受益。
基本上,您将创建EF POCO的“视图”,它表示数据传输对象。它具有视图所需的属性,还决定了如何将数据从数据库投影到视图。
// Poco:
public class Car {
public int Id {get;set;}
public string LocalSequenceNumber {get;set;}
public int ProcessId {get;set; }
public virtual Process Process {get;set;}
// ...
}
public class Process {
// ...
}
// View+Projector:
public class CarView
{
public int Id {get;set;}
public string Color {get;set;}
public string Key {get;set;}
public static Expression<Func<Car, CarView>> Projector = car => new CarView {
Id = car.Id,
Color = car.Color,
Key = car.Process.Name + " " + car.LocalSequenceNumber
}
}
// calling code
var car = db.Cars.Select(CarView.Project).SingleOrDefault(cv => cv.Id == id && cv.Key == key)
这将评估数据库中的所有代码,同时将业务逻辑封装在代码中。
答案 2 :(得分:0)
las,您忘记告诉我们Process.Name
和LocalSequenceNumber
是什么。从标识符看来,它们不是您Cars
的一部分,而是您本地过程中的值。为什么不在查询之前计算密钥?
var key = string.Format("{0}_{1}", Process.Name, LocalSequenceNumber);
db.Cars.SingleOrDefault(c => c.Id == id && c.Key == key);
另一方面,如果Process.Name
或LocalSequenceNumber
是Car
属性,则必须仅使用属性来更改LINQ查询中的IQueryable.Expression
以及IQueryable.Provider
可以将其转换为SQL的方法。
幸运的是,您的Provider
知道ToSTring()
以及字符串串联的概念,因此您可以使用它
当您在Key
中使用属性Queryable.Where
时,建议使用功能IQueryable
扩展WhereKey
。如果扩展功能对您来说有点神奇,请参阅Extension Methods Demystified
public static IQueryable<Car> WhereKey(this IQueryable<Car> cars, int id, string key)
{
return cars.Where(car => car.Id == id
&& key == car.Process.Name.ToString() + "_" + car.LocalSequenceNumber.ToString());
}
用法:
int carId = ...
string carKey = ...
var result = myDbContext.Cars
.WhereKey(carId, carKey)
.FirstOrDefault();
请考虑创建仅检查密钥的WhereKey
。与在Where
上选择的Id
串联。
var result = myDbContext.Cars
.Where(car => car.Id == id)
.WhereKey(carKey)
.FirstOrDefault();
如果Process.Name
或LocalSequenceNumber
不是Car的一部分,请将其添加为参数。你明白了。
请考虑创建仅检查密钥的WhereKey
。与在Where
上选择的Id
串联。
如果需要,您可以创建一个WhereKeyFirstOrDefault()
,但是我怀疑这是否会有用。