如何在LINQ&中查询实体框架未映射的属性

时间:2018-06-13 09:35:48

标签: entity-framework linq

所以我部分地从SO answer开始关于如何在Entity Framework中存储具有数组数据类型的属性。我没有回答的问题是将字符串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.'
}

1 个答案:

答案 0 :(得分:1)

如果实体框架访问注释为[NotMapped]的属性,则它无法将LINQ表达式转换为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();