复杂的Linq查询无法正常工作

时间:2018-06-28 19:52:22

标签: c# linq

我想合并4个表的结果,并使用LINQ选择特定的字段。 因为我还没有完成复杂的LINQ查询,所以请多多包涵。

表1-订户

表2-订阅

表3-状态

表4-国家

注意:订户可以有0个,1个或多个订阅。 这意味着外键(SubscriberID)是Subscription表的一部分

查询应从订户表一次返回每个订户。订户是否有订阅无关紧要。我需要在结果列表中包含所有订户。

这是复杂的地方:

在结果列表中,我想包含一个属性“ PubName”。此属性是一个逗号分隔的字符串,带有订阅者所订阅的发布者名称。 PubName是“订阅”表中的一列。

我已经用SQL编写了一个存储过程,并使用了一个附加功能来构造每个订阅者的PubName字段。

例如:我们的列表有3行:

  
      
  1. 维克多(Victor W),位于纽约W 45th st#43#43,“ Mag A,Mag B,Mag C”
  2.   
     

(Victor订阅了Mag A,B和C)

     
      
  1. 丹,564 E 23rd st FL3,纽约,纽约,“ Mag A,Mag D,Mag F”
  2.   
     

(Dan已订阅Mag A,D和F)

     
      
  1. Nicole,78 E 12rd st#3,纽约,纽约,“ NULL”
  2.   
     

(Nicole没有订阅)

    var model = await (
                from subscriber in db.Subscribers
                    // left join
                from state in db.States.Where(s => s.State_ID == subscriber.SubscriberState_ID).DefaultIfEmpty()
                    // left join
                from country in db.Countries.Where(s => s.Country_ID == subscriber.SubscriberCountry_ID).DefaultIfEmpty()                                   

                orderby subscriber.Subscriber_ID descending

                select new SubscriberGridViewModel
                {
                    Subscriber_ID = subscriber.Subscriber_ID, 
                    Pub = GetPubName(subscriber.Subscriber_ID).ToString(),                        
                    FirstName = subscriber.SubscriberFirstName,
                    LastName = subscriber.SubscriberLastName,
                    Address1 = subscriber.SubscriberAddress1,
                    Address2 = subscriber.SubscriberAddress2,
                    Email = subscriber.SubscriberEmail,
                    Organization = subscriber.SubscriberOrganizationName,
                    Phone = subscriber.SubscriberPhone,                                                
                    Zip = subscriber.SubscriberZipcode
                }).ToListAsync();

    private static string GetPubName(int? subscriber_id)
    {
        string pubs = string.Empty;

        try
        {
            var db = new CirculationEntities();

            var model = db.Subscriptions.Where(s => s.Subscriber_ID == subscriber_id).ToList();

            foreach(Subscription sub in model)
            {
                if (string.IsNullOrEmpty(pubs))
                    pubs = sub.SubscriptionPublication;
                else
                    pubs = ", " + sub.SubscriptionPublication;
            }

            return pubs;
        }
        catch
        {
            return "EMPTY";
        }                
    }

使用以下代码,我收到此错误:

  

“ LINQ to Entities无法识别方法'System.String GetPubName(System.Nullable`1 [System.Int32])',并且该方法无法转换为商店表达式。”

我了解错误。无法将方法转换为LINQ语句内的商店表达式。

  1. 是否可以在LINQ中实现这一目标?如果是这样,有人可以告诉我如何?我找不到解决方法。

弄清楚如何连接字符串

var query = from subscription in db.Subscriptions.ToList()
            group subscription by subscription.Subscriber_ID into g
            select new
            {
                Subscriber_ID = g.Key,
                Pub = string.Join(", ", g.Select(x => x.SubscriptionPublication).Distinct())
            };


var model = (from s in query
             join subscriber in db.Subscribers on s.Subscriber_ID equals subscriber.Subscriber_ID
             join state in db.States on subscriber.SubscriberState_ID equals state.State_ID
             join country in db.Countries on subscriber.SubscriberCountry_ID equals country.Country_ID 
             select new SubscriberGridViewModel
             {
                 Subscriber_ID = subscriber.Subscriber_ID,
                 Pub = s.Pub,
                 FirstName = subscriber.SubscriberFirstName,
                 LastName = subscriber.SubscriberLastName,
                 Address1 = subscriber.SubscriberAddress1,
                 Address2 = subscriber.SubscriberAddress2,
                 Email = subscriber.SubscriberEmail,
                 Organization = subscriber.SubscriberOrganizationName,
                 Phone = subscriber.SubscriberPhone,
                 City = subscriber.SubscriberCity,
                 State = (subscriber.SubscriberState_ID == 54) ? subscriber.SubscriberState : state.StateName,
                 StateAbbv = (subscriber.SubscriberState_ID == 54) ? subscriber.SubscriberState : state.StateAbbreviation,
                 Country = country.CountryName,
                 Zip = subscriber.SubscriberZipcode
             }).ToList();
  

结果不包括没有订阅的订阅者。   有任何解决方法的想法吗?

2 个答案:

答案 0 :(得分:2)

  

结果不包括没有订阅的订阅者。

在编写查询时,始终首先尝试确定根实体。您对订阅感兴趣,因此显然可以将Subscription作为根实体。但实际上,您想查看订户是否有订阅,如果有,则有。订户是根实体,因此从那里开始查询。

  

弄清楚如何连接字符串

当然,db.Subscriptions.ToList()确实允许您执行LINQ到对象存储中的任何操作,但是效率很低。首先,将所有Subscription数据拉入内存。然后,在var model = (from s in query ...中加入DbSet,每个将所有数据拉入内存。 (因为queryIEnumerable,因此不能与IQueryable组合成一个表达式,然后再转换成一个SQL语句。)

在LINQ-to-Entities查询中使用不受支持的方法的策略是:查询确切的数据量-不多,不少于-然后在内存中继续。

两个点都等于该查询:

var query = from s in db.Subcribers // root entity
    select new
    {
         Subscriber_ID = s.Subscriber_ID,
         FirstName = s.SubscriberFirstName,
         LastName = s.SubscriberLastName,
         Address1 = s.SubscriberAddress1,
         Address2 = s.SubscriberAddress2,
         Email = s.SubscriberEmail,
         Organization = s.SubscriberOrganizationName,
         Phone = s.SubscriberPhone,
         City = s.SubscriberCity,
         Zip = s.SubscriberZipcode,

         // Navigation properties here
         State = (s.SubscriberState_ID == 54) ? s.SubscriberState : s.State.StateName,
         StateAbbv = (s.SubscriberState_ID == 54) ? s.SubscriberState : s.State.StateAbbreviation,
         Country = s.Country.CountryName,

         // Empty list when no subscriptions
         Pubs = s.Subscriptions.Select(x => x.SubscriptionPublication).Distinct() 
    };
var result = query.AsEnumerable() // continue in memory
    Select(s => new SubscriberGridViewModel
             {
                 Subscriber_ID = s.Subscriber_ID,
                 FirstName = s.FirstName,
                 LastName = s.LastName,
                 Address1 = s.Address1,
                 Address2 = s.Address2,
                 Email = s.Email,
                 Organization = s.Organization,
                 Phone = s.Phone,
                 City = s.City,
                 State = s.State,
                 StateAbbv = s.StateAbbv,
                 Country = s.Country,
                 Zip = s.Zip
                 Pub = string.Join(", ", s.Pubs)
             }));

当然,如果您要查询Subscriber的几乎所有字段,那么可能会有些冗长:select new { Subscriber = s, Pubs = .. }等。但是我通常会体验到缩小性能< / em>与通过过滤缩短 的结果相比,SQL结果集被大大低估了。

答案 1 :(得分:1)

不是真的是LINQ,而是LINQ to Entities。您正在使用实体框架吗?您的模型中是否定义了关系?如果您的数据库中有外键,并且首先在带有数据库的Entity Framework中构建模型,它将为您映射所有实体关系。

如果是,则可以执行以下操作:

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

public class Program
{
    public class Subscriber{
        public string Name {get;set;}
        public List<Subscription> Subscriptions{get;set;}
    }

    public class Subscription{
        public string Name {get;set;}
    }

    public class MyViewModelItem{
        public string SubscriberName{get;set;}
        public string SubscriptionNames {get;set;}
    }

    public static void Main()
    {
        Console.WriteLine("Hello World");

        // create some dummy data
        var data = new List<Subscriber>{
            new Subscriber{
                Name = "Arnold",
                Subscriptions = new List<Subscription>(){
                    new Subscription{
                        Name = "Subscription A"
                    },
                    new Subscription{
                        Name = "Subscription B"
                    },
                    new Subscription{
                        Name = "Subscription C"
                    }
                }
            },
            new Subscriber{
                Name = "Betty",
                    Subscriptions = new List<Subscription>()
            },
            new Subscriber{
                Name = "Christopher",
                Subscriptions = new List<Subscription>(){
                    new Subscription{
                        Name = "Subscription A"
                    }
                }
            }
        };

        // here's the query and it becomes much simpler
        var myViewModel = data
            .Select(i=> new MyViewModelItem{
                SubscriberName = i.Name,
                SubscriptionNames = string.Join(", ", i.Subscriptions.Select(j=>j.Name))
            })
            .ToList();

        // this shows the output
        foreach(var item in myViewModel){
            Console.WriteLine(string.Format("subscriber: {0}, subscriptions: {1}",item.SubscriberName,item.SubscriptionNames));
        }

    }
}

输出:

Hello World 订阅者:Arnold,订阅:订阅A,订阅B,订阅C 订阅者:Betty,订阅: 订阅者:Christopher,订阅:Subscription A