簡體   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