I am a loading a entity from the database like so
var profileEntity = await Context.Profiles
.Include(x => x.MedicalRecords)
.Include(x => x.DrugHistory)
.Include(x => x.EmploymentStatus)
.SingleOrDefaultAsync(x => x.Id == id);
All is working fine, I was just wondering if there is a better way to include its non generic type properties rather using the Include method because this particular entity has a lot of properties I need to include
It is not possible to automatically eagerly load those properties (Mechanism for statically defining eager loading for navigation properties), but you can create a reusable extension method for this purpose:
public static IQueryable<Profile> IncludeAll(this IQueryable<Profile> query)
{
return query.Include(x => x.MedicalRecords)
.Include(x => x.DrugHistory)
.Include(x => x.EmploymentStatus);
}
Which can be used in a following way:
var profileEntity = Context.Profiles.IncludeAll().SingleOrDefault(x => x.Id == id);
One option is to consider turning off lazy loading for those particular navigation properties by making them not virtual. Example below from the MSDN page.
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Tags { get; set; }
public ICollection<Post> Posts { get; set; }
}
See the section titled Turning off lazy loading for specific navigation properties at this link for reference.
Related
I have a class of Agent
public class Agent
{
public string Id { get; set; }
public string Name { get; set; }
public List<AgentUser> Users { get; set; }
}
public class User
{
public string Id { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class AgentUser
{
public string Id { get; set; }
public string AgentId { get; set; }
public Agent Agent { get; set; }
public string UserId { get; set; }
public User User { get; set; }
}
the relationship is 1-to-many. on the AgentUsers table, it only contains UserId. I want to know how to load the user details(email, firstname, lastName) when I call the Agents table.
This is my query, but it only contains UserId.
var result= await _context.Agents
.Include(au => au.Users)
.Where(x => x.Id.Equals(request.Id))
.AsNoTracking()
.FirstOrDefaultAsync();
Yes I can do a JOIN but How can I get the details from User table in the most efficient way using the FK only?
Thanks!
If you want to use the joining entity in the relationships like your example then the other option when reading data is to use projection rather than loading the entire entity graph:
var result= await _context.Agents
.Where(a => a.Id.Equals(request.Id))
.Select(a => new AgentSummaryViewModel
{
AgentId = a.Id,
// other details from agent...
Users = a.Users.Select(u => new UserSummaryViewModel
{
UserId = u.User.Id,
Email = u.User.EMail,
// ...
}).ToList()
})
.SingleOrDefaultAsync();
This can be simplified using Automapper and it's ProjectTo() method against an EF IQueryable once a mapper is configured with how to build an Agent and User ViewModel. Projection has the added advantage of just loading as much data from the entity graph as your consumer (view, etc.) needs, building efficient queries. There is no need to eager load related entities or worry about tracking references since the entities themselves are not being loaded.
If you want to load that Agent and their Users for editing purposes then:
var agent = await _context.Agents
.Include(a => a.Users)
.ThenInclude(u => u.User)
.Where(a => a.Id.Equals(request.Id))
.SingleOrDefaultAsync();
Alternatively, with Many-to-Many relationships, if the joining table can be represented with just the two FKs as a composite PK then EF can map this table by convention without needing to declare the AgentUser as an entity. You only need to define the AgentUser if you want other details recorded and accessible about the relationship. So for example if you want to use a soft-delete system with an IsActive flag or such and removing an association between agent and user resulted in that record being made inactive rather than deleted, you would need an AgentUser entity.
So if you can simplify your AgentUser table to just:
AgentId [PK, FK(Agents)]
UserId [PK, FK(Users)]
... then your Entity model can be simplified to:
public class Agent
{
public string Id { get; set; }
public string Name { get; set; }
public List<User> Users { get; set; }
}
The entity mapping would need to be added explicityly such as with OnModelCreating or via an IEntityTypeConfiguration implementation that is registered so that:
modelBuilder.Entity<Agent>()
.HasMany(x => x.Users)
.WithMany(x => Agents);
Unfortunately with EF Core, many-to-many relationships mapped this way require navigation properties on both sides. (Agent has Users, and User has Agents)
EF should be able to work out the linking table by convention, but if you want more control over the table name etc. you can explicitly tell it what to use. This is where it can get a little tricky, but you can either do it by defining a joining entity, or without one, using a "shadow" entity. (Essentially a dictionary)
modelBuilder.Entity<Agent>()
.HasMany(x => x.Users)
.WithMany(x => Agents);
.UsingEntity<Dictionary<string, object>>(
"AgentUsers",
l => l.HasOne<Agent>().WithMany().HasForeignKey("AgentId"),
r => r.HasOne<User>().WithMany().HasForeignKey("UserId"),
j =>
{
j.HasKey("AgentId", "UserId");
j.ToTable("AgentUsers"); // Or "UserAgents", or "AgentUserLinks" etc.
});
Edit: A simple example with Automapper...
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Agent, AgentSummaryViewModel>();
cfg.CreateMap<User, UserSummaryViewModel>();
});
var result= await _context.Agents
.Where(a => a.Id.Equals(request.Id))
.ProjectTo<AgentSummaryViewModel>()
.SingleOrDefaultAsync();
"config" above can be extracted from a global Mapper set up with all of your mapper configurations, created ad-hoc as needed, or what I typically do is derive from mapping configuration expressions built as static methods in the view models themselves:
var config = new MapperConfiguration(AgentSummaryViewModel.BuildMapperConfig());
... then in the AgentSummaryViewModel:
public static MapperConfigurationExpression BuildConfig(MapperConfigurationExpression config = null)
{
if (config == null)
config = new MapperConfigurationExpression();
config = UserSummaryViewModel.BuildMapperConfig(config);
config.CreateMap<Agent, AgentSummaryViewModel>()
.ForMember(x => x.Users, opt => opt.MapFrom(src => src.Users.Select(u => u.User));
// append any other custom mappings such as renames or flatting from related entities, etc.
return config;
}
The UserSummaryViewModel would have a similar BuildMapperConfig() method to set up converting a User to UserSummaryViewModel. The resulting mapper configuration expression can chain as many needed related view models as you need, located under their respective view models.
I am working on mapping a few database entities for a reporting tool.
At the moment, there are a few computed properties depending on navigation properties for their loading. They've been bound through AutoMapper to ease the process.
public class Customer
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Foo> Foos { get; set; }
public virtual ICollection<Bar> Bars { get; set; }
}
public class CustomerDto
{
public long Id { get; set; }
public string Name { get; set; }
public long TotalNumberOfFoos { get; set; }
public long NumberOfBarsWithCondition { get; set; }
}
public class CustomerProfile : Profile
{
public CustomerProfile()
{
CreateMap<Customer, CustomerDto>()
.ForMember(d => d.TotalNumberOfFoos, p => p.MapFrom(c => c.Foos.Count))
.ForMember(d => d.NumberOfBarsWithCondition, p => p.MapFrom(c => c.Bars.Where(b => b.BarProperty == "something").Count()));
}
}
public class CustomerController : Controller
{
public async Task<List<CustomerDto>> CustomersByName(string name)
{
using (var db = new MyDbContext())
{
return await db.Customers
.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider)
.Where(c => c.Name == name).ToListAsync();
}
}
}
Of course, the queries to retrieve these properties can become quite expensive as the size of the database increases, and they're not always needed in the final report.
The idea is to have an option for the user to choose if they want them included or not in the final report, but I haven't found a way to make the mapping optional at query time.
Is there a way to do this automatically, or am I forced to materialize the list and query these properties myself separately, losing the advantage of having computed properties from the database?
What you need is to utilize the so called AutoMapper Explicit expansion feature. Which should probably be called "explicit property inclusion" (not to be mixed with EF Core Include which is only for navigations), because it works for any destination property, and what it does it to rather include it automatically in the generated projection (Select), include it only when you opt-in explicitly.
So, you need first to configure such properties as ExplicitExpansion(), e.g.
CreateMap<Customer, CustomerDto>()
.ForMember(d => d.TotalNumberOfFoos, p =>
{
p.MapFrom(c => c.Foos.Count);
p.ExplicitExpansion();
})
.ForMember(d => d.NumberOfBarsWithCondition, p =>
{
p.MapFrom(c => c.Bars.Where(b => b.BarProperty == "something").Count());
p.ExplicitExpansion();
});
Now by default they won't be populated. Use the additional arguments of ProjectTo to pass which ones you want to "expand" (include), e.g.
.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider, e => e.TotalNumberOfFoos)
I have a simple problem - I would like one of the RESTful endpoints serve a resource DTO (auto-mapped) with its related resources as their IDs only. However there does not seem to be any way to implement it without loading the whole(and heavy) related entities. Consider following (DB first) example model:
public partial class Blog
{
public int Id { get; set; }
public string Url { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public partial class Post // some heavy entity
{
public int Id { get; set; }
public string Content { get; set; }
// other properties
}
and its corresponding DTO
// api/v1/blogs serves collection of following type
public class BlogSlimDto
{
public int Id { get; set; }
public string Url { get; set; }
public int[] PostIds { get; set; }
}
a straightforward soltion would be to fetch all the related Posts from database and discard all data except for the IDs, but that can be inefficient or even unfeasible depending on related Post entity size:
var result = ctx.Blogs.Include(blog => blog.Posts) //fecth everything and discard it on next line
.Select(blog => _mapper.Map<BlogSlimDto>(blog));
// simply use a profile that discards Posts but keeps their Ids, e.g.
// .forMember(dto => dto.PostIds, opt => opt.MapFrom(db.Posts.Select(p => p.Id)))
there is similar question which offers a solution using anonymous types, however this does not play well with Automapper at all:
var result = ctx.Blogs.Select(blog => new {
blog.Id,
blog.Url,
PostIds = blog.Posts.Select(b => b.Id),
}).Select(ablog => _mapper.Map<BlogSlimDto>(ablog)); //throws, no mapping and such mapping cannot be defined
The code above will throw during runtime because there no Automapper mapping defined. Even worse, it cannnot be defined because there is no support for anonymous types in Automapper. Moreover, solutions with one-by-one 'manual' property assignment tend to be difficult to maintain.
Is there an alternative solution that would allow EF query without fetching whole related entities while allowing the result to be auto-mapped to the BlogSlimDto?
You can use the queryable extensions:
https://docs.automapper.org/en/stable/Queryable-Extensions.html
var configuration = new MapperConfiguration(cfg =>
cfg.CreateMap<OrderLine, OrderLineDTO>()
.ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name)));
public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
using (var context = new orderEntities())
{
return context.OrderLines.Where(ol => ol.OrderId == orderId)
.ProjectTo<OrderLineDTO>().ToList();
}
}
Replacing the Item and OrderLine with your Post and Blogs
I started working on an ongoing project and it has a many-to-many relationship in the database and in some parts of the code too, but I realized that even the relationship being many-to-many in the model there is always only one line linking the two entities (confirmed with the author). This is what I mean: The two entities are task and task list and a task only belongs to a task list. Models below:
public class ProjectTask
{
public long Id { get; set; }
// other non related properties
}
public class ProjectTaskList
{
public long Id { get; set; }
public DateTime? DateEnd { get; set; }
// other non related properties
}
// link between task list and task
public class ProjectTaskListTask
{
public long ProjectTaskId { get; set; }
public ProjectTask ProjectTask { get; set; }
public long ProjectTaskListId { get; set; }
public ProjectTaskList ProjectTaskList { get; set; }
public int Order { get; set; }
}
And its configuration in the OnModelCreating method of the context class:
modelBuilder.Entity<ProjectTaskListTask>()
.HasKey(a => new { a.ProjectTaskId, a.ProjectTaskListId });
modelBuilder.Entity<ProjectTaskListTask>()
.HasOne(u => u.ProjectTaskList)
.WithMany(u => u.Tasks)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
My problem is: In some parts of my code I need to know the Task List of a task, I need to use it in Where queries to do some validations, like : Tasks.Where(p => p.TaskList.DateEnd == null).
How can I add a Not Mapped property to the ProjectTask entity so I could do that? I'm using Entity Framework Core 2.
Thanks for any help
Without changing the underlying data structure, could you query ProjectTaskListTask? Something along the lines...?
ProjectTaskListTask
.Include(p => p.ProjectTaskList)
.Include(p => p.ProjectTask)
.Where(p => p.ProjectTaskList.DateEnd == null)
.Select(p => p.ProjectTask);
I´m using EF6 and trying to eager fetch the whole structure of an object. The problem is that i´m using inheritance.
Let´s say that i have this classes.
DbContext
DbSet<A> A { get; set; }
Example classes
public class A
{
public string Id { get; set; }
public IList<Base> Bases { get; set; }
}
public abstract class Base
{
public int Id { get; set; }
public string Name { get; set; }
}
public abstract class Base1 : Base
{
public SomeClass SomeClass { get; set; }
}
public class Base2 : Base1
{
}
public class Base3 : Base1
{
public SomeOtherClass SomeOtherClass { get; set; }
}
The error i get is:
The Include path expression must refer to a navigation property defined on the type.
Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Why doesn´t it work with the following ?
public IEnumerable<A> GetAll(string id)
{
return _ctx.A
.Include(x => x.Bases.OfType<Base1>().Select(y=>y.SomeClass))
.Where(x => x.Id.Equals(id)).ToList();
}
New example
public IEnumerable<A> GetAll(string id)
{
var lists = _dbContext.A.Where(x => x.Id == id);
lists.SelectMany(a => a.Bases).OfType<Base1>().Include(e=>e.SomeClass).Load();
lists.SelectMany(b => b.Bases).OfType<Base3>().Include(e => e.SomeOtherClass).Load();
return lists;
}
EDIT: Added a new example that seems to work.
Shortly, it's not possible out of the box.
The only workaround I can suggest is to materialize the master query result, then execute several OfType queries with the necessary Includes using the same filter as the master query, and rely on EF navigation property fixup.
It requires an inverse navigation property in the Base class:
public abstract class Base
{
// ...
public A A { get; set; }
}
Then you can use something like this:
public IEnumerable<A> GetAll(string id)
{
var a = _ctx.A.Where(x => x.Id == id).ToList();
_ctx.Base.OfType<Base1>().Include(e => e.SomeClass).Where(e => e.A.Id == id).Load();
_ctx.Base.OfType<Base3>().Include(e => e.SomeOtherClass).Where(e => e.A.Id == id).Load();
return a;
}
The same idea can be used w/o inverse navigation property but with using the returned base Ids as filter:
public IEnumerable<A> GetAll(string id)
{
var a = _ctx.A.Include(e => e.Bases)
.Where(x => x.Id == id).ToList();
var baseIds = a.SelectMany(e => e.Bases.OfType<ModelA.Base1>().Select(b => b.Id));
db.Base.OfType<Base1>().Include(e => e.SomeClass)
.Where(e => baseIds.Contains(e.Id)).Load();
baseIds = a.SelectMany(e => e.Bases.OfType<Base3>().Select(b => b.Id));
db.Base.OfType<Base3>().Include(e => e.SomeOtherClass)
.Where(e => baseIds.Contains(e.Id)).Load();
return a;
}
Your problem is not in the Select(y=>y.SomeClass) it self, if you try to remove it from your query and execute your query again, you will get same problem. You cannot query the inherited type as child and you expect from entity framework to take care for everything.
If you look to your database, the table Base has a reference to A which is relation 1-many from A to Base.
you can either get all the Base entities where A.Id = something, by adding a navigational property A in the class Base, and in your DbContext you add DbSet<Base> Bases{get;set;} then your query will look like this
var details = _ctx.Bases.OfType<Base1>()
.Include(t=>t.Box)
.Include(t=>t.SomeClass)
.Where(t=>t.Box.Id ==something);
Other option, to use a DTO, in the below sample I used Anonymous type, but you can create a strongly DTO typed to meet your requirements.
var details = _ctx.A
.Where (t=>t.Id ==something)
.Select(a => new {
Id = a.Id,
// ... other A properites ,
Bases = _ctx.Bases.OfType<Base1>().Select(m=> new {
Id = m.Id,
Name = m.Name,
SomeClass = m.SomeClass
});
}
Hope this will help you