Ef Including Other Entities (Generic Repository Pattern)

EF Including Other Entities (Generic Repository pattern)

Use just the Include extension on IQueryable. It is available in EF 4.1 assembly. If you don't want to reference that assembly in your upper layers create wrapper extension method in your data access assembly.

Here you have example:

public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] includes)
where T : class
{
if (includes != null)
{
query = includes.Aggregate(query,
(current, include) => current.Include(include));
}

return query;
}

You will use it for example like:

var query = context.Customers
.IncludeMultiple(
c => c.Address,
c => c.Orders.Select(o => o.OrderItems));

This query will load all customers with their addresses and orders and every order will contain its order items.

EF Core Including Other Entities (Generic Repository pattern)

Solved :

following this answer link
I added this code that parse my lambda expression of includes :

    // This method is a slight modification of EF6 source code
private bool TryParsePath(Expression expression, out string path)
{
path = null;
var withoutConvert = RemoveConvert(expression);
var memberExpression = withoutConvert as MemberExpression;
var callExpression = withoutConvert as MethodCallExpression;

if (memberExpression != null)
{
var thisPart = memberExpression.Member.Name;
string parentPart;
if (!TryParsePath(memberExpression.Expression, out parentPart))
{
return false;
}
path = parentPart == null ? thisPart : (parentPart + "." + thisPart);
}
else if (callExpression != null)
{
if (callExpression.Method.Name == "Select"
&& callExpression.Arguments.Count == 2)
{
string parentPart;
if (!TryParsePath(callExpression.Arguments[0], out parentPart))
{
return false;
}
if (parentPart != null)
{
var subExpression = callExpression.Arguments[1] as LambdaExpression;
if (subExpression != null)
{
string thisPart;
if (!TryParsePath(subExpression.Body, out thisPart))
{
return false;
}
if (thisPart != null)
{
path = parentPart + "." + thisPart;
return true;
}
}
}
}
else if (callExpression.Method.Name == "Where")
{
throw new NotSupportedException("Filtering an Include expression is not supported");
}
else if (callExpression.Method.Name == "OrderBy" || callExpression.Method.Name == "OrderByDescending")
{
throw new NotSupportedException("Ordering an Include expression is not supported");
}
return false;
}

return true;
}

// Removes boxing
private Expression RemoveConvert(Expression expression)
{
while (expression.NodeType == ExpressionType.Convert
|| expression.NodeType == ExpressionType.ConvertChecked)
{
expression = ((UnaryExpression)expression).Operand;
}

return expression;
}

#endregion

Then change my EvaluateInclude function to :

  private IQueryable<TEntity> EvaluateInclude(IQueryable<TEntity> current, Expression<Func<TEntity, object>> item)
{
if (item.Body is MethodCallExpression)
{
string path;
TryParsePath(item.Body, out path);
return current.Include(path);

}

return current.Include(item);
}

And it works

How to add include in Generic repository pattern in Ef Core?

When you want to use GenericRepository you should declare Type when Initializing. if you don't initilize GenericRepository with Explicit Type (like SiteDetail) this is Initializing in your example:

public class SiteDetailService : ISiteService 
{
private readonly IBaseRepository<SiteDetail> _siteDetailRepository;
public SiteDetailService(IBaseRepository<SiteDetail> siteDetailsRepository)
{
_siteDetailRepository = siteDetailsRepository;
}
}

and you can call your repository methods with defined Type:

   var siteDetails = 
await this._siteDetailsRepository
.GetAsync(x =>
x.siteNo == siteNo, //Conditions
null, //Orders
x => x.Country) //Includes
.ConfigureAwait(false);

EF Generic Repository Multiple Includes

You have fallen in the typical trap of calling a method which returns something and ignoring the result. The line entities.Include(include); does nothing - similar to entities.Where(...);, entities.Select(...); etc.

The correct code is something like this:

var query = entities.AsQueryable();
foreach (var include in includes)
query = query.Include(include);
return query;

or with single line Aggregate:

return includes.Aggregate(entities.AsQueryable(), (query, path) => query.Include(path));

What is the relevance of the Generic Repository Pattern in .Net Core with Entity Framework

Without being too presumptuous, I would like to answer my own question based off of the comment thread.

The generic repository pattern is a hold-over from the days before the era of Object-Relational Mappers (ORM's) like Entity Framework, xHibernate, Dapper, PetaPoco and a million others.

With the advent of the ORM, all the features found in the Repository Pattern are encapsulated within the ORM itself.

For instance, Entity Framework by default uses a Transaction/UnitOfWork each time you call .SaveChanges() on the DbContext.

It also automatically tracks changes made to entities returned by a query unless you explicitly tell it not to with UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking).

As far as Generic Repositories are concerned, what do you think the following code is:

MyDbContext.Set<WeatherForecast>().Add(forecast);

.Net Core Entity Framework Generic Repository Pattern - Implement Shared Service With UnitOfWork

Because each entity type is a strongly typed object, you cannot genericize your implementation without reflection. A common workaround is to provide a generic callback while allowing the invoker to provide the specific mapping per entity type.

public class Service<TEntity> : IService<TEntity> where TEntity : class
{
private readonly IUnitOfWork _unitOfWork;
public Service(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}

public async Task<TEntity> AddAsync(TEntity entity,
Func<TEntity, IUnitOfWork, Task> addEntityCallback)
{
await addEntityCallback(entity, _unitOfWork);
await _unitOfWork.CommitAsync();
return entity;
}
}

public interface IService<TEntity> where TEntity : class
{
Task<TEntity> AddAsync(TEntity entity, Func<TEntity, IUnitOfWork, Task> addEntityCallback);
}

You can then call IService.AddAsync with a specific mapping:

public class ProductService : IProductService
{
private readonly IService<Product> _service;
public ProductService(IService<Product> service)
{
_service = service;
}

public async Task<Product> CreateProduct(Product newProduct)
{
await _service.AddAsync(newProduct,
(entity, unitOfWork) => unitOfWork.Products.AddAsync(entity));
return newProduct;
}
}

Update: in the case where you want to always inherit Service<TEntity> (per your comment), you could use an abstract method that functions in a similar manner to a callback parameter. This allows you to still encapsulate the logic in the ProductService but now no longer requires you to provide a create method.

public class ProductService : Service<Product>, IProductService
{
public ProductService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}

protected override async Task<Product> AddEntityCallback(Product entity,
IUnitOfWork unitOfWork)
{
await unitOfWork.Products.AddAsync(entity);
return entity;
}
}

public abstract class Service<TEntity> : IService<TEntity> where TEntity : class
{
private readonly IUnitOfWork _unitOfWork;
public Service(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}

protected abstract Task<TEntity> AddEntityCallback(TEntity entity,
IUnitOfWork unitOfWork);

public async Task<TEntity> AddAsync(TEntity entity)
{
await AddEntityCallback(entity, _unitOfWork);
await _unitOfWork.CommitAsync();
return entity;
}
}


Related Topics



Leave a reply



Submit