Select only specific columns from included table in Entity Framework Core - entity-framework

If I have this
var selectedEntities = db.MyEntities.Include(item => item.RelatedEntities);
It will load all properties (columns) in MyEntities but also all properties in ReleatedEntities. If I only need one property from RelatedEntities, how would I specify that?

use .Select() and anonymous type to restrict the columns you want
var result = await _appDbContext.Companies
.AsNoTracking()
.Select(company => new
{
Company = company,
EmployeeIds = company.Employees.Select(emp => emp.Id)
})
.ToListAsync()
.ConfigureAwait(false);

I was looking for the same, and after referring to other questions it seems like it's not possible. The understanding i got is that an EF entity is represented by the collection of it's properties, and hence loading individual values will not fully define the Entity.
To load only selected properties, we need to use Select query, instead of loading the related data. The query will result in an anonymous type.
Note : If the resulting type contains any entity, then the changes will be tracked. Refer Tracking and projections
References :
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/basic-linq-query-operations#selecting-projections
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/projection-operations
How to include only selected properties on related entities
https://learn.microsoft.com/en-us/ef/core/querying/tracking#tracking-and-projections

Related

In Objection.js, what's the benefit of setting up relationMappings?

I'm kind of confused about what relationMappings do inside a Objection.js Model class.
I thought once we setup the relationMapping inside a Model, we will get related data in every query. But, it turns out that I still only the Model properties itself.
Is there anything else I should use to get related data in query?
Relation mappings gives model semantics how relations can be fetched when they are needed. It would be really bad for performance to always query all related rows in addition to main table's row. When you create relation mappings to model, you will not need to write joins manually every time you need to query relations. Also they enable many other objection features, which requires information how row relations goes in DB.
To use relation mappings in query Objection.js requires that within every query you must tell which relations you want to fetch with the main row with .withGraphFetched or .withGraphJoined methods https://vincit.github.io/objection.js/guide/query-examples.html#eager-loading
for example:
class Person extends Model {
static get tableName() {
return 'persons';
}
static get relationMappings() {
return {
pets: {
relation: Model.HasManyRelation,
modelClass: Animal,
join: {
from: 'persons.id',
to: 'animals.ownerId'
}
}
};
}
}
const people = await Person.query().withGraphFetched('pets');
// Each person has the `pets` property populated with Animal objects related
// through the `pets` relation.
console.log(people[0].pets[0].name);
console.log(people[0].pets[0] instanceof Animal); // --> true
Mappings are also used when you insert nested object data with .insertGraph so that related objects are inserted to related tables and foreign key references etc. are automatically filled according to relation mapping declarations.
There are many other places where those are used, but I hope this gives a rough idea why they exist.

Long Running Query in Entity Framework with multiple table joins

I have a query that joins about 10 tables some that are self referencing tables. I use an "IN" statement for the conditional on the ID column (indexed) of the top most table.
var aryOrderId = DetermineOrdersToGet(); //Logic to determine what orderids to get
var result = dbContext.Orders.Where(o=>aryOrderId.Contains(o.id)
.Include(o=>o.Customer)
.Include(o=>o.Items.Select(oi=>oi.ItemAttributes))
.Include(o=>o.Items.Select(oi=>oi.Dimensions))
.Include(o=>o.CustomOptions.Select(oc => oc.CustomOptions1))
.....A Bunch more.....
.ToList();
I would like to figure out a way to speed this up without redesigning my tables and flattening out the structure. Currently 50-200 records take 10-20 seconds.
This data can be read only. I don't need to update these records.
Can I convert this to a stored procedure?
How hard is this to do?
Will I be able to get noticeable performance gains?
One of the slower parts of the database query is the transport of the selected data from the DBMS to your local process. Hence it is wise to select only the properties you actually plan to use.
For example, it seems that an Order has zero or more ItemAttributes. Evey ItemAttribute belongs to exactly one Order, using a foreign key OrderId.
If you fetch all Orders with Id in ArryOrderId, each order with its thousand ItemAttributes, you know that every ItemAttribute will have a foreign key OrderId with the same value as the Id of the Order that it belongs to. It is a waste to send 1000 times the same value.
When querying data using entity framework, always use Select. Select only the properties yo actually plan to use. Only use Include if you intend to change the fetched objects.
var result = dbContext.Orders
.Where(order=>aryOrderId.Contains(order.id)
.Select(order => new
{ // select only the properties you plan to use:
Id = order.Id,
...
Customer = order.Customer.Select(customer => new
{ // again: only the properties you plan to use
Id = order.Customer.Id,
Name = order.Customer.Name,
...
},
ItemAttributes = order.ItemAttributes.Select(itemAttribute => new
{
...
})
.ToList(),
Dimensions = order.Dimensions.Select(dimension => new
{
...
})
.ToList(),
....A Bunch more.....
})
.ToList();
If after selecting only the properties that you actually plan to use, the query still takes too long, think again: do I really need all these properties.
Another solution to limit the execution time is fetching the date 'per page', using Skip / Take. The danger is of course that when you are viewing page 10, the data of page 1 might be changed in a way that page 10 should be interpreted differently.
As jtate mentions, if you don't need everything from the joined tables, don't include them. Instead, utilize .Select() to retrieve just the data you want from the entity and it's associated relationships.
I.e.
var query = dbContext.Orders
.Where(x => aryOrderId.Contains(x => x.OrderId))
.Select(x => new
{
x.OrderId,
x.OrderNumber,
OrderItems = x.Items.Select(i => new
{
i.ItemId,
Attributes = i.Attributes.Select(a => a.AttributeName).ToList(),
Dimensions = i.Dimensions.Select(d => new {d.DimensionId, d.Name}).ToList(),
}).ToList(),
// ...
}).ToList();
You can structure the query, or queries however you like to find an optimal result.
Alternatively you can consider utilizing a view on the database and binding an entity to the view. This option works well for read-only views of data. Provided you include the relevant IDs you can always retrieve the applicable "real" entities at any time to load a details page or perform an action/update against the entity.
Answering your 3 questions. Yes, you can use a stored procedure and that's what I would do in this situation. It is not hard at all; EF makes it quite simple. You can either have it return a new complex type or you can map it to an entity. Since you're saying the data can be readonly, you probably are okay with a basic function import returning a complex type (EF's default behavior). Either way, you will have noticeable performance gains.
For db-first, see http://www.entityframeworktutorial.net/stored-procedure-in-entity-framework.aspx
Basically, you'll follow these steps.
Create the stored procedure on your database
Update the model from the database. When it asks which objects to include, you should be able to select your stored procedure.
Click Finish. EF will generate a complex type that has all the properties returned by your stored procedure, and it will add a signature to your context for executing the stored procedure, so it can be called like this: var results = myContext.myProcedure(param1, param2); There are screenshots of this at the link above.
You can also go in and modify the model to customize the details, such as the name of the complex type and the name of the function (by default the function will match the name of the SP and will return an ObjectResult<T> where T is your complex type, which will be the name of the procedure with "_Result" as a suffix).

Restricting lazy loading while automapping EF entity type

Folks,
I am mapping EF objects to simple Pocos using automapper in my service layer. I have certain entities that have many relationships in EF,but I want to restrict how much of this gets pulled back from the db. One of these entities would map to a database table but would have many relationships to other tables that would appear as entity collections in the generated model (EDMX).
So I have created a "shallow" poco for the entity which only has poco properties for the first level of properties in the entity, i.e. some integer Ids instead of associated collections/entity collections. I map in the following manner....
var simplepocoforentity= Mapper.Map(_readOnlyDb.Single<EfEntity>(x => x.Id== Id),
new SimplPocoForEntity());
My question is.. because I am only mapping the entity to a simple poco here, can I be confident that EF will not try and query other non referenced data from the underlying db tables relationships when I do the mapping?
I know that I can investigate this via SQL Profiling, but I would appreciate any input befor going down that route.
Thanks
K.
It depends on the internal implementation of AutoMapper. I would assume that AutoMapper does not try to access the navigation properties of the entity (in which case lazy loading and an additional database query would kick in). I don't see a reason why AutoMapper should do this if you only map scalar properties. But who knows...
If you want to be on the safe side you could disable lazy loading temporarily:
try
{
_readOnlyDb.ContextOptions.LazyLoadingEnabled = false;
var simplepocoforentity = Mapper.Map(_readOnlyDb.Entities
.Single(x => x.Id == Id), new SimplPocoForEntity());
// ...
}
finally
{
_readOnlyDb.ContextOptions.LazyLoadingEnabled = true;
}
As a side note: Be aware that you load the full entity into the context with this approach. After that the mapping happens. Potentially you load more columns from the database than you really need (perhaps not in your special case here). In general I would do the mapping with a projection which ensures that the database only queries the columns which are needed:
var simplepocoforentity = _readOnlyDb.Entities
.Where(e => e.Id == Id)
.Select(e => new SimplPocoForEntity
{
PocoProperty1 = e.EntityProperty1,
PocoProperty2 = e.EntityProperty2,
PocoProperty3 = e.EntityProperty3
// etc.
})
.Single();
With this approach no lazy loading would happen at all because you don't load the entity but directly the PocoForEntity from the database.

Access the property used in mapping entity to a table in EFv4

When we have two entities in EFv4 EDM diagram and only one table for both in the database (for instance, having table Documents and entities Invoice and Qoute), table Documents having documentTypeId column as a discriminator and set this column as a discriminator in the EDM (in Table mappings), how do we read the value of this property in our code?
We cannot assign values to it because EF does it for us under the hood (based on what we entered in Table mappings for condition) but somehow I don't get it why we are also not allowed to read it.
Imo this property is already mapped so you can't map it again. It is used to determine type of materialized entity. Why do you need such column. Usually it is enough to use is operator like:
var document = context.Documents.GetById(id);
if (document is Invoice)
{
...
}
If you only need to select subtypes you can use OfType extension method like:
var invoices = context.Documents.OfType<Invoice>().ToList();
You also don't need to set this value when adding new entity because you are adding subtype - Invoice or Quote.
Edit:
As I understand from your comment you don't need this information in query. In such case you don't need to map it. Simply use partial class of your entity and add custom property which will return your string. Sound like stupid solution but actually it would be the easiest one.
Discriminator column should be part of mapping metadata so in case of T4 template generating your entities, it could be possible to update the template so it generate such property for you.
You may want to use a single-table inheritance hierarchy, as described here.
That way, you could have an abstract Document class that includes a DocumentTypeId column. Invoices and Quotes would extend this class, but specify certain DocumentTypeId filters. However, because the original class has a DocumentTypeId column, they would each have that column as well.
Another advantage to this approach is that you could create utility methods that can act on any Document, and you could pass any Invoice or Quote to these methods.

Using Include() with inherited entities

In EF eager loading related entities is easy.
But I'm having difficulties including inherited entities when loading data using table-per-type model.
This is my model:
Entities:
ArticleBase (base article entity)
ArticleSpecial (inherited from ArticleBase)
UserBase (base user entity)
UserSpecial (inherited from UserBase)
Image
Relations are as shown on the image (omitting many columns):
In reality my users are always of type UserSpecial, since UserBase is used in another application, thus we can share credentials. That's the only reason I have two separate tables. UserBase table can't be changed in any way shape or form, because the other app would break.
Question
How am I suppose to load ArticleSpecial with both CreatedBy and EditedBy set, so that both are of type UserSpecial (that defines Image relation)?
I've tried (unsuccessfully though) these options:
1.
Using lambda expressions:
context.ArticleBases
.OfType<ArticleSpecial>()
.Include("UserCreated.Image")
.Include("UserEdited.Image");
In this case the problem is that both CreatedBy and EditedBy are related to UserBase, that doesn't define Image navigation. So I should somehow cast these two to UserSpecial type like:
context.ArticleBases
.OfType<ArticleSpecial>()
.Include("UserCreated<UserSpecial>.Image")
.Include("UserEdited<UserSpecial>.Image");
But of course using generics in Include("UserCreated<UserSpecial>.Image") don't work.
2.
I have tried using LINQ query
var results = from articleSpecial in ctx.ArticleBase.OfType<ArticleSpecial>()
join created in ctx.UserBase.OfType<UserSpecial>().Include("Image")
on articleSpecial.UserCreated.Id equals created.Id
join edited in ctx.UserBase.OfType<UserSpecial>().Include("Image")
on articleSpecial.UserEdited.Id equals edited.Id
select articleSpecial;
In this case I'm only getting ArticleSpecial object instances without related properties being set. I know I should select those somehow, but I don't know how?
Select part in my LINQ could be changed to something like
select new { articleSpecial, articleSpecial.UserCreated, articleSpecial.UserEdited };
but images are still not loaded into my context. My joins in this case are barely used to filter out articleSpecial results, but they don't load entities into context (I suppose).
This seems to be a limitation in the current version of Entity Framework (1.0) Have a look at this related SO question.
In your case including the related UserCreated and UserEdited properties in the projection is the right solution. However if you also want to populate the Image property on the UserSpecial object, you must be sure to include that as well:
var results = from articleSpecial in ctx.ArticleBase.OfType<ArticleSpecial>()
select new
{
articleSpecial,
articleSpecial.UserCreated,
((UserSpecial)articleSpecial.UserCreated).Image,
articleSpecial.UserEdited,
((UserSpecial)articleSpecial.UserEdited).Image
};
Of course this query builds on the assumption that all ArticleSpecial entities always refer to a UserSpecial entity, otherwise the casting will fail.
If this assumption isn't always true, you could express the same query using the LINQ extension methods and a multi-line lambda function to perform a safe casting:
var results = ctx.ArticleBase
.OfType<ArticleSpecial>()
.AsEnumerable()
.Select(a =>
{
var userCreated = a.UserCreated as UserSpecial;
if (userCreated != null)
{
var image = userCreated.Image;
}
var userEdited = a.UserEdited as UserSpecial;
if (userEdited != null)
{
var image = userEdited.Image;
}
return a;
});
In the latter example, you also do not need to include UserSpecial and Image entities in the results. Instead you just need to access the navigation properties on the ArticleSpecial entities during the projection phase in order to force Entity Framework to eager load the related objects.