Lazy loading vs individually loading each property for foreign keys - c#

Since I am using Entity Framework 6 Database First and my database has all of its relationships setup, my EDMX auto generates navigation properties for me whether they are object lists or individual objects. This way I can lazy load using .Include(x => x.NavProperty).
How would I handle something such as the following using lazy loading that was previously done in a stored procedure that would return a list of OrderDetailDto which has a string property called MaterialDescription?
SELECT od.*, IIF(m.Description is null, od.Description, m.description) as MaterialDescription
FROM OrderDetail od
LEFT OUTER JOIN Material m ON od.MaterialId = m.Id
I am really trying to avoid doing this but I might end up ignoring all of the build in navigation properties and create a Dto for each entity and strictly add the properties I want bound to it. Then do selects like the following:
using (var context = new AppContext())
{
var odList = (from od in context.MaterialPODetails
join m in context.Materials on od.MaterialId equals m.Id into gj
from m in gj.DefaultIfEmpty()
select OrderDetailDto
{
Id = od.Id,
MaterialPOId = od.MaterialPOId,
MaterialId = od.MaterialId,
Description = od.Description,
Unit = od.Unit,
Quantity = od.Quantity,
UnitPrice = od.UnitPrice,
Archived = od.Archived,
DateCreated = od.DateCreated,
CreatedBy = od.CreatedBy,
DateModified = od.DateModified,
ModifiedBy = od.ModifiedBy,
// navigation properties
MaterialDescription = (m == null ? obj.Description : m.Description)
}).ToList();
return odList;
}
}
Please give me any advice possible on how to avoid. I want to avoid creating Dtos and simply use the Auto Generated EF classes with the virtual properties but it seems that there is limitations and performance differences. I won't be able to do the If statement that I want to do with the left outer join.
I don't know if this is proper terminology but I believe what I want is to still use Flattened objects but not have to create Dtos.

Related

Entity Framework split string

Outside of the main model I am building this list which I am them adding to a the main model that the method is returning. I wanted to know if I can do all this work inside building the main model?
Before building the main model
string Territorystring = (from p in db.Projects
join pm in db.ProjectMaterialUses on p.ProjectId equals pm.ProjectId
where projectId == p.ProjectId
select pm.Territory).FirstOrDefault();
List<string> guidStrings = TerritoryOfDistributionList.Split(',').ToList();
List<Guid> guids = guidStrings.Select(Guid.Parse).ToList();
List<Model.Country> = (from co in db.vwCountryItemDictionaries
where guids.Contains(co.CountryGUID)
select new Model.Country
{
Name = co.Name
}).ToList();
Main model
select new Project
{
TerritoryOfDistributionList = TerritoryOfDistributionList,
.....
}
When your list is used client-side, I'm assuming it will be in the form of an array. If that's true, then you could always switch your csv over to a json array (assuming your territory links aren't expecting to change):
https://learn.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-ver15
If the territory links are expected to change, you could always do version-chaining/updates via bulk sql statements.
Alternatively, you could set up a view, and read against that instead.

Linq GroupBy Clause not including items with zero count

I have a query below which is supposed to group the result by Id, EntityName, DocType, Jurisdiction. For each group the query also returns the ProductList items.
At the moment if the group contains one or more than one product, Then i can see the result giving out a group with a combination of Id,EntityName,DocType,Jurisdiction and ProductList, However if the result doesnt contain products for a particular group i do not see the group at all. What i would like to do is show the groups even if does not have any products in its group. So if the count of ProductList is zero, i would like to set
ProductList= new List NettingAgreementProductDto. Any input would be highly appreciated.
var result = from nae in nettingAgreementEntities.Result
join no in nettingOpinions.Result
on nae.EntityId equals no.EntityId
join np in nettingProducts.Result
on no.ProductId equals np.Id
group np by new
{ nae.EntityId,
nae.EntityName,
nae.DocType,
nae.Jurisdiction
} into g
select new NettingAgreementEntityDto
{
Id = g.Key.EntityId,
EntityName = g.Key.EntityName,
DocType = g.Key.DocType,
Jurisdiction = g.Key.Jurisdiction,
ProductList = g.Select(x => new
NettingAgreementProductDto
{
Id = x.Id,
Name = x.Name
}).ToList()
};
To recap from the comments, currently your query is using Inner Join for associating NettingAgreementEntity with NettingAgreementProducts. This not only multiplies the result set (and thus requires you to use GroupBy after), but also filters out the NettingAgreementEntity without NettingAgreementProducts.
You can achieve the goal by switching to Group Join (or Left Outer Join + GroupBy).
But why entering all these complications. EF navigation properties allow you to almost forget about manual joins, and also allow you to easily see the multiplicity, thus whether you need to group the result or not.
So what I would suggest is to add the currently missing collection navigation property to your NettingAgreementEntity class:
public class NettingAgreementEntity
{
// ...
public virtual ICollection<NettingOpinion> Opinions { get; set; }
}
Optionally do the same for NettingAgreementProduct in case in the future you need something similar for products (it's a many-to-many relationship and should be able to be queried from both sides).
Also I would rename the NettingOpinion class navigation properties NettingAgreementProductNavigation and NettingAgreementEntityNavigation to something shorter, for instance Product and Entity. These names (as well as the names of the collection navigation properties) do not affect the database schema, but IMHO provide better readability.
Once you have that, you'll see that the desired LINQ query is a matter of simple Selects which convert entity class to DTO and let EF query translator produce the necessary joins for you:
var result = db.Set<NettingAgreementEntity>()
.Selec(nae => new NettingAgreementEntityDto
{
Id = nae.EntityId,
EntityName = nae.EntityName,
DocType = nae.DocType,
Jurisdiction = nae.Jurisdiction,
ProductList = nae.Opinions
.Select(no => new NettingAgreementProductDto
{
no.Product.Id,
no.Product.Name,
}).ToList(),
});

Using Entity Framework to Select Only Some Columns and then Saving Back to the Database

I want to update a sequence (SortOrder) column in my database using Entity Framework.
Seems like the easiest way is to load the SortOrder for each item in the set, update the SortOrder as needed, and then save the rows back to the database.
Of course, this would be more efficient if I don't need to retrieve every column in the rows I am updating. And what I'm having trouble understanding is what happens if I only select some of the columns, make changes to those columns, and save it back.
var x = from o in Package.PackageProducts
orderby o.SortOrder
select new { o.Id, o.SortOrder };
// Modify SortOrder in this collection x
// Save x back to the database
How does Entity Framework handle modifying and saving partial entities like this?
Does anyone know if Microsoft has documented somewhere what happens in this case? Does anyone know enough about what happens that you can tell me?
You can create stub entities from the anonymous types and mark SortOrder as modified:
var x = (from o in Package.PackageProducts
orderby o.SortOrder
select new { o.Id, o.SortOrder }).ToList();
// Modify SortOrder in this collection x
...
// Save x back to the database
foreach(y in x)
{
var p = new PackageProduct { Id = y.Id, SortOrder = y.SortOrder }); // stub
db.PackageProducts.Attach(p);
db.Entry(p).Property(p1 => p1.SortOrder).Modified = true;
}
db.SaveChanges();
Where db is the DbContext.
You may have to disable validation first, because the stubs probably don't have all required properties:
db.Configuration.ValidateOnSaveEnabled = false;

C# - Entity Framework - Join method

I have a table called "PublicUserOfferingSignUp" which contains the following columns.
Id
PublicUserId - foreign key to PublicUser.Id
OfferingId
Created
My application is using Entity Framework, but I am getting stuck with how to join from the PublicUserOfferingSignUp table to the PublicUser table.
I want to obtain a list of PublicUserOfferingSignUp records but ordered by the Name column of the PublicUser table.
Currently I have this ....
return DataBase.PublicUserOfferingSignUps.Join(PublicUser,
But I can't seem to work it out, any ideas ....
Steven
Can anybody help.
Something like that
DataBase.PublicUserOfferingSignUps.Join(Database.PublicUsers,
puosu => puosu.PublicUserId,//PublicUserOfferingSignUps key
pu => pu.Id,//PublicUser key
(puosu, pu) => new {
publicUsersOfferingSignUp = puosu,//we take all data from PubliUserOfferingSignUps
puName = pu.Name//and Name from PublicUser
})
.OrderBy(x => x.puName)//we order by PublicUser Name
.Select(x => x.publicUsersOfferingSignUp );//we take only the PublicOfferingSignUps
Edit : as #M.Schenkel noticed, it would be easier to have a
public virtual PublicUser PublicUser {get;set;}
in your PublicUserOfferingSignUp model
then the query would be
DataBase.PublicUserOfferingSignUps
.OrderBy(puosu => puosu.PublicUser.Name);
easier, no ?
When you use the Entity Framework, the public user should be a property of your PublicUserOfferingSignUp-entity. If not, you can write a LINQ query to join them. For example:
var result = from pu in context.PublicUserOfferingSignUp
join u in context.PublicUser on u.id equals pu.PublicUserId
select pu;
(this code is untested, but should give you the idea).

Filtering a graph of entity framework objects

I'm trying to filter down the results returned by EF into only those relevant - in the example below to those in a year (formattedYear) and an ordertype (filtOrder)
I have a simple set of objects
PEOPLE 1-M ORDERS 1-M ORDERLINES
with these relationships already defined in the Model.edmx
in SQL I would do something like...
select * from PEOPLE inner join ORDERS on ORDERS.PEOPLE_RECNO=PEOPLE.RECORD_NUMBER
inner join ORDERLINE on ORDERLINE.ORDER_RECNO=ORDERS.RECORD_NUMBER
where ORDERLINE.SERVICE_YEAR=#formattedYear
and ORDERS.ORDER_KEY=#filtOrder
I've tried a couple of approaches...
var y = _entities.PEOPLE.Include("ORDERS").Where("it.ORDERS.ORDER_KEY=" + filtOrder.ToString()).Include("ORDERLINEs").Where("it.ORDERS.ORDERLINEs.SERVICE_YEAR='" + formattedYear + "'");
var x = (from hp in _entities.PEOPLE
join ho in _entities.ORDERS on hp.RECORD_NUMBER equals ho.PEOPLE_RECNO
join ol in _entities.ORDERLINEs on ho.RECORD_NUMBER equals ol.ORDERS_RECNO
where (formattedYear == ol.SERVICE_YEAR) && (ho.ORDER_KEY==filtOrder)
select hp
);
y fails with ORDER_KEY is not a member of transient.collection...
and x returns the right PEOPLE but they have all of their orders attached - not just those I am after.
I guess I'm missing something simple ?
Imagine you have a person with 100 orders. Now you filter those orders down to 10. Finally you select the person who has those orders. Guess what? The person still has 100 orders!
What you're asking for is not the entity, because you don't want the whole entity. What you seem to want is a subset of the data from the entity. So project that:
var x = from hp in _entities.PEOPLE
let ho = hp.ORDERS.Where(o => o.ORDER_KEY == filtOrder
&& o.ORDERLINES.Any(ol => ol.SERVICE_YEAR == formattedYear))
where ho.Any()
select new
{
Id = hp.ID,
Name = hp.Name, // etc.
Orders = from o in ho
select new { // whatever
};
I am not exactly sure what your question is but the following might be helpful.
In entity framework if you want to load an object graph and filter the children then you might first do a query for the child objects and enumerate it (i.e. call ToList()) so the childern will be fetched in memory.
And then when you fetch the parent objects (and do not use .include) enitity framework will able to construct the graph on its own (but note that you might have to disable lazy loading first or it will take long to load).
here is an example (assuming your context is "db"):
db.ContextOptions.LazyLoadingEnabled = false;
var childQuery = (from o in db.orders.Take(10) select o).ToList();
var q = (from p in db.people select p).ToList();
Now you will find that every people object has ten order objects
EDIT: I was in a hurry when I wrote the sample code, and as such I have not tested it yet, and I probably went wrong by claiming that .Take(10) will bring back ten orders for every people object, instead I believe that .Take(10) will bring back only ten overall orders when lazy loading is disabled, (and for the case when lazy loading is enabled I have to actually test what the result will be) and in order to bring back ten orders for every people object you might have to do more extensive filtering.
But the idea is simple, you first fetch all children objects and entity framework constructs the graph on its own.

Categories