繁体   English   中英

在LINQ to Entities中使用中间实体会导致System.NotSupportedException

[英]Using an intermediate entity in LINQ to Entities leads to System.NotSupportedException

我的实体模型主要由6个实体组成,它们基于2个属性相互连接。

我必须根据旧的纯文本SQL查询构建182个LINQ查询。 这些查询有一些共同点,所以为了避免重复,我构建了一个小框架来构建构建块之上的查询。 我马上就会展示一些例子。

由于所有查询都是由6个实体的组合之间的连接构成的(从根实体SectionA ),为了方便起见,我构建了一个包装器类JoinOfSections ,它包装了所有这些部分。

在该类上,我可以执行所有182个查询通用的简单LINQ评估。

例如

    public override IQueryable<QueryRow> Run(Models.auitool2014Entities dataContext, int aUserId, DateTime? dataInizioControllo, DateTime? dataFineControllo, string[] a52Exclude, bool codifiche2014)
        string now = DateTime.Now.ToString("yyyyMMdd");

        var sj = dataContext.sezione_a.SelectMany(
            a => dataContext.sezione_d.Where(d => a.A03 == d.A03 && a.utente == d.utente).DefaultIfEmpty(),
            (a, d) => new SezioneJoin { A = a, D = d });

        sj = CommonFiltering(sj, aUserId, dataInizioControllo, dataFineControllo, a52Exclude);

        return (from SezioneJoin ssj in sj
                let a = ssj.A
                let d = ssj.D

                where
                 a.utente == aUserId &&
                 (
                 String.IsNullOrEmpty(a.A21) || String.IsNullOrEmpty(a.A51) ||
                 a.A51.CompareTo(a.A21) < 0 ||
                 a.A21.CompareTo(now) > 0 ||
                 a.A51.CompareTo(now) > 0 ||
                 a.A21.CompareTo("19000101") < 0 ||
                 a.A51.CompareTo("19000101") < 0
                 )
                select ssj).Select(Select());
    }

    protected virtual IQueryable<SezioneJoin> CommonFiltering(IQueryable<SezioneJoin> sj, int aUserId, DateTime? dataInizioControllo, DateTime? dataFineControllo, string[] a52Exclude)
    {
        sj = sj.Where(x => x.A.utente == aUserId);
        if (dataInizioControllo != null)
        {
            string dataInzio = dataInizioControllo.Value.ToString("yyyyMMdd");
            sj = sj.Where(x => x.A.A21.CompareTo(dataInzio) >= 0);
        }
        if (dataFineControllo != null)
        {
            string dataFine = dataFineControllo.Value.ToString("yyyyMMdd");
            sj = sj.Where(x => x.A.A21.CompareTo(dataFine) <= 0);
        }
        if (a52Exclude != null)
            sj = sj.Where(x => !a52Exclude.Contains(x.A.A52));

        return sj.Take(Parameters.ROW_LIMIT);
    }

Select()方法是常见模式的简化。 由于结果集必须展平以便旧组件处理它,我发明了另一个适配器层

[Serializable]
public class QueryRow
{
    public string A01 { get; set; }

    public string A01a { get; set; }

    public string A01b { get; set; }

    public string A02 { get; set; }

    public string A03 { get; set; }

    public string A11 { get; set; }

    public string A12 { get; set; }

    // Dozens of string members
}

为了不复制和粘贴......

    protected virtual Expression<Func<SezioneJoin, QueryRow>> Select()
    {
        return sj => new QueryRow
        {
            A01 = sj.A.A01,
            A01a = sj.A.A01a,
            A01b = sj.A.A01b,
            A02 = sj.A.A02,
            A03 = sj.A.A03,
            A11 = sj.A.A11,
            A12 = sj.A.A12,
            A12a = sj.A.A12a,
            A12b = sj.A.A12b,
            A12c = sj.A.A12c,
            A21 = sj.A.A21,
            A22 = sj.A.A22,
            A23 = sj.A.A23,
            A24 = sj.A.A24,
            A25 = sj.A.A25,
            A31 = sj.A.A31,
            A31a = sj.A.A31a,
            A31b = sj.A.A31b,
            A32 = sj.A.A32,
        }

SezioneJoin类表示数据集中的一行,它是多个实体之间JOIN的组合,如下所示。 它的设计是为了让任何查询实例化其自定义JOIN(例如A内部D,A左D左E,A内D左H)

public class SezioneJoin
{

    public SezioneA A { get; set; }

    public SezioneD D { get; set; }

    public SezioneE E { get; set; }

    public SezioneF F { get; set; }

    public SezioneG G { get; set; }

    public SezioneH H { get; set; }

}

基本上所有查询都要求在当前用户ID和可选检查日期上过滤数据集,并且它们允许最大数量的结果。

我付出了尝试用NotSupportedException概括概念(由我翻译的异常消息)

无法将“DiagnosticoSite.Data.Query.SezioneJoin”类型转换为“DiagnosticoSite.Data.Query.SezioneJoin”。 LINQ to Entities仅支持Enum或原始EDM数据类型

问题可能在于(a, d) => new SezioneJoin { A = a, D = d }行:如果我选择一个匿名类型LINQ查询工作正常,但是我无法将查询对象传递给受保护的用额外的常见检查来装饰它的方法。

作为182查询,我必须找到一种方法来为所有复制和粘贴的查询添加常用检查。

我想知道如何使用不在数据上下文中的“缓冲区”或“中间”实体来操纵LINQ to Entities查询,以便查询本身(复杂)可以作为参数传递给装饰器方法。

在枚举和调用Run返回的IQueryable的ToString()方法时都会发生错误。 我需要ToString来提取发给DB的查询

EF可能不知道SezioneJoin是什么。

您可以将这样的类型定义为实体,也许它可以工作。 但是,如果您的CommonFiltering是现实的,那就没有必要了。 考虑:

var q = CommonFiltering(dataContext.sezione_a, aUserId, dataInizioControllo, dataFineControllo, a52Exclude)
  .Join(
        dataContext.sezione_d,
        a => new { A03 = a.A03, User = a.utente },
        d => new { A03 = d.A03, User = d.utente },
        (a, d) => new SezioneJoin { A = a, D = d }
      )
    .Where(x =>
      ...
    ).Take(Parameters.ROW_LIMIT);

protected virtual IQueryable<AType> CommonFiltering(IQueryable<SezioneA> sj, int aUserId, DateTime? dataInizioControllo, DateTime? dataFineControllo, string[] a52Exclude)
{
  sj = sj.Where(x => x.utente == aUserId);
  if (dataInizioControllo != null)
  {
    string dataInzio = dataInizioControllo.Value.ToString("yyyyMMdd");
    sj = sj.Where(x => x.A21.CompareTo(dataInzio) >= 0);
  }
  if (dataFineControllo != null)
  {
    string dataFine = dataFineControllo.Value.ToString("yyyyMMdd");
    sj = sj.Where(x => x.A21.CompareTo(dataFine) <= 0);
  }
  if (a52Exclude != null)
    sj = sj.Where(x => !a52Exclude.Contains(x.A52));

  return sj;
}

实际上,如果过滤点在同一步骤中关注AD某些质量,您只需要担心将连接的结果而不是组件传递给它。 在这里,您的过滤只关心A ,这是一个定义的类型。

如果你真的需要处理类似的东西,你可以建立表达式。

为了简化,我们只考虑具有签名IQueryable<T> Filter<T>(IQueryable<T> source, bool makeA21Match) 如果makeA21Match为true,那么我们将Where(sj => sj.A.A21 == sj.D.A21)到查询中,否则我们不会:

private static IQueryable<T> Filter<T>(IQueryable<T> source, bool makeA21Match)
{
  if(makeA21Match)
  {
    var getA = typeof(T).GetProperty("A"); // .A
    var getD = typeof(T).GetProperty("D"); // .D
    var getAA21 = typeof(SezioneA).GetProperty("A21"); // a.A21 for some A.
    var getDA21 = typeof(SezioneD).GetProperty("A21"); // d.A21 for some D.
    var parExp = Expression.Parameter(typeof(T)); // sj.
    var getAExp = Expression.Property(parExp, getA); // sj.A
    var getDExp = Expression.Property(parExp, getD); // sj.D
    var getAA21Exp = Expression.Property(getAExp, getAA21); // sj.A.A21
    var getDA21Exp = Expression.Property(getDExp, getDA21); // sj.D.A21
    var eqExp = Expression.Equal(getAA21Exp, getDA21Exp); // sj.A.A21 == sj.D.A21
    var λExp = Expression.Lambda<Func<T, bool>>(eqExp, parExp); // sj => sj.A.A21 == sj.D.A21
    source = source.Where(λExp);
  }
  return source;
}

这比在C#代码中使用lambda表达式更复杂,并且缺少类型的编译时检查,但它确实意味着我们可以将表达式应用于匿名类型的可查询,并且性能应该在这样的案件。

一个平衡的方法是首先尽可能地过滤sezione_asezione_d ,因为这可以更容易地完成,然后有一个方法只处理那些绝对需要它的情况下更复杂的表达式构建手工编码。 您甚至可以缓存必要时生成的Expression<Func<T, bool>>

暂无
暂无

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

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