EF6在linq查询中使用自定义属性

时间:2018-10-15 04:16:29

标签: c# asp.net-mvc linq entity-framework-6

我有一个具有以下属性的类:

[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);

3 个答案:

答案 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”解决方案。

  1. 分两部分执行查询-SQL部分,然后是“ in code”部分。

db.Cars.Where(c => c.Id == id).ToList().SingleOrDefault(c => c.Key == key)

这仍将您的逻辑封装在类中,但是您没有从SQL执行中受益。

  1. 我喜欢称之为“投影仪”模式的东西。这个比较冗长。

基本上,您将创建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.NameLocalSequenceNumber是什么。从标识符看来,它们不是您Cars的一部分,而是您本地过程中的值。为什么不在查询之前计算密钥?

var key = string.Format("{0}_{1}", Process.Name, LocalSequenceNumber);
db.Cars.SingleOrDefault(c => c.Id == id && c.Key == key);

另一方面,如果Process.NameLocalSequenceNumberCar属性,则必须仅使用属性来更改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.NameLocalSequenceNumber不是Car的一部分,请将其添加为参数。你明白了。

请考虑创建仅检查密钥的WhereKey。与在Where上选择的Id串联。

如果需要,您可以创建一个WhereKeyFirstOrDefault(),但是我怀疑这是否会有用。