Entity Framework, Linq : concatenating results from child table - entity-framework

I have an existing linq query which gets some data into a view model object. This is working fine.
I want to add a new property for data from child table which will have column values from a child table in a comma separated string format.
Problem: I am not able to concatenate the results using string.join
Simplified version of tables showing only relevant fields
part
id
part number
1
ABC1
2
DEF1
vendor
id
vendorname
1
acme
2
john
vendor part name (vendor specific part number)
partid
vendorid
partname
1
1
GDSE-553-32
1
2
JWWVV-HH-01
simplified version of query
result = (from p in DBContext.Parts.Where(w => w.EquipmentId == eId)
select new PartModel
{
Id = p.Id,
Number = p.PartNumber,
VendorPartNames= String.Join(",", DBContext.VendorPartName.Where(w => w.PartId == p.Id).Select(s => s.PartName))//this line causes exception (shown below)
});
Exception:
LINQ to Entities does not recognize the method 'System.String Join(System.String, System.String[])' method, and this method cannot be translated into a store expression.
Please note: the actual query has some joins and other columns, so please dont suggest solutions that requires joins.
If I change the "VendorPartName" to a List type , I can get the results without any problems.
My only problem is in "How to convert the results for "VendorPartName" property to a comma separated strings?"
eg: based on sample table data provided, it should be
GDSE-553-32, JWWVV-HH-01

Entity Framework does not support String.Join() method.
So, what we can do is to fetch VendorPartNames as a string collection and then we can later separate it with ,.
Note: For this, we would first use an anonymous object and later convert it to PartModel.
So your query would look like this:
var parts = DBContext.Parts
.Where(w => w.EquipmentId == eId)
.Select(p => new {
Id = p.Id,
Number = p.PartNumber,
VendorPartNames = p.VendorPartName.Select(n => n.PartName)
}).ToList();
var result = parts.Select(i => new PartModel {
Id = i.Id,
Number = i.Number,
VendorPartNames = String.Join(",", i.VendorPartNames)
}).ToList();

Related

Linq with Included Subquery is failing on EF Core

I am using MySQL DB with ef core all works fine but the following query does nto return expected result.
var query = _context.ServiceData.
Include(x => x.Country)
.Include(x=>x.Country.CountryLocale)
.Where(x=>x.Country.CountryLocale.Any(l=>l.Locale == "en-US"));
After executing and doing
query.First().Country.CountryLocale.Count // Returns count of greater than 1 when expected count is 1
The table only has 2 entries and the above pulls both when only 1 is expected.
Table Layout
Id Name Locale
1 Test en-US
2 Test en-GB
All examples I have seen seem to suggest doing it how I am doing it so not sure what I am missing.
Here is the sql generated by above Linq
SELECT `a`.`Id`, `a`.`ServiceDataCode`, `a`.`CountryId`, `a`.`Enabled`, `a`.`LastUpdated`, `a`.`TimezoneId`, `c`.`Id`, `c`.`DialingCode`, `c`.`Enabled`, `c`.`IsoNumeric`, `c`.`IsoThreeLetterCode`, `c`.`IsoTwoLetterCode`, `c`.`LastUpdated`, `c0`.`Id`, `c0`.`IsoTwoLetterCode`, `c0`.`LastUpdated`, `c0`.`Locale`, `c0`.`Name`
FROM `ServiceData` AS `a`
INNER JOIN `CountryData` AS `c` ON `a`.`CountryId` = `c`.`Id`
LEFT JOIN `CountryLocale` AS `c0` ON `c`.`IsoTwoLetterCode` = `c0`.`IsoTwoLetterCode`
WHERE EXISTS (
SELECT 1
FROM `CountryLocale` AS `c1`
WHERE (`c`.`IsoTwoLetterCode` = `c1`.`IsoTwoLetterCode`) AND (`c1`.`Locale` = 'en-US'))
ORDER BY `a`.`Id`, `c`.`Id`, `c0`.`Id`
I guess a stored procedure is another option but wanted to do it without.
Well, here is an object query with a right paths of country and specific country locale that belong to first service data:
using (var query = _context.ServiceData
.Include("Country.CountryLocale")
.Where(x => x.ServiceData.CountryId == x.Country.Id)
.Where(x => x.Country.IsoTwoLetterCode == x.Country.CountryLocale.IsoTwoLetterCode)
.Where(x => x.Country.CountryLocale.Locale == "en-US"))
{
query.FirstOrDefault();
}
Hope this helps.

Selecting Subcollections in Union-Query

I'm trying to select objects from the database with Entity Framework into an anonymous type. When using Union and Selecting a Subcollection, I get an exception:
System.ArgumentException: The 'Distinct' operation cannot be applied to the collection ResultType of the specified argument.
My model contains several types derived from BaseType. This base type has a reference to RefType which contains a collection of ItemType. The types derived from BaseType are stored in separate tables, thus the Union.
The query looks like this:
var q1 = ctx.Set<Type1>().Select(x => new { x.Id, x.Ref.Items });
var q2 = ctx.Set<Type2>().Select(x => new { x.Id, x.Ref.Items });
q1.Union(q2).ToList();
But to reproduce the error, you can even union queries of the same type, as long as you select a collection.
I would do the select after the union, but to union Type1, Type2, etc. I must cast them to BaseType, which is not allowed in LINQ-to-SQL.
Any way to do this in the same query?
The exception emerges from Entity Framework's query generation pipeline when the ExpressionConverter tries to translate the expression q1.Union(q2) into SQL.
In a valid query you'll see that EF adds a DISTINCT clause to the SQL query. A type with collection properties (x.Ref.Items) doesn't pass as a valid argument for a Distinct operation and EF throws the exception you see.
Unfortunately, using Concat instead of Union is not a valid work-around. EF will also throw an exception:
The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'
Which means that it's simply not supported to concat nested queries containing types with collection properties.
So you have to do the Union in memory:
var result = q1.AsEnumerable() // Continues the query in memory
.Union(q2).ToList();
C# doesn't have any problem with equating anonymous types containing collections: it simply considers any collection member as unequal to another collections member. This does mean that the query can produce a collection containing non-unique results (same Id, same Items) which may not be expected when relying on Union's implicit Distinct.
I am not sure why, for some reason distinct is failing, maybe because it is anonymous type, and it is still IQuerable, I would suggest firing the query something like this
var q1 = ctx.Set<Type1>().Select(x => new { x.Id, x.Ref.Items }).ToList<object>();
var q2 = ctx.Set<Type2>().Select(x => new { x.Id, x.Ref.Items }).ToList<object>();
q1.Union(q2).ToList();
Note that in this case, Distinct will check for all properties equality, meaning if 2 objects have the same id but different items, both will be there.
if you don't care about distinct values, you can also use concat
if you care about distinct and first option didn't work, you can use group by and implement your own distinct,
something like this
var q1 = ctx.Set<Type1>().Select(x => new { Id = x.Id, Items =x.Ref.Items });
var q2 = ctx.Set<Type2>().Select(x => new { Id = x.Id, Items = x.Ref.Items });
//this will group by id, and select the first object items
var qFinal = q1.concat(q2).GroupBy(e => e.id)
.select(e => new {e.key, e.First().Items})
.ToList();
maybe you don't want First(), you can use whatever you want

Not able to use IN query in LINQ with Entity Framework

I am using EF Framework to retrieve the data from SQL DB.
Sub Request Table looks like below:
In this table "org_assigneddept" is foreign key to another Department Table.
I have list of Departments as Input and I want to retrieve only those rows from DB whose org_assigneddept is matching the list.
Please find my whole code:-
private List<EventRequestDetailsViewModel> GetSummaryAssignedDeptEventRequests(List<EmpRoleDeptViewModel> vmDept)
{
List<EventRequestDetailsViewModel> vmEventRequestDeptSummary = new List<EventRequestDetailsViewModel>();
RequestBLL getRequestBLL = new RequestBLL();
Guid subRequestStatusId = getRequestBLL.GetRequestStatusId("Open");
using (var ctxGetEventRequestSumm = new STREAM_EMPLOYEEDBEntities())
{
vmEventRequestDeptSummary = (from ers in ctxGetEventRequestSumm.SubRequests
where vmDept.Any(dep=>dep.DeptId == ers.org_assigneddept)
select new EventRequestDetailsViewModel
{
SubRequestId = ers.org_subreqid
}).ToList();
}
}
It is giving the following error at the LINQ Query level:-
System.NotSupportedException: 'Unable to create a constant value of
type 'Application.Business.DLL.EmpRoleDeptViewModel'. Only primitive
types or enumeration types are supported in this context.'
Please let me know as how can I achieve the result
You cannot pass the department VMs to SQL, it doesn't know what those are.
// Extract the IDs from the view models.. Now a list of primitive types..
var departmentIds = vmDept.Select(x => x.DeptId).ToList();
then in your select statement...
..
where departmentIds.Contains(id=> id == ers.org_assigneddept)
..

Entity Framework: selecting from multiple tables

I have a statement:
var items = from e in db.Elements
join a in db.LookUp
on e.ID equals a.ElementID
where e.Something == something
select new Element
{
ID = e.ID,
LookUpID = a.ID
// some other data get populated here as well
};
As you can see, all I need is a collection of Element objects with data from both tables - Elements and LookUp. This works fine. But then I need to know the number of elements selected:
int count = items.Count();
... this call throws System.NotSupportedException:
"The entity or complex type 'Database.Element' cannot be constructed in a LINQ to Entities query."
How am I supposed to select values from multiple tables into one object in Entity Framework? Thanks for any help!
You are not allowed to create an Entity class in your projection, you have to either project to a new class or an anonymous type
select new
{
ID = e.ID,
LookUpID = a.ID
// some other data get populated here as well
};
Your code doesn't work at all. The part you think worked has never been executed. The first time you executed it was when you called Count.
As exception says you cannot construct mapped entity in projection. Projection can be made only to anonymous or non mapped types. Also it is not clear why you even need this. If your class is correctly mapped you should simply call:
var items = from e in db.Elements
where e.Something == something
select e;
If LookupID is mapped property of your Element class it will be filled. If it is not mapped property you will not be able to load it with single query to Element.

Entity Framework limit length of a returned nvarchar column

I want to limit the length of a column in an EF query, ala:
var query = from ce in entities.ContactEvents
.Include("Person")
.Include("Orders")
where ce.PersonID = personID
orderby ce.DateTimeContact descending
select new ContactEvent
{
ID = ce.ID,
DateTimeContact = ce.DateTimeContact,
Description = ce.Description.Substring(0, 500),
Orders = ce.Orders
};
The query fails because the EF can't project the complex type Orders.
The entity or complex type 'Model.ContactEvent' cannot be constructed in a LINQ to Entities query.
I've tried a few different ways to do the same thing such as use an explicit join in the LINQ expression but so far I always hit a snag populating the Orders collection in the select projection.
Any ideas on how I can construct my query? Ideally I don't even want to use a select projection but I'm assuming I need to in order to be able to limit the length of the description column returned from the database.
You cannot project to entity types. That is the limitation. If you want to return projection (calling select new) you must either return anonymous type or custom non entity type. If you want to return entity type you must always return whole column from linq-to-entities. You can try to trim the column after object is materialized by using:
var data = (from ce in entities.ContactEvents
.Include("Person")
.Include("Orders")
where ce.PersonID = personID
orderby ce.DateTimeContact descending
select ce)
.AsEnumerable()
.Select(e => new ContactEvent
{
ID = e.ID,
DateTimeContact = e.DateTimeContact,
Description = e.Description.Substring(0, 500),
Orders = e.Orders
});