Linq: return List contained List - c#

I have a class:
public class BigClass
{
public int Id { get; set; }
public string Name { get; set; }
...
public List<LittleClass> LittleClassList { get; set; }
}
Is it possible to return List of its instances in one request to DataBase? This approach invokes many requests to DataBase:
List<BigClass> data = context.Table1_Name ...
foreach(var item in data)
{
item.LittleClassList = context.Table2_Name ...
}
Lambda syntax is preferred. Thank you.

from big in context.TableA
from little in big.LittleClassList
select little;
And in extension format
Context.TableA.Where(...).SelectMany(x=>x.LittleClassList);

List<LittleClass> littleClassesOfListBigClasses = new List<LittleClass>();
context.BigClass.ToList().ForEach(e=>littleClassesOfListBigClasses.AddRange(e.LittleClassList));
Specialy for Dave A.
What I think OP wants:
He want to take all LittleClasses stored in db by one lambda-expression.
Explanation about how does my code works:
1) I use EF(entity framework). If you dont know what it is you can read information
here. In the question I see that the OP knows what is EF and how to use it, so I didnt find a need to explain.
2) I take all data from table BigClass from the DB and bring it as List(in 1 query)
3) I used ForEach(from Linq) to go through the returned BigClass list and add the all LittleClasses to the littleClassesOfListBigClasses
Notice also you can add Where before to ToList to get not all data stored in BigClass table but only a part, it will look something like this:
context.BigClass.Where(some lambda-expression).ToList()
So my answer is clear now?

Related

Why: LINQ to Entities does not recognize the method?

I have two pieces of code which seems have same functionality but one of them lead to exception but another one is works like a charm. I want to know can you imagine why this happens?
I have below line in my web application which leads to this exception:
LINQ to Entities does not recognize the method 'System.Object
FilterDeliveryAddressFields(WebApplication1.Models.DeliveryAddress)'
dynamic deliveryAddresses = (from address in db.DeliveryAddress
select filterDeliveryAddressFields(address)).ToList();
And here is filterDeliveryAddressFields:
private dynamic filterDeliveryAddressFields(DeliveryAddress address)
{
return new { address.address, address.deliverTo, address.deliverToPhoneNumber, address.id };
}
And here is Linq-2-Sql generated Model for DeliveryAddress which have foreign key relationship with Subscriber:
public partial class DeliveryAddress
{
public int id { get; set; }
public int fkSubscriberId { get; set; }
public string address { get; set; }
public string deliverTo { get; set; }
public string deliverToPhoneNumber { get; set; }
public virtual Subscriber Subscriber { get; set; }
}
But when I change db.DeliveryAddress items to list first and then run code again as below everything goes well and no exception occurs again. I want to know what wrong with first code snippet which does not happen in below snippet?
List<DeliveryAddress> addresseList = db.DeliveryAddress.ToList(); //magic trick?!
dynamic deliveryAddresses =
(from address in addresseList
select filterDeliveryAddressFields(address)).ToList();
Actually, your method couldn't be translated to T-SQL, Linq to Entities couldn't recognize every method, the magic behind the .ToList() method which you are looking for is, after data is loaded, any further operation (such as select) is performed using LINQ to Objects, on the data already in memory.
However the performance is not guaranteed in this approach, as you have to load
your data into memory, so imagine you have a lot of data in your db, what will be happened next?
Agree with #Salah . In LINQ to Entities, it first try to convert your query to command tree and execute against your ORM. Please read here you can find more details.
In your first approach Linq tries to convert your filterDeliveryAddressFields(address)) method to command tree. That is why it complains that LINQ to Entities does not recognize the method.
In your second approach you execute against list or IEnumerable<T>, which means you use LINQ to Object. You can read more about it here.
For your first solution you can try another implementation. Simply try to use aggregate method to filter your result. Then you don't need filterDeliveryAddressFields(address)) method. You can find example here
Something like this, (Sorry I didn't try this my self. This is only for you to get an idea.)
from address in db.DeliveryAddress
select new { address.address, address.deliverTo, address.deliverToPhoneNumber, address.id };

Calculated fields that improve performance but need to be maintained (EF)

I have this "1 to N" model:
class Reception
{
public int ReceptionId { get; set; }
public string Code { get; set; }
public virtual List<Item> Items { get; set; }
}
class Item
{
public int ItemId { get; set; }
public string Code { get; set; }
public int Quantity { get; set; }
public int ReceptionId { get; set; }
public virtual Reception Reception { get; set; }
}
And this action, api/receptions/list
public JsonResult List()
{
return dbContext.Receptions
.Select(e => new
{
code = e.Code,
itemsCount = e.Items.Count,
quantity = e.Items.Sum(i => i.Quantity)
}).ToList();
}
which returns a list of receptions, with their number of items:
[
{code:"1231",itemsCount:10,quantity:30},
{code:"1232",itemsCount:5,quantity:70},
{code:"1234",itemsCount:30,quantity:600},
...
]
This was working fine but I'm having too many Reception's and Item's thus the query is taking too long...
So I want to speed up by adding some persisted fields to Reception:
class Reception
{
public int ReceptionId { get; set; }
public string Code { get; set; }
public virtual List<Item> Items { get; set; }
public int ItemsCount { get; set; } // Persisted
public int Quantity { get; set; } // Persisted
}
With this change, the query ends up being this:
public JsonResult List()
{
return dbContext.Receptions
.Select(e => new
{
code = e.Code,
itemsCount = e.ItemsCount,
quantity = e.Quantity
}).ToList();
}
My question is:
What's the best way to maintain these two fields?
I will gain in performance but now I will need to be more careful with the creation of Item's
Today an Item can be created, edited and deleted:
api/items/create?receptionId=...
api/items/edit?itemId=...
api/items/delete?itemId=...
I also have a tool for importing receptions via Excel:
api/items/createBulk?...
Maybe tomorrow I will have more ways of creating Item's, so the question is how do I make sure that these two new fields, ItemsCount and Quantity will be up to date always?
Should I create a method within Reception like this?
class Reception
{
...
public void UpdateMaintainedFields()
{
this.Quantity = this.Items.Sum(e => e.Quantity);
this.ItemsCount = this.Items.Count();
}
}
And then REMEMBER to call it from all the previous URL's? (items/create, items/edit, ...)
Or maybe should I have a stored procedure in the database?
What is the common practice? I know there are calculated columns but these refer to fields of the same class. Also there are indexed views, but I'm not sure if they apply well to scenarios like this.
From your code it seems to me that you do not have a layer for business logic, and everything is implemented in the controllers, this causes the problem for you that when you would have a different way (and it seems, that you mean a different controller) you have to implement this logic again and it is easy to forget, and if you do not forget, you could forget to maintain later.
So I would recommend to have a layer for business logic (like adding new items) and use it from the controllers where you want to create items.
I would also recommend write the function UpdateMaintainedFields as you asked, but call it in the business logic layer after adding the items, not in the controllers!
You could write the logic on the database also (trigger) if you can accept that you can't write unit test.
Assuming the original query cannot be improved with the correct execution plan in SQLServer, the way to update these fields is via a trigger in the DB. When an insert occurs (or possible an update if your persisted fields change according to the data) then when an insert occurs to that table, the trigger is run. It would be responsible for updating all the rows with the new values.
Obviously your insert performance would drop, but your query performance would be that of a simple index and read of a single row. Obviously you wouldn't be able to use this trick if you were to return a subset of the table, as all the quantities would be fixed.
An alternative is to hold the count and quantity sums in a separate table, or in a dummy row that holds the summed quantities as its entry for quantity. YMMV.
PS I hate how what is a SQL question has been turned in one about C# code! Learn SQL and run the queries you need directly in the DB, that will show you much more about the performance and structure of what you're looking for than getting EF involved. /rant :)
You want to store the same information duplicitly, which can lead to inconsistencies. As an inspiration, indexes are also duplicating data. How do you update them? You don't. It is all fully transparent. And I would recommend the same approach here.
Make sum table, maintained by triggers. The table would not be included in any datacontext schema, only way to read it would be through non updateable views or stored procedures. Its name should evoke, that nobody should ever touch this table directly.
You can now access your data from various frameworks and do not worry about updating anything. Database would assure the precalculated sums are always correct, as long as you do not write to the sum table on your own. In fact you can add or remove this table any time and no application would even notice.

How to create dynamic Linq Select Expression with anonymous objects

Am using Entity Framework to run a query on a table. However, i need to get select columns only.
class MyEvent
{
public string Name { get; set; }
public int Id { get; set; }
virtual Stage EventStage { get; set; }
..... more columns .....
}
class Stage
{
public string Name { get; set; }
public string Location { get; set; }
..... more columns .....
}
I can write an IQueryable to return these as
dbContext.MyEvents
.Select(s =>
new {
Name = s.Name,
Id = s.Id,
EventStage = new
{
Name = s.EventStage.Name,
Id = s.EventStage.Id
}
}
)
.ToList();
This works as expected, giving me just those columns am interested in.
Now, I need to construct that 'Select' call dynamically using Expression tree, something like here.
How can I achieve that? Is it feasible to construct an anynomous object, like above, via expressions?
EDIT:
The use case for me is that I have a generic dB context class which takes a list of columns as strings to be fetched. In the past, we were returning all columns, ignoring that input list. So, now I need to dynamically generate the select statement to return only the required subset of columns, which can either be done via anonymous object or a dynamically created DTO.
Thanks
Maybe you can use something like the ToDynamic method from here:
https://gist.github.com/volak/20f453de023ff75edeb8
A possible usecase for this problem:
Let the user select the columns to display and query only those selected columns, so you don't query always the whole entity from the database.
Define a strongly typed object and return that. I would avoid using a dynamic object.
Note: you can't return an anonymous object.

How to do free (non-associated) join for one specific row with XPO?

let's say I have a pesistent class called DailyVisitorSummary, which describes for each web page how many visitors it had per day. For simplicitzy, assume that we represent the day as a pure integer.
Now I would like to create a query to retrieve a specific day data, and also the data from its previous and next days. What I know is that there surely wil be at most one previous and next day data record for the same webpage, so I could write an SQL query (MySQL syntax) like:
SELECT c.*,p.*,n.* from DailyVisitorSummary c
LEFT JOIN DailyVisitorSummary p ON p.WebPage = c.WebPage AND p.Day = c.Day - 1
LEFT JOIN DailyVisitorSummary n ON n.WebPage = c.WebPage AND n.Day = c.Day + 1
WHERE c.Day = 453;
I would like to populate the following viewmodel with the result:
public class VMDailyVisitors3Day {
public VMDailyVisitors CurrentDay { get; set; }
public VMDailyVisitors PreviousDay { get; set; }
public VMDailyVisitors NextDay { get; set; }
}
public class VMDailyVisitors {
public int Day { get; set;; }
public int WebPageID { get; set; }
public int VisitorCount { get; set; }
}
How could I do this query with Linq to XPO?
I need a LINQ solution, because I need to use the result in a server-mode MVC GridView.
Linq to XPO supports group join only. A possible solution would be to create a SQL view in the database and populate it with data using your SQL query. Then you can map another persistent class to this view to obtain data.
The view must have at least one column with unique values. Do not use the newid function or similar to generate unique values, because this approach assigns different values to one and the same row each time the data is being queried. Server Mode uses a key column to identify rows. Use actual data to populate a key column. For example, concatenate values from the WebPage and the Day columns. Make sure that this produces distinct values, though.
I've found a workaround with the help of the DX support by using the PersistentAliasAttribute as a middle step, where I can do free joins, and then use that property in the XPQuery. If anyone intrested, check out here.

Custom object with nested collection

I've created an object that contains another collection in one of it properties.
This is the main object:
public class MeterPrevReadInfo
{
public int JobMeterID { get; set; }
public string PreviousJobReference { get; set; }
public FuelType MeterFuelType { get; set; }
public List<MeterPrevReadRegInfo> Regs { get; set; }
public DateTime DateMeterRead { get; set; }
}
This is the child object:
public class MeterPrevReadRegInfo
{
public string RegisterID { get; set; }
public string MeterRead { get; set; }
}
I need to bind this object to a repeater control, I would like to show the DateMeterRead property and all the MeterRead properties in the repeater.
Is this possible using Linq? I could easily do it using a t-sql query from the database, but I just figured it should be possible to do this in memory without the overhead of another trip to the database.
Don't get confused - LINQ isn't a data access layer or ORM (perhaps you're thinking of LINQ-to-SQL, or LINQ-to-Entities?)
You can absolutely query an in-memory collection using LINQ, although your questions seems to relate to database.
I could easily do it using a t-sql
query from the database, but I just
figured it should be possible to do
this in memory without the overhead of
another trip to the database.
You can retrieve all this data from the database in one query & then construct objects. You can do this with a stored procedure, LINQ-to-SQL, Entity Framework, or other tools. You should choose the best tool for your requirements. I expect this is a very small part of the requirement, so take a step back, choose the best tool, and make this work using that tool.
sure this is possible. It looks like you want something like this:
List<MeterPrevReadInfo> list = ...;
var result = from item in list
from info in item.Regs
select new {item.DateMeterRead, info.MeterRead};
This query defines a list of anonymous objects with the two properties you want.
You can access an anonymous object representing the model you want by using the Linq Select extension method as follows:
var readInfo = new MeterPrevReadInfo();
readInfo.Regs.Select(x => new {
x.RegisterID,
x.MeterRead,
readInfo.DateMeterRead
});

Categories

Resources