How can I use be generic to result of linq query? - linq

Be first, my English is not very good. So I am sorry about that. :)
My question as the title. I have two tables on my database:
Siniflar (SinifId, SinifAdi, Kapasite, OgretmenId)
Ogretmenler (OgretmenId, Ad, Soyad, Brans)
Query:
var siniflar = (from s in db.Siniflar
join o in db.Ogretmenler
on s.OgretmenId equals o.OgretmenId
select new { s.SinifId, s.SinifAdi, s.Kapasite, o.Ad }).ToList();
I want to use the result of the query as a generic, like this:
public List<Siniflar> SiniflariListele()
{
var siniflar = (from s in db.Siniflar
join o in db.Ogretmenler
on s.OgretmenId equals o.OgretmenId
select new { s.SinifId, s.SinifAdi, s.Kapasite, o.Ad }).ToList();
return siniflar;
}
But I get an error. Because the result is anonymous types.
Error: Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Collections.Generic.List<Entity.Siniflar>
How Can I use be generic result. What Can I do for that?
I changed question:
Class:
public class RSiniflar
{
public int SinifId { get; set; }
public string SinifAdi { get; set; }
public int Kapasite { get; set; }
public string OgretmenAdiSoyadi { get; set; }
}
Metod:
public List<RSiniflar> SiniflariListele()
{
List<RSiniflar> siniflar = (from s in db.Siniflar
join o in db.Ogretmenler
on s.OgretmenId equals o.OgretmenId
select new RSiniflar
{
SinifId = s.SinifId,
SinifAdi = s.SinifAdi,
Kapasite = s.Kapasite,
OgretmenAdiSoyadi = o.Ad + ' ' + o.Soyad
}).ToList();
return siniflar;
}
But now error to query: Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context.
I think, there are a problem on my query... I get an error same when query to var type...

You can return typed list, but you can't return a list of anonymous type. So you need to create a class which will handle your results:
public class A
{
public int SinifId { get; set; }
public int SiniAdi { get; set; }
public int Kapasite { get; set; }
public int Ad { get; set; }
}
And then use it in your query, instead of anonymous objects:
public List<A> SiniflariListele()
{
var siniflar = (from s in db.Siniflar
join o in db.Ogretmenler
on s.OgretmenId equals o.OgretmenId
select new A {
SinifId = s.SinifId,
SinifAdi = s.SinifAdi,
Kapasite = s.Kapasite,
Ad = o.Ad
}).ToList();
return siniflar;
}
You could do the same with an existing class (e.g. Siniflar), but you have to be sure that this class has all necessary properties.

Rather than using new { property1, property2 }, create new Siniflar's in your select.
E.g.
select new Siniflar() {
SinifId = s.SinifId,
SinifAdi = s.SinifAdi,
Kapasite = s.Kapasite }).ToList()
Note: as lazyberezovsky mentioned, your mapping seems to be flawed, as Siniflar does not contain the property Ad.

Related

EF Core - many queries sent to database for subquery

Using EF Core 2.2.2, I have a table in my database which is used to store notes for many other tables. In other words, it's sortof like a detail table in a master-detail relationship, but with multiple master tables. Consider this simplified EF Model:
public class Person
{
public Guid PersonID { get; set; }
public string Name { set; set; }
}
public class InvoiceItem
{
public Guid InvoiceItemID { get; set; }
public Guid InvoiceID { get; set; }
public string Description { get; set; }
}
public class Invoice
{
public Guid InvoiceID { get; set; }
public int InvoiceNumber { get; set; }
public List<Item> Items { get; set; }
}
public class Notes
{
public Guid NoteID { get; set; }
public Guid NoteParentID { get; set; }
public DateTime NoteDate { get; set; }
public string Note { get; set; }
}
In this case, Notes can store Person notes or Invoice notes (or InvoiceItem notes, though let's just say that the UI doesn't support that).
I have query methods set up like this:
public IQueryable<PersonDTO> GetPersonQuery()
{
return from p in Context.People
select new PersonDTO
{
PersonID = p.PersonID,
Name = p.Name
};
}
public List<PersonDTO> GetPeople()
{
return (from p in GetPersonQuery()
return p).ToList();
}
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber
};
}
public List<InvoiceDTO> GetInvoices()
{
return (from i in GetInvoiceQuery()
return i).ToList();
}
These all work as expected. Now, let's say I add InvoiceItems to the Invoice query, like this:
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber,
Items = (from ii in p.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList()
};
}
That also works great, and issues just a couple queries. However, the following:
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber,
Items = (from ii in p.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList(),
Notes = (from n in Context.Notes
where i.InvoiceID = n.NoteParentID
select new NoteDTO
{
NoteID = n.NoteID,
Note = n.Note
}).ToList(),
};
}
sends a separate query to the Note table for each Invoice row in the Invoice table. So, if there are 1,000 invoices in the Invoice table, this is sending something like 1,001 queries to the database.
It appears that the Items subquery does not have the same issue because there is an explicit relationship between Invoices and Items, whereas there isn't a specific relationship between Invoices and Notes (because not all notes are related to invoices).
Is there a way to rewrite that final query, such that it will not send a separate note query for every invoice in the table?
The problem is indeed the correlated subquery versus collection navigation property. EF Core query translator still has issues processing such subqueries, which are in fact logical collection navigation properties and should have been processed in a similar fashion.
Interestingly, simulating collection navigation property with intermediate projection (let operator in LINQ query syntax) seems to fix the issue:
var query =
from i in Context.Invoices
let i_Notes = Context.Notes.Where(n => i.InvoiceID == n.NoteParentID) // <--
select new InvoiceDTO
{
InvoiceID = i.InvoiceID,
InvoiceNumber = i.InvoiceNumber,
Items = (from ii in i.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList(),
Notes = (from n in i_Notes // <--
select new NoteDTO
{
NoteID = n.NoteID,
Note = n.Note
}).ToList(),
};

How to convert LINQ query result to list and return generic List?

I used generic ORM list
example.
This is my table.
Person table
Guid
Name
LastName
and this is my struct class.
public struct PersonItem // database and class field names are the same
{
public Guid Guid{ get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
public struct PersonItems
{
public PersonItems()
{
Items = new List<PersonItem>();
}
public List<PersonItem> Items { get; set; }
}
I'm using such and no problem but I always have to write field's
public PersonItems GetPersons()
{
var query = (from p in _DbEntities.t_Crew
select p).ToList();
if (query != null)
{
foreach (var item in query)
{
_PersonItems.Items.Add(new PersonItem
{
Guid = item.Guid,
Name = item.Name,
LastName = item.LastName
});
}
}
return _PersonItems;
}
public PersonItems GetPersons()
{
PersonItems personItems = new PersonItems();
var query = from p in _DbEntities.t_Person
select p; >> this here query I need to convert linq query result to list
personItems = query.ToList();
return personItems ;
}
error
Cannot implicitly convert type System.Collections.Generic.List' to PersonData.PersonItems'.
Try this
public PersonItems GetPersons()
{
PersonItems personItems = new PersonItems();
var query = (from p in _DbEntities.t_Person
select new PersonItems
{
test = p.SomeName,
//Other Stuff
//...
}).ToList();
return query;
}
Comparing to the other version of your GetPersons() method, I think this line :
personItems = query.ToList();
should've been this way instead :
personItems.Items = query.ToList();
Update regarding the latest error. You can't assign list of t_Person to Item property which is of type list of PersonItem. Hence, your query need to be adjusted to return list of PersonItem :
var query = from p in _DbEntities.t_Person
select new PersonItem
{
Guid = p.Guid,
Name = p.Name,
LastName = p.LastName
};
or other option is to change definition of Item property to list of t_Person :
public List<t_Person> Items { get; set; }
Cannot implicitly convert type System.Collections.Generic.List' to PersonData.PersonItems'
Above error says that you are trying to convert generic list to PersonItems.
So at this line, query.ToList() code returns List<PersonItem> not the PersonItems
var query = from p in _DbEntities.t_Person
select p; >> this here query I need to convert linq query result to list
personItems = query.ToList();
return personItems ;
So thats the reason above line of code fails.
What about this?
Change .ctor of PersonItems:
public struct PersonItems
{
public PersonItems(List<PersonItem> items)
{
Items = items;
}
public List<PersonItem> Items { get; set; }
}
And then method GetPersons():
public PersonItems GetPersons()
{
return new PersonItems(_DbEntities.t_Person.ToList());
}

Get Count from entity framework

Quite new to EF, basically i want to convert this SQL query:
SELECT
PSKU.ProductSKUID,
PSKU.ProductSKUName,
W.WarehouseID,
W.WarehouseName,
SA.SystemAreaName,
COUNT(SLI.ProductSKUID) AS QTY
FROM dbo.StockLineItem AS SLI INNER JOIN
dbo.ProductSKU AS PSKU ON PSKU.ProductSKUID = SLI.ProductSKUID INNER JOIN
dbo.Warehouse AS W ON W.WarehouseID = SLI.WarehouseID INNER JOIN
dbo.SystemArea AS SA ON SA.SystemAreaID = SLI.SystemAreaID
WHERE (SA.SystemAreaID = 1)
AND W.WarehouseID = #WarehouseID
GROUP BY PSKU.ProductSKUID, PSKU.ProductSKUName, W.WarehouseName, SA.SystemAreaName, W.WarehouseID
To an effective EF statement. This is what i Have so far, my Model class and the method:
[Serializable]
public class StockReturnMethod
{
public int ProductSKUID { get; set; }
public int WarehouseID { get; set; }
public int LotID { get; set; }
public string LotName { get; set; }
public int AreaID { get; set; }
public string AreaName { get; set; }
public int BinID { get; set; }
public string BinName { get; set; }
}
public class DALStockMovement
{
scmEntitiesPrimaryCon entities = new scmEntitiesPrimaryCon();
public List<AvailibleStock> AvailibleStockQty(int warehouseID)
{
var rows = (from PLA in entities.ProductLocationAssignments
from W in entities.Warehouses
from SLI in entities.StockLineItems
from SA in entities.SystemAreas
from PSKU in entities.ProductSKUs
where W.WarehouseID == warehouseID
select new AvailibleStock() { WarehouseID = W.WarehouseID, ProductSKUID = PSKU.ProductSKUID, ProductSKUName = PSKU.ProductSKUName, WarehouseName = W.WarehouseName, Status = SA.SystemAreaName, QtyUnassigned = SLI.ProductSKUID }).ToList();
return rows;
}
Any Advice to get this to an Effective EF Statement would be appreciated
I actually used this tool called Linqer, since I had the SQL
I just popped it into that tool and it generated the Linq for me.
Here is what came out:
var SKUStock = (from sli in entities.StockLineItems
where
sli.SystemArea.SystemAreaID == 1 &&
sli.WarehouseID == warehouseID
group new { sli.ProductSKU, sli.Warehouse, sli.SystemArea, sli } by new
{
ProductSKUID = (System.Int32?)sli.ProductSKU.ProductSKUID,
sli.ProductSKU.ProductSKUName,
sli.Warehouse.WarehouseName,
sli.SystemArea.SystemAreaName,
WarehouseID = (System.Int32?)sli.Warehouse.WarehouseID
} into g
select new AvailibleStock()
{
ProductSKUID = (int)(System.Int32?)g.Key.ProductSKUID,
ProductSKUName = g.Key.ProductSKUName,
WarehouseID = (int)(System.Int32?)g.Key.WarehouseID,
WarehouseName = g.Key.WarehouseName,
Status = g.Key.SystemAreaName,
QtyUnassigned = (int)(Int64?)g.Count(p => p.sli.ProductSKUID != null)
}).ToList();
return SKUStock;
It returns exactly what i need :).

Select multiple columns in LINQ

I've written a LINQ query shown below :
List<Actions> actions = resourceActions.Actions.Select(s => s.ActionName).ToList();
How do I give for selecting multiple columns here ? ie I want to add columns s.ActionId and s.IsActive. I'm unable to apply it.
Make a class to represent the data you want:
public class ResourceAction
{
public int Id {get;set;}
public string Name {get; set; }
}
Select a list of those instead:
List<ResourceAction> actions = resourceActions.Actions
.Select(s => new ResourceAction() { Id = s.Id, Name = s.ActionName}).ToList();
I believe this is what your looking for. However you need to change the output to an anonymous type.
var actions = resourceActions.Actions.Select(s => new { s.ActionName, s.ActionId, s.IsActive } ).ToList();
You can use a anonymous type for this, for example
var actions = resourceActions.Actions.Select(s =>
new { Id = s.Id, Name = s.ActionName, Active = s.IsActive).ToList();
but a better way would be to create a class like
public class ActionWithId
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
List<ActionWithId> actions = resourceActions.Actions.Select(s =>
new ActionWithId() { Id = s.Id, Name = s.ActionName, Active = s.IsActive }).ToList();

ef cf linq populate ignored property

I'm sure this has to have been asked before, but I couldn't find a good way to search on it.
I have a class like the following
public class Vendor
{
public int VendorId { get; set; }
public string Name { get; set; }
public int ProductCount { get; set; }
}
I have a configuration class setup like
public class VendorConfiguration : EntityTypeConfiguration<Vendor>
{
public VendorConfiguration()
{
Property(p => p.Name).IsRequired().HasMaxLength(128);
Ignore(v => v.ProductCount);
}
}
Here is the query i use to grab the vendors.
public Vendor[] GetVendors()
{
using (var db = new UbidContext())
{
var query = (from vendor in db.Vendors
select vendor);
return query.ToArray();
}
}
How could I populate ProductCount with a subquery that would look similar to
ProductCount = (from vend in db.VendorProducts
where vend.VendorId == id
select vend).Count()
Is there a way I can add that to the main query, so I'm only making 1 call to the db?
Thanks,
Andrew
I would try it this way:
public Vendor[] GetVendors()
{
using (var db = new UbidContext())
{
var query = from vendor in db.Vendors
join vp in db.VendorProducts
on vendor.VendorId equals vp.VendorId
into vendorProducts
select new
{
Vendor = vendor,
ProductCount = vendorProducts.Count()
};
foreach (var item in query)
item.Vendor.ProductCount = item.ProductCount;
return query.Select(a => a.Vendor).ToArray();
}
}
Problem is that you must project into a non-entity type (anonymous in the example above) and then copy the projected ProductCount value into the projected Vendor item by item before you return it.

Resources