Complex Linq query is not working as expected

后端 未结 2 840
粉色の甜心
粉色の甜心 2021-01-07 14:05

I want to combine results from 4 tables and select specific fields using LINQ. Please bear with me since I have not done complex LINQ queries.

Table 1 - Subscriber<

相关标签:
2条回答
  • 2021-01-07 14:49

    It's not really LINQ that's tripping up here, it's LINQ to Entities. Are you using Entity Framework? Does your model have the relationships defined in it? If you have foreign keys in your database, and build your model in Entity Framework with database first, it will map all the entity relationships for you.

    If yes, you can then do something like:

    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));
            }
    
        }
    }
    

    Output:

    Hello World subscriber: Arnold, subscriptions: Subscription A, Subscription B, Subscription C subscriber: Betty, subscriptions: subscriber: Christopher, subscriptions: Subscription A

    0 讨论(0)
  • The results is not including subscribers without subscriptions.

    When writing queries, always first try to determine the root entity. You're interested in subscriptions, so it seems obvious to take Subscription as root entity. But in fact you want to see whether or not subscribers have subscriptions, and if so, which. Subscriber is the root entity, so start the query there.

    Figured out how to concatenate the string

    Sure, db.Subscriptions.ToList() does allow you to do anything that LINQ-to-objects has in store, but it's very inefficient. First, you pull all Subscription data into memory. Then, in var model = (from s in query ... you join with DbSets that each pull all their data into memory. (Because query is IEnumerable and, hence, can't be combined with IQueryables into one expression and then translated into one SQL statement).

    The strategy for using non-supported methods in LINQ-to-Entities queries is: query the exact amount of data --no more, no less-- then continue in memory.

    Both points amount to this query:

    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)
                 }));
    

    Of course, if you're querying almost all fields from Subscriber this can be a bit less verbose: select new { Subscriber = s, Pubs = .. } etc. But I usually experience that the performance gain of narrowing down the SQL result set is greatly underestimated as compared to shortening it by filtering.

    0 讨论(0)
提交回复
热议问题