繁体   English   中英

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

[英]EF6 using custom property in a linq query

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

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

DelegateDecompiler软件包https://github.com/hazzik/DelegateDecompiler可以处理这种情况。

使用Computed属性装饰属性,然后在添加Decompile方法的情况下进行如下查询:

db.Cars.Decompile().SingleOrDefault(c => c.Id == id && c.Key == key)

有许多第三方程序包可以解决此问题。 我也相信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)

这将评估数据库中的所有代码,同时将您的业务逻辑封装在代码中。

LocalSequenceNumber您忘了告诉我们什么是Process.NameLocalSequenceNumber 从标识符看来,它们不是您Cars一部分,而是本地流程中的值。 为什么不在查询之前计算密钥?

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

如果,另一方面, Process.NameLocalSequenceNumberCar性能,你必须改变IQueryable.Expression只使用性能,并且可以通过你的翻译方法就是在你的LINQ查询IQueryable.Provider到SQL。

幸运的是,您的Provider知道ToSTring()和字符串串联的概念,因此您可以使用它

当您在Queryable.Where中使用属性Key时,建议使用功能WhereKey扩展IQueryable 如果扩展功能对您来说有点神奇,请参阅扩展方法揭秘

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 与在Id上选择的Where串联。

 var result = myDbContext.Cars
    .Where(car => car.Id == id)
    .WhereKey(carKey)
    .FirstOrDefault();

如果Process.NameLocalSequenceNumber不是Car的一部分,请将其添加为参数。 你明白了。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM