繁体   English   中英

如何使用通用映射方法将多个实体从LINQ查询映射到新实体

[英]How to map multiple entities from LINQ query into a new entity using a common map method

以下代码与我的解决方案非常相似,不同之处仅在于“ entitiesA”和“ entitiesB”集合的值使用LINQ数据上下文存储在SQL Server数据库中。 该代码在控制台应用程序中运行完美,但是当从数据库中检索值时,系统会显示“无法将表达式'...'转换为SQL,也无法将其视为本地表达式。”。 我做错了什么?

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqSelectDemo
{
    class EntityA
    {
        public int ID { get; set; }
        public string StringValue { get; set; }
    }

    class EntityB
    {
        public int ID { get; set; }
        public int? EntityAID { get; set; }
        public int IntValue { get; set; }
    }

    class EntityC
    {
        public int ID { get; set; }
        public string StringValue { get; set; }
        public int IntValue { get; set; }
    }

    class Program
    {
        static List<EntityA> entitiesA;

        static List<EntityB> entitiesB;

        static EntityC MapEntityC(EntityB entityB, EntityA entityA = null)
        {
            if (entityA == null)
            {
                entityA = entitiesA.FirstOrDefault(entity => (entity.ID == entityB.EntityAID));
            }
            return new EntityC()
            {
                ID = ((entityA != null) ? entityA.ID : 0) + ((entityB != null) ? entityB.ID : 0),
                StringValue = (entityA != null) ? entityA.StringValue : null,
                IntValue = (entityB != null) ? entityB.IntValue : int.MinValue
            };
        }

        static void Main(string[] args)
        {
            entitiesA = new List<EntityA>()
            {
                new EntityA() { ID = 11, StringValue = "string1" },
                new EntityA() { ID = 12, StringValue = "string2" },
                new EntityA() { ID = 13, StringValue = "string3" },
            };
            entitiesB = new List<EntityB>()
            {
                new EntityB() { ID = 21, IntValue = 1, EntityAID = 11 },
                new EntityB() { ID = 22, IntValue = 2 },
                new EntityB() { ID = 23, IntValue = 3, EntityAID = 13 },
            };

            List<EntityC> entitiesC1 = entitiesB.GroupJoin(entitiesA, entityB => entityB.EntityAID, entityA => entityA.ID, (entityB, entityA) => new { entityB, entityA = entityA.SingleOrDefault() })
                                                .Select(entity => MapEntityC(entity.entityB, entity.entityA))
                                                .ToList();

            List<EntityC> entitiesC2 =
            (
                from entityB in entitiesB
                join entityA in entitiesA on entityB.EntityAID equals entityA.ID into tempEntitiesA
                from entityA in tempEntitiesA.DefaultIfEmpty()
                select MapEntityC(entityB, entityA)
            ).ToList();

            foreach (EntityC entityC in entitiesC1)
            {
                Console.WriteLine("ID: {0}, StringValue: '{1}', IntValue: {2}", entityC.ID, entityC.StringValue, entityC.IntValue);
            };

            Console.WriteLine();

            foreach (EntityC entityC in entitiesC2)
            {
                Console.WriteLine("ID: {0}, StringValue: '{1}', IntValue: {2}", entityC.ID, entityC.StringValue, entityC.IntValue);
            };
        }
    }
}

我认为您不应该创建linq-to-objectslinq-to-entities兼容查询。 即使它们看起来相似,但在功能上却完全不同。 因此,我提出的解决方案将忽略事物的“ linq-to-objects方面。


第一步是创建一个包含两个实体的类。 在这种情况下, EntityAEntityB

public class EntityBandA
{
   public EntityB EntityB { get;set; }
   public EntityA EntityA { get;set; }
}

现在我们需要创建一个IQueryable转换方法。 此方法的目的是将新的IQueryable<EntityBandA>转换为IQueryable<EntityC> 注意-仅在我们对数据库进行迭代(即ToListFirstOrDefault等)之后,此操作FirstOrDefault数据库运行。

static IQueryable<EntityC> MapEntityC(IQueryable<EntityBandA> query)
{
  var query = from e in query
              select new EntityC()
              {
                ID = e.EntityA.ID + e.EntityB,
                StringValue = e.EntityA.StringValue,
                IntValue = e.EntityB != null ? entityB.IntValue : int.MinValue
            };
  return query;
}

注意,我们删除了一些空检查。 当使用linq-to-entities ,它们不是必需的,因为我们的表达式已被翻译成sql。 如果找不到任何值,它将选择默认值。 Int:0, String:null


现在我们需要调整您当前的应用程序以使用我们的新方法

IQueryable<EntityBandA> queryBandA = entitiesB.GroupJoin(entitiesA, entityB => entityB.EntityAID, entityA => entityA.ID, 
                                                 (entityB, entityA) => new EntityBandA 
                                                 { 
                                                    EntityB = entityB, 
                                                    EntityA = entityA.SingleOrDefault()
                                                 });

List<EntityC> entitiesC1 = MapEntityC(queryBandA).ToList();

这意味着Linq To SQL无法MapEntity()调用映射到相等的SQL表达式。

您需要从Linq To SQL中获取数据(也许可以通过查询包含JOINVIEW来完成,这很容易做到),并且在接收到数据并将其存储在内存后(在List ),然后将其映射到EntityC数据类型。

另外,您可以通过将整个表读入内存中来下载列表entitiesA和entityB,如下所示:

entitiesA = DbContext.EntitiesA.ToList();
entitiesB = DbContext.EntitiesB.ToList();

然后,您可以继续使用所使用的表达式,因为现在使用的是内存中列表,因此使用的是常规LINQ to Object,而不是LINQ to SQL。

谢谢大家花一点时间写一个可能的解决方案和/或评论。 所有的想法都很棒。 实际上,原始代码按预期方式工作。 问题是我不是用平凡的参数而是使用三次运算符来调用映射函数。 就像是:

Select(entity => MapEntityC(entity.entityB,
                           (entity.entityA != null) ? entity.entityA : new EntityA()));

LINQ to SQL无法转换为SQL语句。 一旦删除该表达式并使用了一个简单的表达式,该代码就会起作用。 再次感谢您。

暂无
暂无

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

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