【问题标题】:How to query in LINQ & Entity Framework the unmapped property如何在 LINQ 和实体框架中查询未映射的属性
【发布时间】:2018-06-13 09:35:48
【问题描述】:

所以我从SO answer 部分遵循了如何在实体框架中存储具有数组数据类型的属性。我没有按照该答案将字符串 InternalData 设置为私有而不是公共,因为如果将其设置为公共,我发现它是一种代码味道(还没有足够的声誉来评论)。

我还设法从这个blog 映射实体框架中的私有属性。

当我从该实体执行 CR(创建、读取)时,一切顺利。但是,当我的 LINQ 查询有一个 where 子句使用该属性和数组数据类型时,它说 "System.NotSupportedException: '指定的类型成员在 LINQ to Entities 中不受支持。只有初始化程序、实体成员和实体导航支持属性。'"

如何解决这个问题?以下是相关代码块:

public class ReminderSettings
{
   [Key]
   public string UserID { get; set; }
   [Column("RemindForPaymentStatus")]
   private string _remindForPaymentStatusCSV { get; set; }
   private Status[] _remindForPaymentStatus;
   [NotMapped]
   public Status[] RemindForPaymentStatus
   {
      get 
      {
         return Array.ConvertAll(_remindForPaymentStatusCSV.Split(','), e => (Status)Enum.Parse(typeof(Status), e));
      }
      set
      {
         _remindForPaymentStatus = value;
         _remindForPaymentStatusCSV = String.Join(",", _remindForPaymentStatus.Select(x => x.ToString()).ToArray());
      }
   }
   public static readonly Expression<Func<ReminderSettings, string>> RemindForPaymentStatusExpression = p => p._remindForPaymentStatusCSV;
}

public enum Status
{
  NotPaid = 0,
  PartiallyPaid = 1,
  FullyPaid = 2,
  Overpaid = 3 
}

protected override void OnModelCreating(DbModelBuuilder modelBuilder)
{
   modelBuilder.Entity<ReminderSettings>().Property(ReminderSettings.RemindForPaymentStatusExpression);
}

//This query will cause the error
public IEnumerable<ReminderSettings> GetReminderSettingsByPaymentStatus(Status[] statusArray)
{
   var query = ApplicationDbContext.ReminderSettings.Where(x => x.RemindForPaymentStatus.Intersect(statusArray).Any());
   return query.ToList(); //System.NotSupportedException: 'The specified type member 'RemindForPaymentStatus' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.'
}

【问题讨论】:

    标签: entity-framework linq


    【解决方案1】:

    如果 LINQ 表达式访问注释为 [NotMapped] 的属性,则实体框架无法将它们转换为 SQL。 (如果属性在其 getter/setter 中包含自定义 C# 代码,它也无法翻译)。

    作为一种快速(但性能可能较低)的解决方法,您可以执行不会导致问题的查询部分,然后在内存中应用额外的过滤。

    // execute query on DB server and fetch items into memory
    var reminders = dbContext.ReminderSettings.ToList(); 
    
    // now that we work in-memory, LINQ does not need to translate our custom code to SQL anymore
    var filtered = reminders.Where(r => r.RemindForPaymentStatus.Contains(Status.NotPaid)); 
    

    如果这会导致性能问题,您必须公开 NotMapped 属性的支持字段并直接使用它。

    var filtered = dbContext.ReminderSettings
        .Where(r => r._remindForPaymentStatusCSV.Contains(Status.NotPaid.ToString("D"));
    

    编辑 要将多个状态作为查询参数处理,您可以在循环中附加 Where 子句(其行为类似于 AND)。只要您的状态枚举值是可区分的(即,如果还有状态“1”,则没有状态“11”)。

    var query = dbContext.ReminderSettings.Select(r => r);
    foreach(var statusParam in queryParams.Status) {
        var statusString = statusParam.ToString("D");
        query = query.Where(r => r._remindForPaymentStatusCSV.Contains(statusString));
    }
    var result = query.ToArray();
    

    【讨论】:

    • 谢谢乔治。我选择后者,因为它是可扩展的。尽管在 where 子句中,我想比较状态数组与状态参数/输入数组。我为这个问题编辑了我的问题。我尝试用这个组成一个 where 子句:但我仍然得到一个错误。 var query = ApplicationDbContext.ReminderSettings .Where(x => Array.ConvertAll(x._remindForPaymentStatusCSV.Split(','), e => (Status)Enum.Parse(typeof(Status), e)).Intersect(statusArray) 。任何());返回查询.ToList();
    • 您也可以这样做无需首先实现数据集,前提是您的数据格式良好(不是 CSV,但 JSON 会好得多):stackoverflow.com/a/52953313/4564272
    猜你喜欢
    • 2015-11-27
    • 1970-01-01
    • 2015-01-22
    • 1970-01-01
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多