Entity framework recursively populate children of DTO - c#

So I have a DataTable object, which I want to populate based on a table from my database. Currently, I do the following:
public Task<IEnumerable<DataDTO>> Loadreferences(int Id)
{
IEnumerable <DataDTO> DTOList = _context.Data.Where(p => p.Id == Id).Select(p => new DataDTO
{
Id = p.Id,
Children = p.Children
});
return Task.FromResult(DTOList);
}
The original table in my database is called Data and contains a lot of other stuff that I have left out for this question. However, the point in constructing this datatable object is basically to reduce the payload towards the server (as it leaves out some of the columns and transform others)
I can successfully populate my DTO, but I have a problem populating the children. Naturally this is because, the list of children is pointing to objects of type Data, and I try to use it in type DataDTO.
Therefore, I get an error stating that I can't convert a list of Data objects to a list of DataDTO objects.
Do I really need a loop to fix this for me, or can I do it with a simple query?

So it turned out, that there was a relatively easy fix to this:
foreach (DataDTO data in datas)
{
data.Children = datas.Where(p => p.ParentId == data.Id).ToList();
}
By doing the following, I didn't have to recursively add new objects in my select statement or do some sort of stored procedure.
The loop simply checks if any items in the list has the current looped item as parentId, whereafter it appends to the children property.

Related

Blank Entry for DataContext

We have a data bound control that is populated with a list of banks.
I can figure out how to Read, Insert and Delete real accounts.
On the form, however, our client needs a way to select a "None" option (some customers use cash).
I cannot insert a "None" entry into the banks data table because of how the data tables are linked to other data tables.
I attempted to insert a "None" record before returning the DataContext so that this option would always appear at the top of the list, but there doesn't appear to be any way to get the data into the collection.
public System.Data.Linq.Table<bank> Banks
{
get
{
return this.GetTable<bank>();
}
}
I was able to insert a fake record into an IQueryable list:
internal IQueryable<bank> GetBankInfo(bool insertBlank = false)
{
var list = (from item in banks
orderby item.bank_name
select item).ToList();
if (insertBlank)
{
var item = new bank()
{
bank_name = String.Empty,
contact_name = String.Empty,
contact_phone_num = String.Empty,
routing_num = "None",
source = String.Empty,
fact_routing_num = String.Empty,
note_id = (int?)null,
};
list.Insert(0, item);
}
return (IQueryable<bank>)list;
}
But, I can't use this because it throws a TargetInvocationException:
Unable to cast object of type 'System.Collections.Generic.List1[Billing.DataDB.bank]' to type 'System.Linq.IQueryable1[Billing.DataDB.bank]'.
How do I insert a blank record for the DataContext display?
I do not want the blank record in the database.
You can fix this two ways:
First, change the return type of the method to IEnumerable<T>...
internal IEnumerable<bank> GetBankInfo(bool insertBlank = false)
...and leave out the cast in the return statement. It'll be redundant, as List<T> implements IEnumerable<T>
return list;
Based on what you mentioned in your question, this should work.
Second way:
If you're doing something else with that list/queryable/whatever, such that you really need it to really be IQueryable<T>, that's even easier. Leave the function return type as it was:
internal IQueryable<bank> GetBankInfo(bool insertBlank = false)
And return this:
return list.AsQueryable();
Here's the problem:
I was able to insert a fake record into an IQueryable list:
Actually, you weren't. You enumerated the results of an IQueryable into a new List<T> instance: That's what happened in your ToList() call. It's not a cast, it creates a new collection object. Then you inserted a fake record into the List<T>.
The thing you were casting to IQueryable<bank> wasn't the IQueryable<bank> that you started with; it was a completely different object which contained all the items that were enumerated by the IQueryable<bank>.
The purpose of IQueryable is so the actual query can be executed remotely, as with Entity Framework for example. However, once you materialize the query in the ToList() call, the remote ship has sailed the barn out of the station. Since IQueryable<T> inherits from IEnumerable<T>, I'd tend to use the latter for a general "reference to sequence of things" type. Any method that takes IEnumerable<T> for a parameter will happily consume an IQueryable<T> instead.

select distinct on model and iterate

I'm having issues getting a distinct record from my DB for one of my models.
I'm using EF to select and return the table contents to the model type which I then iterate over, the below code works just fine:
IQueryable<StudentRecord> students = _dbaccess.GetAllStudents();
Where _dbaccess is a local instance of an interface to DB context and GetAllStudents returns all items of that model from the DB context itself.
foreach (StudentRecord student in students)
{
if (student.Name == "something")
{
}
}
The issue arises when I try to use LINQ to select distinct students:
IQueryable<StudentRecord> students =
_dbaccess.GetAllStudents().Select(x => x.StudentID).Distinct();
Because of the way my loop is structured I need to access various members of my model, but I can't use LINQ when my enumeration variable is of the type of StudentRecord (it works with a string but that then means I cant access the model members in my loop).
Is there an easy way to implement this that I'm not seeing?

List<T> vs IEnumerable<T> in foreach

I was trying to get some Lists sorted using OrderBy within a foreach loop, but for some reason they weren't maintaining their sort order outside of the loop. Here's some simplified code and comments to highlight what was happening:
public class Parent
{
// Other properties...
public IList<Child> Children { get; set; }
}
public IEnumerable<Parent> DoStuff()
{
var result = DoOtherStuff() // Returns IEnumerable<Parent>
.OrderByDescending(SomePredicate)
.ThenBy(AnotherPredicate); // This sorting works as expected in the return value.
foreach (Parent parent in result)
{
parent.Children = parent.Children.OrderBy(YetAnotherPredicate).ToList();
// When I look at parent.Children here in the debugger, it's sorted properly.
}
return result;
// When I look at the return value, the Children are not sorted.
}
However, when I instead assign result like this:
var result = DoOtherStuff()
.OrderByDescending(SomePredicate)
.ThenBy(AnotherPredicate)
.ToList(); // <-- Added ToList here
then the return value has the Children sorted properly in each of the Parents.
What is the behavior of List<T> vs an IEnumerable<T> in a foreach loop?
There seems to be some sort of difference since turning result into a List fixed the problems with sorting in the foreach loop. It feels like the first code snippet creates an iterator that makes a copy of each element when you do the iteration with foreach (and thus my changes get applied to the copy but not the original object in result), while using ToList() made the enumerator give a pointer instead.
What's going on here?
The difference is that one is an expression that can procuce a set of Parent objects, and the other is a list of Parent objects.
Each time that you use the expression, it will use the original result from DoOtherStuff and then sort them. In your case it means that it will create a new set of Parent objects (as they obviously don't retain the children from the previous use).
This means that when you loop through the objects and sort the children, those objects will be thrown away. When you use the expression again to return the result, it will create a new set of objects where the children naturally is in the original order.
Sample code of what likely happens to add to Guffa's answer:
class Parent { public List<string> Children; }
Enumerable of "Parent", will create new "Parent" objects every time it is iterated:
var result = Enumerable.Range(0, 10)
.Select(_ => new Parent { Children = new List<sting>{"b", "a"});
Now first iteration with foreach there will be 10 "Parent" objects created (one for each iteration of the loop) and promptly discarded at the end of each iteration:
foreach (Parent parent in result)
{
// sorts children of just created parent object
parent.Children = parent.Children.OrderBy(YetAnotherPredicate).ToList();
// parent is no longer referenced by anything - discarded and eligible for GC
}
When you look at result again it will be re-iterated and new set of "Parent" objects created every time you look at it, hence "Children" are not sorted.
Note that depending on how DoOtherStuff() // Returns IEnumerable<Parent> is implemented result could be different. I.e. DoOtherStuff() can return collection of existing items from some cached collection:
List<Parent> allMyParents = ...;
IEnumerable<Parent> DoOtherStuff()
{
return allMyParents.Take(7);
}
Now every iteration of result will give you new collection, but each item in the collection will just be an item from allMyParents list - so modification "Children" property would change the instances in allMyParents and change would stick.
Remarks
The ToList(IEnumerable) method forces immediate
query evaluation and returns a List that contains the query
results. You can append this method to your query in order to obtain a
cached copy of the query results.
From: https://msdn.microsoft.com/en-us/library/bb342261(v=vs.110).aspx
If you omit the ToList() the query is not evaluated... Your debugger might do that for you but that is just a wild guess of me.

Calculation in EF Projection

Is there a way I can achieve the following?
// colourInfo.Discount = 75, but can change
// allPrice type has Part, Desc, Type
var a = allPricesForPgs.Where(x => x.PG == c && x.ColourCode == colourInfo.ColourCode).Select(y=> new AllPrice {Part=y.Part, Desc=y.Desc, Price=y.Price*(colourInfo.Discount/100)}));
I get the error : The entity or complex type 'Portal.AllPrice' cannot be constructed in a LINQ to Entities query.
It seems the EF cannot handle calculations, what are my options since I am getting a dynamic value from one table to do a calculation on another?
Sam1's comment is correct. You cannot project into another entity. Other options you have can be to create an anonymous type like so:
var a = allPricesForPgs
.Where(x => x.PG == c && x.ColourCode == colourInfo.ColourCode)
.Select(y=> new
{
Part=y.Part,
Desc=y.Desc,
Price=y.Price*(colourInfo.Discount/100)
}
));
Or to create a class that will hold the temporary data (such as a DTO).
Since it seems like all you need to do is have this information to modify some other entity, you should be able to do it with the anonymous type.
EDIT:
You could add a '.ToList()' right before the .Select(...). You'd essentially be using LINQ TO OBJECTS instead of LINQ TO ENTITIES, so if a lot of entities might match the allPricesForPgs.Where(...) statement, you should keep away from that.
But, if you want these as AllPrice's, why are they not added to the AllPrice DB? Are you keeping a separate list of some AllPrice's from Entity Framework and some AllPrice's from this list? This could get confusing and cause errors.
A final option would be to extend the class. All entities are declared PARTIAL. You can create another class like:
partial class AllPrice
{
Double DiscoutedPrice { get { Price * myDiscount/100; } }

C#, Linq2SQL - tricks to fetch a ViewModel object with relation data?

I don't know Linq2Sql so well yet and I was wondering if there is a trick for this probably common MVVM scenario. I have Linq2Sql data context containing Domain models, but I am fetching data for my customized ViewModel object from it.
var query = from ord in ctx.Table_Orders
select new OrderViewModel()
{
OrderId = ord.OrderId,
OrderSum = ord.OrderSum,
OrderCurrencyId = ord.OrderCurrencyId,
OrderCurrencyView = ord.Currency.CurrencyText
};
So i want my ViewModel to inculde both CurrencyId from domain object and the CurrencyText from related table to show it nicely in the View.
This code works great. It generates one DB call with join to fetch the CurrencyText. But the model is simplified, real one has many more fields. I want to make the code reusable because I have many different queries, that returns the same ViewModel. Now every minor change to OrderViewModel requires lots of maintainance.
So I moved the code to OrderViewModel itself as a constructor.
public OrderViewModel(Table_Order ord)
{
OrderId = ord.OrderId,
OrderSum = ord.OrderSum,
OrderCurrencyId = ord.OrderCurrencyId,
OrderCurrencyView = ord.Currency.CurrencyText
}
And call it like this.
var query = from ord in ctx.Table_Orders
select new OrderViewModel(ord);
The Problem: The join is gone DB query is no more optimised. Now I get 1+N calls to database to fetch CurrencyText for every line.
Any comments are welcome. Maybe I have missed different great approach.
This is how far i could get on my own, to get the code reusability. I created a function that does the job and has multiple parameters. Then I need to explicitly pass it everything that has crossed the line of entity.
var query = ctx.Table_Orders.Select(m =>
newOrderViewModel(m, m.Currency.CurrencyText));
The DB call is again optimized. But it still does not feel like I am there yet! What tricks do You know for this case?
EDIT : The final solution
Thanks to a hint by #Muhammad Adeel Zahid I arrived at this solution.
I created an extension for IQueryable
public static class Mappers
{
public static IEnumerable<OrderViewModel> OrderViewModels(this IQueryable<Table_Order> q)
{
return from ord in q
select new OrderViewModel()
{
OrderId = ord.OrderId,
OrderSum = ord.OrderSum,
OrderCurrencyId = ord.OrderCurrencyId,
OrderCurrencyView = ord.Currency.CurrencyText
};
}
}
Now i can do this to get all list
var orders = ctx.Table_Order.OrderViewModels().ToList();
or this to get a single item, or anything in between with Where(x => ..)
var order = ctx.Table_Order
.Where(x => x.OrderId == id).OrderViewModels().SingleOrDefault();
And that completely solves this question. The SQL generated is perfect and the code to translate objects is reusable. Approach like this should work with both LINQ to SQL and LINQ to Entities. (Not tested with the latter) Thank You again #Muhammad Adeel Zahid
Whenever we query the database, we mostly require either enumeration of objects (more than one records in db) or we want a single entity (one record in db). you can write your mapping code in method that returns enumeration for whole table like
public IEnumerable<OrderViewModel> GetAllOrders()
{
return from ord in ctx.Table_Orders
select new OrderViewModel()
{
OrderId = ord.OrderId,
OrderSum = ord.OrderSum,
OrderCurrencyId = ord.OrderCurrencyId,
OrderCurrencyView = ord.Currency.CurrencyText
};
}
Now you may want to filter these records and return another enumeration for example on currencyID
public IEnumerable<OrderViewModel> GetOrdersByCurrency(int CurrencyID)
{
return GetAllOrders().Where(x=>x.CurrencyId == CurrencyID);
}
Now you may also want to find single record out of all these view models
public OrderViewModel GetOrder(int OrderID)
{
return GetAllOrders().SingleOrDefault(x=>x.OrderId == OrderID);
}
The beauty of IEnumerable is that it keeps adding conditions to query and does not execute it until it is needed. so your whole table will not be loaded unless you really want it and you have kept your code in single place. Now if there are any changes in ViewModel Mapping or in query itself, it has to be done in GetAllOrders() method, rest of code will stay unchanged
You can avoid the N+1 queries problem by having Linq2SQL eagerly load the referenced entites you need to construct your viewmodels. This way you can build one list of objects (and some referenced objects) and use it to construct everything. Have a look at this blog post.
One word of warning though: This technique (setting LoadOptions for the Linq2SQL data context) can only be done once per data context. If you need to perform a second query with a different eager loading configuration, you must re-initalize your data context. I automated this with a simple wrapper class around my context.

Categories