Getting only specific columns in entity framework lambda join - c#

I am surprised I cannot find a solution for this on the web, but wording the search terms was a bit difficult. The question I have is about generating entity SQL that only returns the needed columns in a group join using Lambda syntax.
The following is a "toy" example. I am not joining on two entities, rather on an enumerated list and an entity. And tunnelling is not an acceptable answer. I need to apply this to a much larger problem using a group join and select many.
var result1 = clientprofiles.Join(Context.Adjusters,
c => c.AdjusterId,
a => a.AdjusterId,
(c, a) => new {a.ClientAccountId}).ToList();
Using Julie Lehrman's Entity profiler, I see that the query is being generated to select every record in the rows that meet the join criteria. How do I pare it down so it only selects the ClientAccountId field in this example?

You can project a set of columns on any select from the context, so in your case you can constrain the Context.Adjusters parameter by using
Context.Adjusters.Select(a=> new { a.ClientAccountId })
to constrain the query to just the single column

Related

Perform left join with Include() with Linq in C#

I'm facing an issue with Entity Framework Core in C#. I have created a function to return the list of Organisation from my database. For each organisation, I have two tables:
OrganisationUser: the link with Organisation is the OrganisationId. This table is linked to the Licence table with the LicenceId
Invitation the link with Organisation is the OrganisationId. This table is linked to the Licence table with the LicenceId
If I run the query I received, as I expected, the list of organisations with all details.
public IQueryable<Organisation> GetOrganisationsByBillingDay(int billingDay)
{
return _context.Organisation
.Include(org => org.OrganisationUser)
.ThenInclude(usr => usr.License)
.Include(org => org.Invitation)
.ThenInclude(i => i.License)
.Where(o => o.BillingDay == billingDay);
}
In this list the organisation that doesn't have any records in OrganisationUser or Invitation are excluded.
How is it possible to include them? Is there a diffrent way than writing a query with Linq?
Update
If you look at the query Linq creates it ia a left join.
The configuration is
builder.Services.AddDbContext<BillingContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("BillingDatabase"),
providerOptions => providerOptions.EnableRetryOnFailure()
));
Last Update
As you can see in the screenshot, Linq performs left join queries. I have now what I'm looking for. The problem was in the data not in the code.
If it is optional, then your navigation property will either be null or an empty collection depending upon what the cardinality of relationship is. There is nothing special you need to do as the join is implied by Include() but not explicitly specified by the user in the query itself.
The most likely issue is your relationship definition. Can you share your model config?

C# Entity Framework: Linq Filter on GrandChildren and Conduct a Select on the Parent with Attributes

Our company is currently using Entity Framework Net Core 2.2 with Sql Server
Trying to find all Distinct customers who purchased a certain Product Input Parameter .
The following EF Linq query was written to get the distinct Customers.
Later another question came up, how do we get more (navigation) properties of customer? Should Include be placed Before the Where or After the Where? Does it matter? When running the SQL Profiler, it noted no difference in the queries. I just wanted to be sure in some cases does the location of Include here matter?
select distinct c.customerName
from dbo.customer customer
inner join dbo.Transactions transaction
on transaction.customerid = customer.customerid
inner join dbo.Purchases purchases
on purchases.PurchaseId = transaction.PurchaseId
inner join dbo.Product product
on transaction.ProductId = product.ProductId
where tra.BKProduct = #ProductInput
Original Solution: C# Entity Framework: Linq Filter on GrandChildren and Conduct a Select on the Parent
var customerData = db.Customer
.Where(p => p.Transactions.SelectMany(c => c.Purchases).Select(gc => gc.Product.Where(gc => gc.BKProduct == ProductInput).Any());
Another question came up, we need to get more (navigation) properties of Customer.
Alternative 1:
var customerData = db.Customer
.Include(c=>c.CustomerType)
.Include(c=>c.CustomerAddress)
.Where(p => p.Transactions.SelectMany(c => c.Purchases).Select(gc => gc.Product.Where(gc => gc.BKProduct == ProductInput).Any());
Alternative 2:
var customerData = db.Customer
.Where(p => p.Transactions.SelectMany(c => c.Purchases).Select(gc => gc.Product.Where(gc => gc.BKProduct == ProductInput).Any())
.Include(c=>c.CustomerType)
.Include(c=>c.CustomerAddress)
In this specific case, it probably does not matter.
Since c# "Include" is about the SELECT and the JOIN of the generated sql.
However, you do not want to use the "it does not matter" as a blanket statement.
See here answer below (and overall question and other answers).
Does the order of LINQ functions matter?
When you start putting in things like Where and OrderBy, the order-of-the-operations can matter.
Always look at the generated sql and ask yourself "does it look reasonable"? (Which you already did from your question :) ..I mention primarily this for future readers)
So in this specific case, it is a preference. I typically put .Where
last. So your first example would match my personal preference.
And for some "further investigation", check out something like : https://weblogs.asp.net/dixin/introducing-linq-3-waht-is-functional-programming

LINQ - Left Join converting RIGHT results to List

I'm struggling to find the correct combination of LINQ Methods to perform a multi-table left join with a one to many mapping that makes a list along with the grouped results.
Current Status
I have a Plan table, joined with other tables to get the columns I need to get my list of plans.
var plans = await (
from ubp in db.ViewUserBusinessPlan
join bp in db.ViewBusinessPlan on ubp.BusinessPlanId equals bp.BusinessPlanId
where bp.BusinessId == businessId
select new
{
ubp.UserBusinessPlanId,
ubp.BusinessPlanId,
bp.Name,
bp.PlanGroup,
bp.BusinessId,
ubp.BusinessLocationId,
ubp.StripeSubscriptionId,
ubp.UserId,
ubp.BusinessPlanPriceCents
}
Problem:
Now I need to ultimately get the applicable Tax Rates that are associated with those Plans. This is stored in 2 tables.
UserBusinessPlanTaxRates - (a mapping table) that contains UserBusinessPlanId and TaxRateId.
TaxRates - TaxRate Information with TaxRateId as PK
Some plans have tax rates, some do not, so need a LEFT JOIN type scenario. Also, some plans can have multiple tax rates so I need a list of TaxRates. I've tried various Group methods, subqueries, and left joins. But nothing seems to put them all together.
I want to get all plans, with a list of TaxRates.
You can add a sub-query in the select to do the necessary join:
TaxRates = db.UserBusinessPlanTaxRates.Where(ubptr => ubptr.UserBusinessPlanId == ubp.UserBusinessPlanId)
.GroupJoin(db.TaxRates, ubptr => ubptr.TaxRateId, tr => tr.TaxRateId, (ubptr, trj) => trj)
.SelectMany(trj => trj)
.ToList()
Whether it will translate properly (or optimally) to SQL depends on what LINQ to database you are using.

LINQ left-join Entity Frame work - Unable to create a constant value of type

There are simular questions with answers that do not work in my situation.
I'm getting a
Unable to create a constant value of type '.Model.featureoptions'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Using Entity First, EntityFramework 4.1, MVC3, C# 4.
vehicles is a table of vehicle details, owners is a table of vehicle owners. vehicles and owners are inner joined and that works.
features table is a list of optional features e.g. sunroof, paint, etc. featureOptions is a list of the available options for a feature. e.g. paint could be 'pearlescent', 'matalic' and sunroof could be 'glass pop-up', 'title + slide'.
vehicleFeatures is a list of chosen options for a vehicle, for a particulare feature a vehicle can have zero or one record.
In this query feature1 should be null or the chosen value for a feature (i.e. the chosen sunroof option) and feature2 should be null or the chosen value for a different feature (i.e. the chosen paint option)
var query = (from v in _entities.vehicles
join o
in _entities.owners
on v.OwnerID equals o.OwnerID
// Some more inner joins
select new
{
// <code snipped >
// o. fields and v. fields
// </ code snipped>
feature1 = (from feature1
in _entities.vehiclefeatures
.Where ( f_1 => f_1.VehicleID == v.VehicleID)
join feature1_fo
in _entities.featureoptions
on feature1.FeatureOptionID equals feature1_fo.FeatureOptionID
join feature1_f
in _entities.features
.Where (bt_f => bt_f.CodeEnum==1)
on feature1_fo.FeatureID equals feature1_f.FeatureID
select new featureoptionsDTO () { Option = feature1_fo.Option }
),
feature2 = (from feature2
in _entities.vehiclefeatures
.Where(f_2 => f_2.VehicleID == v.VehicleID)
join feature2_fo
in _entities.featureoptions
on feature2.FeatureOptionID equals feature2_fo.FeatureOptionID
join feature2_f
in _entities.features
.Where(feature2_f => feature2_f.CodeEnum == 2)
on feature2_fo.FeatureID equals feature2_f.FeatureID
select new featureoptionsDTO() { Option = feature2_fo.Option }
)
}
);
foreach (var vehicle in query) // Exception here
{
}
the
feature1 = (from ..
and
feature2 = (from ..
are causing the
Unable to create a constant value of type '.Model.featureoptions'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
I understand that LINQ is trying to create an entity, how can I get it to create an anonymous (or own class) instead?
Unfortunately Entity Framework can't handle select clauses which construct arbitrary types in LINQ to Entities queries. I've tripped over this one a few times myself, and it's quite annoying. It is, however, quite necessary as LINQ to Entities queries are translated into SQL to run on the database, and the database can't handle the construction of .NET objects. It might be nice to be able to do it at the end of the query, but it can certainly never be allowed in the middle.
What I tend to do is write a query which produces exactly the input required to the constructors all in LINQ to Entities, so that it runs on the database. Then call ToEnumerable() on the IQueryable you get from that, which turns it into an IEnumerable, and after that you're in LINQ to Objects so you can do whatever you like in your Select().
I resolved this by using a view in the database to do the outer joins and the linq queries the entity assciated with the view.
By doing the left outer in the database means the outer joins are done earlier, possibly making it a bit quicker. The linq is tidier and just has to do what it needs to, which in this case is filtering.

How do I apply the LINQ to SQL Distinct() operator to a List<T>?

I have a serious(it's getting me crazy) problem with LINQ to SQL. I am developing an ASP.NET MVC3 application using c# and Razor in Visual Studio 2010.
I have two database tables, Product and Categories:
Product(Prod_Id[primary key], other attributes)
Categories((Dept_Id, Prod_Id) [primary keys], other attributes)
Obviously Prod_Id in Categories is a foreign key. Both classes are mapped using the Entity Framework (EF). I do not mention the context of the application for simplicity.
In Categories there are multiple rows containing the Prod_Id. I want to make a projection of all Distinct Prod_Id in Categories. I did it using plain (T)SQL in SQL Server MGMT Studio according to this (really simple) query:
SELECT DISTINCT Prod_Id
FROM Categories
and the result is correct. Now I need to make this query in my application so I used:
var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();
I go to check the result of my query by using:
query.Select(m => m.Prod_Id);
or
foreach(var item in query)
{
item.Prod_Id;
//other instructions
}
and it does not work. First of all the Intellisense when I attempt to write query.Select(m => m. or item.shows just suggestions about methods (such as Equals, etc...) and not properties. I thought that maybe there was something wrong with Intellisense (I guess most of you many times hoped that Intellisense was wrong :-D) but when I launch the application I receive an error at runtime.
Before giving your answer keep in mind that;
I checked many forums, I tried the normal LINQ to SQL (without using lambdas) but it does not work. The fact that it works in (T)SQL means that there is something wrong with the LINQ to SQL instruction (other queries in my application work perfectly).
For application related reasons, I used a List<T> variable instead of _StoreDB.Categories and I thought that was the problem. If you can offer me a solution without using a List<T> is appreciated as well.
This line:
var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();
Your LINQ query most likely returns IEnumerable... of ints (judging by Select(m => m.Prod_Id)). You have list of integers, not list of entity objects. Try to print them and see what you got.
Calling _StoreDB.Categories.Select(m => m.Prod_Id) means that query will contain Prod_Id values only, not the entire entity. It would be roughly equivalent to this SQL, which selects only one column (instead of the entire row):
SELECT Prod_Id FROM Categories;
So when you iterate through query using foreach (var item in query), the type of item is probably int (or whatever your Prod_Id column is), not your entity. That's why Intellisense doesn't show the entity properties that you expect when you type "item."...
If you want all of the columns in Categories to be included in query, you don't even need to use .Select(m => m). You can just do this:
var query = _StoreDB.Categories.Distinct();
Note that if you don't explicitly pass an IEqualityComparer<T> to Distinct(), EqualityComparer<T>.Default will be used (which may or may not behave the way you want it to, depending on the type of T, whether or not it implements System.IEquatable<T>, etc.).
For more info on getting Distinct to work in situations similar to yours, take a look at this question or this question and the related discussions.
As has been explained by the other answers, the error that the OP ran into was because the result of his code was a collection of ints, not a collection of Categories.
What hasn't been answered was his question about how to use the collection of ints in a join or something in order to get at some useful data. I will attempt to do that here.
Now, I'm not really sure why the OP wanted to get a distinct list of Prod_Ids from Categories, rather than just getting the Prod_Ids from Projects. Perhaps he wanted to find out what Products are related to one or more Categories, thus any uncategorized Products would be excluded from the results. I'll assume this is the case and that the desired result is a collection of distinct Products that have associated Categories. I'll first answer the question about what to do with the Prod_Ids first, and then offer some alternatives.
We can take the collection of Prod_Ids exactly as they were created in the question as a query:
var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();
Then we would use join, like so:
var products = query.Join(_StoreDB.Products, id => id, p => p.Prod_Id,
(id,p) => p);
This takes the query, joins it with the Products table, specifies the keys to use, and finally says to return the Product entity from each matching set. Because we know that the Prod_Ids in query are unique (because of Distinct()) and the Prod_Ids in Products are unique (by definition because it is the primary key), we know that the results will be unique without having to call Distinct().
Now, the above will get the desired results, but it's definitely not the cleanest or simplest way to do it. If the Category entities are defined with a relational property that returns the related record from Products (which would likely be called Product), the simplest way to do what we're trying to do would be the following:
var products = _StoreDB.Categories.Select(c => c.Product).Distinct();
This gets the Product from each Category and returns a distinct collection of them.
If the Category entity doesn't have the Product relational property, then we can go back to using the Join function to get our Products.
var products = _StoreDB.Categories.Join(_StoreDB.Products, c => c.Prod_Id,
p => p.Prod_Id, (c,p) => p).Distinct();
Finally, if we aren't just wanting a simple collection of Products, then some more though would have to go into this and perhaps the simplest thing would be to handle that when iterating through the Products. Another example would be for getting a count for the number of Categories each Product belongs to. If that's the case, I would reverse the logic and start with Products, like so:
var productsWithCount = _StoreDB.Products.Select(p => new { Product = p,
NumberOfCategories = _StoreDB.Categories.Count(c => c.Prod_Id == p.Prod_Id)});
This would result in a collection of anonymous typed objects that reference the Product and the NumberOfCategories related to that Product. If we still needed to exclude any uncatorized Products, we could append .Where(r => r.NumberOfCategories > 0) before the semicolon. Of course, if the Product entity is defined with a relational property for the related Categories, you wouldn't need this because you could just take any Product and do the following:
int NumberOfCategories = product.Categories.Count();
Anyway, sorry for rambling on. I hope this proves helpful to anyone else that runs into a similar issue. ;)

Categories