Entity Framework not including children in join - entity-framework

I am using inner join to return results with Entity Framework (v6.2.0) and the following code is not returning the RouteWaypoints children (i.e. route.RouteWaypoints is always null). Interestingly, single children are loading (Customer, OriginLocation, etc), but not multiple children:
public List<Route> GetAllForTripWithWaypoints(int tripId)
{
return (
from route in GetAllBaseWithWaypoints()
from tripTask in DbContext.TripTasks.Where(x =>
x.TripId == tripId && x.OriginLocationId == route.OriginLocationId)
select route
).ToList();
}
private IQueryable<Route> GetAllBaseWithWaypoints()
{
return DbContext.Routes
.Include(x => x.Customer)
.Include(x => x.OriginLocation)
.Include(x => x.DestinationLocation)
.Include(x => x.RouteWaypoints.Select(y => y.Location))
.OrderBy(x => x.OriginLocation.Name).ThenBy(x => x.DestinationLocation.Name)
.AsQueryable();
}
This approach does work if I load just the Route entity, but not when I do the join. As a reference, this does load the children successfully:
public Route GetByIdWithWaypoints(int id, bool validateExists = true)
{
var route = GetAllBaseWithWaypoints().FirstOrDefault(x => x.Id == id);
if (validateExists && route == null)
throw new Exception("Route not found for id: " + id);
return route;
}
How can I keep it working when joining?

I did an imperfect workaround by making two calls to the db - slightly less efficient, but it solves the problem:
public List<Route> GetAllForTripWithWaypoints(int tripId)
{
var routeIds = (
from route in GetAllBase()
from tripTask in DbContext.TripTasks.Where(x =>
x.TripId == tripId && x.OriginLocationId == route.OriginLocationId)
select route.Id
).ToList();
return GetAllBaseWithWaypoints().Where(x => routeIds.Contains(x.Id)).ToList();
}

Related

Get all the roles after include with main entity in Entity framework Core

I have three entities which are Account, Role and AccountRole. Through repository pattern I have fetched all the accounts, roles and accountRoles like below:
var accounts = _accountRepository.Query()
.Include(x => x.AccountRoles)
.ThenInclude(x => x.Role);
Now I would like to get a single account information with all it's roles. So I have written the below query. But the problem is only first role comes out.
var userAccount = accounts.Where(x => x.Id == id)
.Select(x => new AccountDto
{
Id = x.Id,
Name = x.UserFullName,
FirstName = x.FirstName,
LastName = x.LastName,
Email = x.Email,
Mobile = x.Mobile,
UserName = x.UserName,
PhotoUrl = x.PhotoUrl,
IsActive = x.IsActive,
Roles = accounts.Where(x => x.Id == id).Select(x => new RoleDto
{
Title = x.AccountRoles
.Where(x => x.IsActive == true)
.Select(x => x.Role.Title).FirstOrDefault()
}).ToList()
}).FirstOrDefault();
Can anyone help me to write the proper query?
First, this part
Roles = accounts.Where(x => x.Id == id).Select(x => new RoleDto
{
Title = x.AccountRoles
.Where(x => x.IsActive == true)
.Select(x => x.Role.Title).FirstOrDefault()
}).ToList()
is quite strange mixture, you should simply use the navigation property (yes, it is not only for includes as some people think).
Second, don't apply FirstOrDefault() when you want to get all items rather than the first only
// here x variable type is Account (coming from the outer Select)
Roles = x.AccountRoles
.Where(ar => ar.IsActive)
.Select(ar => new RoleDto
{
Title = ar.Role.Title,
}).ToList()

How to filter and include children using Z.EntityFramework

The query below is using Z.EntityFramework.Plus.EF6 to filter children. Its filtering and including the AssetXref entity correctly however the result is not able to include Child.Parent entity relationship
var result = await _repository.GetQuery<Assets>()
.IncludeFilter(x => x.AssetsXRef
.Where(y => y.Child.Perent.ParentID == parentID)
.Select(y => y.Child.Perent)
)
.Where(x => x.Active == true)
.ToListAsync();
I have also tried
var result = await _repository.GetQuery<Assets>()
.IncludeFilter(x => x.AssetsXRef
.Where(y => y.Child.Perent.ParentID == parentID)
)
.Include(x=>x.AssetsXRef.Select(y=>y.Child.Parent))
.Where(x => x.Active == true)
.ToListAsync();
The IncludeFilter is not compatible with Include. You need to use IncludeFilter all the way even if there is no filter
In addition, you need to always use the full parent filter on child
Here is an example:
var result = await _repository.GetQuery<Assets>()
.IncludeFilter(x => x.AssetsXRef.Where(y => y.Child.Perent.ParentID == parentID))
// Might be SelectMany
.IncludeFilter(x => x.AssetsXRef.Where(y => y.Child.Perent.ParentID == parentID).Select(y=>y.Child.Parent))
.Where(x => x.Active == true)
.ToListAsync();

How to simplify transforming entity framework entities to DTOs

I map to a data transformation object when retrieving items from an ASP.NET Web API like so for a list:
public async Task<IList<PromotionDTO>> GetPromotionsList()
{
return await _context.Promotions
.Select(p => new PromotionDTO
{
PromotionId = p.PromotionId,
Is_Active = p.Is_Active,
Created = p.Created,
Title = p.Title,
BusinessName = p.BusinessName,
})
.Where(x => x.Is_Active)
.OrderByDescending(x => x.Created)
.ToListAsync();
}
And like this for getting a single record:
public async Task<PromotionDTO> GetPromotion(int id)
{
return await _context.Promotions
.Select(p => new PromotionDTO
{
PromotionId = p.PromotionId,
Is_Active = p.Is_Active,
Created = p.Created,
Title = p.Title,
BusinessName = p.BusinessName,
})
.Where(x => x.Is_Active && x.PromotionId == id)
.FirstOrDefaultAsync();
}
I'm new to DTO's and I find that I'm using the same DTO transformation code at many places, and was wondering how I can simplify my code to only do this once?
Though it may be enough to map like you've stated, but when your project starts to grow it will just complicated things and cause additional work.
I suggest that you use some kind of mapping library like AutoMapper.
https://github.com/AutoMapper/AutoMapper
static MyRepositoryConstructor()
{
// Define your maps
Mapper.Initialize(cfg => {
cfg.CreateMap<PromotionEntity, PromotionDTO>();
});
}
public async Task<IList<PromotionDTO>> GetPromotionsList()
{
return Mapper.Map<IList<PromotionDTO>>(await _context.Promotions
.Where(x => x.Is_Active)
.OrderByDescending(x => x.Created)
.ToListAsync()
);
}
public async Task<PromotionDTO> GetPromotion(int id)
{
return Mapper.Map<PromotionDTO>(await _context.Promotions
.Where(x => x.Is_Active && x.PromotionId == id)
.FirstOrDefaultAsync()
);
}
One option is to create a method which returns an IQueryable and then use that in each
Private IQueryable<PromotionDTO> Query()
{
return _context.Promotions
.Select(p => new PromotionDTO
{
PromotionId = p.PromotionId,
Is_Active = p.Is_Active,
Created = p.Created,
Title = p.Title,
BusinessName = p.BusinessName,
});
}
public async Task<IList<PromotionDTO>> GetPromotionsList()
{
return await Query()
.Where(x => x.Is_Active)
.OrderByDescending(x => x.Created)
.ToListAsync();
}
public async Task<PromotionDTO> GetPromotion(int id)
{
return await Query()
.Where(x => x.Is_Active && x.PromotionId == id)
.FirstOrDefaultAsync();
}

EF send Includes list to repository via parametr

I at this moment I have repository filled with multiple gets methods.
E.q. Get1() => cxt.Entites.Include(e => e.obj1);
Get2() => cxt.Entities.Include(e => e.obj1).Include(e => e.obj2)
And so on.
Is there good method, pattern to have one GET method where I can send inclues via parameter?
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
See repository pattern in msdn
You can use
_sampleRepostiory.Get(h=>h.Id>1,null,"Employees.Departments");
Including same with lambda
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
Expression<Func<TEntity, object>>[] includes)
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
if (includes != null)
{
query = includes.Aggregate(query,
(current, include) => current.Include(include));
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
Consume it like this
var query = context.Customers
.Get(x=>x.Id>1,null,
c => c.Address,
c => c.Orders.Select(o => o.OrderItems));
Similar SO question
I did the following in my projects:
public Entity[] GetAll(bool includeObj1, bool includeAllOthers) {
IQueryable<Entity> entity = ctx.Entities;
if (includeObj1)
entity = entity.Include(e => e.obj1);
if (includeAllOthers) {
entity = entity
.Include(e => e.obj2)
.Include(e => e.obj3)
.Include(e => e.obj4)
.Include(e => e.obj5);
}
return entity.ToArray();
}
Providing arguments like includeObj1 and includeObj2 separates a consumer of repository from implementation and encapsulates any data access logic.
Passing direct "include these properties" orders to a repository means that you know how repository works and assume that it is some sort ORM which blurs abstractions.

Entity Framework - How to pass Include off to method caller?

I have a method that calls a DbSet from an Entity Framework database:
public static List<CostEntryVM> ToViewModelList(this DbSet<CostEntry> CostEntrys, Expression<Func<CostEntry, bool>> query) {
return AutoMapper.Mapper.Map<List<CostEntry>, List<CostEntryVM>>(
CostEntrys
.Include(x => x.Job)
.Include(x => x.User)
.Where(query)
.ToList());
}
To use this I can then do for example:
CostEntrys.ToViewModelList(x => x.Active == true);
I want to also be able to call:
CostEntrys.ToViewModelList(x => x.Include(y => y.Job).Include(y.User), x => x.Active == true);
I can't for the life of me figure out how the method signature should look or how I would then apply that to the DbSet.
How can I do this?
First you need to change the extension method to:
public static List<CostEntryVM> ToViewModelList(
this DbSet<CostEntry> CostEntrys,
Expression<Func<CostEntry, bool>> query,
Func<IQueryable<CostEntry>, IQueryable<CostEntry>> func)
{
// Adding the predicate query
IQueryable<CostEntry> queryable = CostEntrys.Where(query);
// Adding include paths
IQueryable<CostEntry> queryableWithFetch = func(queryable);
// Executing the query and map it to the view model object
return AutoMapper.Mapper.Map<List<CostEntry>, List<CostEntryVM>>(
queryableWithFetch.ToList());
}
And then you can call it:
CostEntrys.ToViewModelList(
x => x.Active == true,
x => x.Include(y => y.Job).Include(y.User));