Simplifying a Linq query by using Include instead of Join - c#

I've been asked to simplify the following Linq query:
var orders = db.Orders
.Join(db.Shipments,
o => o.OrderID,
s => s.OrderID,
(o, s) => new { Order = o, Shipment = s })
.Join(db.LineItems,
s => s.Shipment.ShipmentID,
l => l.ShipmentID,
(s, l) => new { Order = s.Order, Shipment = s.Shipment, LineItem = l })
.Join(db.StatusTypes,
s => s.Shipment.StatusTypeID,
st => st.StatusTypeID,
(s, st) => new { Order = s.Order, Shipment = s.Shipment, LineItem = s.LineItem, Description = st.ExternalDescription })
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Order.Created),
OrderNumber = x.Key,
Cost = x.Sum(y => y.LineItem.UnitPrice).ToString(),
Status = x.Max(y => y.Description)
}
);
By replacing the Joins with Includes. I've searched around, and I've discovered that Joins and Includes are somewhat equivalent. But I can't figure out how to convert this query to one that uses includes instead of joins. Is it actually less code and simpler to use includes instead of joins here?

I strongly suggest you to use navigation properties instead of manual builded joins. It will be more efficent and controlable. Read this article.
If you would convert your query to navigation property form by using Include, it would seem like that;
var orders = db.Orders
.Include(x => x.Shipments)
.Include(x => x.Shipments.Select(y => y.LineItems))
.Include(x => x.Shipments.Select(y => y.StatusType))
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Created),
OrderNumber = x.Key,
Cost = x.LineItems.Sum(k => k.UnitPrice),
Status = x.Max(y => y.Description)
}
);
But, as I said, you should define the navigation properties for entities first.

var orders = db.Orders
.Include("Shipments")
.Include("Shipments.LineItems")
.Include("Shipments.StatusTypes")
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Order.Created),
OrderNumber = x.Key,
Cost = x.Sum(y => y.LineItem.UnitPrice).ToString(),
Status = x.Max(y => y.Description)
}
);
i not tested above code ,just try
ObjectQuery.Include Method (String)

Related

Evaluate locally ToDictionary after GroupBy

I have the following Entity Framework Core query:
var result = await products
.Select(x => new {
PackageId = x.PackageId,
Price = x.Price
})
.GroupBy(x => x.PackageId)
.Select(x => new {
PackageId = x.Key,
TotalPrice = x.Sum(y => y.Price)
})
.ToDictionaryAsync(x => x.PackageId, x => new Model {
TotalPrice = x.TotalPrice
});
I was able to make this query evaluate on the server.
Is there a way to simplify it and still make sure it evaluates on the server?
Try this.
var result = await products
.GroupBy(x => x.PackageId)
.Select(x => new {
PackageId = x.Key,
TotalPrice = x.Sum(y => y.Price)
})
.ToDictionaryAsync(x => x.PackageId, x => new Model {
TotalPrice = x.TotalPrice
});
EF will take care about not to load unnecessary data.

C# Linq Lambda for group by multiple columns select max

I want to translate this into lambda syntax and can't seem to get it to work:
Grouping by two columns, select max on a different column, return list of complete complex object.
I am writing more text here to get past the validation on this form. How much text is needed until I am allowed to post this?
_clientpolicies = (from policy in
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
group policy by
new
{
PolicyReference = GetPolicyReference(policy),
PolicyType = policy.ProductInformation.PolicyTypeCode
}
into g
let maxPolicyInception = g.Max(p => p.InceptionDate)
from policyGroup in g
where policyGroup.InceptionDate == maxPolicyInception
select policyGroup).ToList();
I dont think there's a way of doing it in one line. So there's my try :
policyGroups=
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm
.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
.GroupBy(x => GetPolicyReference(x))
.ThenBy(x => x.ProductInformation.PolicyTypeCode)
.ToList();
var maxPolicyInception = policyGroups.Max(p => p.InceptionDate);
_clientpolicies = policyGroups
.Where(g => g.InceptionDate == maxPolicyInception)
.ToList();
_clientpolicies =
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
.GroupBy(x =>
new
{
PolicyReference = GetPolicyReference(x),
PolicyType = x.ProductInformation.PolicyTypeCode
},
(key, g) => g.OrderByDescending(gx => gx.InceptionDate).First()

Comparing list with list inside another list with linq [duplicate]

I am getting this error for the query below
Unable to create a constant value of type API.Models.PersonProtocol. Only primitive types or enumeration types are supported in this context
ppCombined below is an IEnumerable object of PersonProtocolType, which is constructed by concat of 2 PersonProtocol lists.
Why is this failing? Can't we use LINQ JOIN clause inside of SELECT of a JOIN?
var persons = db.Favorites
.Where(x => x.userId == userId)
.Join(db.Person, x => x.personId, y => y.personId, (x, y) =>
new PersonDTO
{
personId = y.personId,
addressId = y.addressId,
favoriteId = x.favoriteId,
personProtocol = (ICollection<PersonProtocol>) ppCombined
.Where(a => a.personId == x.personId)
.Select( b => new PersonProtocol()
{
personProtocolId = b.personProtocolId,
activateDt = b.activateDt,
personId = b.personId
})
});
This cannot work because ppCombined is a collection of objects in memory and you cannot join a set of data in the database with another set of data that is in memory. You can try instead to extract the filtered items personProtocol of the ppCombined collection in memory after you have retrieved the other properties from the database:
var persons = db.Favorites
.Where(f => f.userId == userId)
.Join(db.Person, f => f.personId, p => p.personId, (f, p) =>
new // anonymous object
{
personId = p.personId,
addressId = p.addressId,
favoriteId = f.favoriteId,
})
.AsEnumerable() // database query ends here, the rest is a query in memory
.Select(x =>
new PersonDTO
{
personId = x.personId,
addressId = x.addressId,
favoriteId = x.favoriteId,
personProtocol = ppCombined
.Where(p => p.personId == x.personId)
.Select(p => new PersonProtocol
{
personProtocolId = p.personProtocolId,
activateDt = p.activateDt,
personId = p.personId
})
.ToList()
});
In my case, I was able to resolve the issue by doing the following:
I changed my code from this:
var r2 = db.Instances.Where(x => x.Player1 == inputViewModel.InstanceList.FirstOrDefault().Player2 && x.Player2 == inputViewModel.InstanceList.FirstOrDefault().Player1).ToList();
To this:
var p1 = inputViewModel.InstanceList.FirstOrDefault().Player1;
var p2 = inputViewModel.InstanceList.FirstOrDefault().Player2;
var r1 = db.Instances.Where(x => x.Player1 == p1 && x.Player2 == p2).ToList();
Don't know if anyone searches for this.
I had the same problem. A select on the query and then doing the where (or join) and using the select variable solved the problem for me.
(problem was in the collection "Reintegraties" for me)
query.Select(zv => new
{
zv,
rId = zv.this.Reintegraties.FirstOrDefault().Id
})
.Where(x => !db.Taken.Any(t => t.HoortBijEntiteitId == x.rId
&& t.HoortBijEntiteitType == EntiteitType.Reintegratie
&& t.Type == TaakType))
.Select(x => x.zv);
hope this helps anyone.
I had this issue and what I did and solved the problem was that I used AsEnumerable() just before my Join clause.
here is my query:
List<AccountViewModel> selectedAccounts;
using (ctx = SmallContext.GetInstance()) {
var data = ctx.Transactions.
Include(x => x.Source).
Include(x => x.Relation).
AsEnumerable().
Join(selectedAccounts, x => x.Source.Id, y => y.Id, (x, y) => x).
GroupBy(x => new { Id = x.Relation.Id, Name = x.Relation.Name }).
ToList();
}
I was wondering why this issue happens, and now I think It is because after you make a query via LINQ, the result will be in memory and not loaded into objects, I don't know what that state is but they are in in some transitional state I think. Then when you use AsEnumerable() or ToList(), etc, you are placing them into physical memory objects and the issue is resolving.
It's worth adding, since the OP's code sample doesn't provide enough context to prove otherwise, but I received this error as well on the following code:
public RetailSale GetByRefersToRetailSaleId(Int32 refersToRetailSaleId)
{
return GetQueryable()
.FirstOrDefault(x => x.RefersToRetailSaleId.Equals(refersToRetailSaleId));
}
Apparently, I cannot use Int32.Equals in this context to compare an Int32 with a primitive int; I had to (safely) change to this:
public RetailSale GetByRefersToRetailSaleId(Int32 refersToRetailSaleId)
{
return GetQueryable()
.FirstOrDefault(x => x.RefersToRetailSaleId == refersToRetailSaleId);
}
Just add AsEnumerable() andToList() , so it looks like this
db.Favorites
.Where(x => x.userId == userId)
.Join(db.Person, x => x.personId, y => y.personId, (x, y).ToList().AsEnumerable()
ToList().AsEnumerable()

Grouping earliest entry for each day using Linq

Trying to get my head around Linq, and at the same time keep track of the time I log on in the morning, which should be the time I get into the office thereabouts.
My code so far is:
EventLog SecurityLog = new EventLog("Security");
var AccountLoggedOnEntries = SecurityLog.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.Select(x => new
{
DateGenerated = x.TimeGenerated.ToShortDateString()
,
TimeGenerated = x.TimeGenerated.ToShortTimeString()
,
x.Message
})
.ToList();
DgvLogSummary.DataSource = AccountLoggedOnEntries;
DgvLogSummary.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
I want to filter the results so that I only have one entry for each day, which is the earliest time.
In SQL I would normally take the Message of the earliest entry and then group by all fields.
How do I perform a similar query in Linq?
In LINQ you would group by, sort each group, and pick the first item:
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(g => g.OrderBy(x => x.TimeGenerated).First())
.Select(x => new {
DateGenerated = x.TimeGenerated.ToShortDateString()
, TimeGenerated = x.TimeGenerated.ToShortTimeString()
, x.Message
})
.ToList();
You could GroupBy the date and then select the minimum time
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(x => new {
DateGenerated = x.Key
, TimeGenerated = x.Min(y => y.TimeGenerated).ToShortTimeString()
})
.ToList();
Getting the appropriate Message is a little more tricky. One easy option is to use x.First().Message in the above Select projection.
Try this :
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(days => days.OrderBy(time => time.TimeGenerated).FirstOrDefault())
.Select(x => new
{
DateGenerated = x.TimeGenerated.ToShortDateString()
,
TimeGenerated = x.TimeGenerated.ToShortTimeString()
,
x.Message
})
.ToList();

Linq nhibernate groups and joins

Consider a simple order example (should get the title and quantity ordered for each product):
(note - grouping by Title is not a good answer)
var orderQuery =
session.Query<OrderLine>()
.Where(ol => ol.Quantity > 0)
.GroupBy(ol => ol.Product.Id)
.Select(x => new
{
productId = x.Key,
quantity = x.Sum(i => i.Quantity)
});
var query =
session.Query<Product>()
.Join(orderQuery,
x => x.Id,
x => x.productId,
(x, p) => new { x.FeedItem.Title, p.quantity });
However, this throws a
could not resolve property: Key of: OrderLine
any ideas?
Try to create your query using QueryOver and Projections
Sample

Categories

Resources