Including children of children in C# Generic Repository EF - c#

I am facing a slight problem and hope you could help, basically I would like to get children of children in Generic repository pattern as I have relationship table with multi-multi relationship
My Repository method looks like that:
public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes)
{
var query = _entities.Where(predicate).AsQueryable();
if (includes != null)
{
query = includes.Aggregate(query, (current, include) => current.Include(include));
}
return query;
}
public async Task<IQueryable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes)
{
return await Task.Run(() => Find(predicate, includes));
}
My model:
Product:
public class Product : BaseEntity<long>
{
[MaxLength(100)]
public string Name { get; set; }
[MaxLength(100)]
public string Barcode { get; set; }
public int ShelfLife { get; set; }
public int Weight { get; set; }
public bool HasAllergens { get; set; }
[ForeignKey("Id")]
public int CustomerId { get; set; }
public virtual ICollection<ProductIngredient> ProductIngredient { get; set; }
}
Ingredient:
public class Ingredient : BaseEntity<long>
{
[MaxLength(100)]
public string Name { get; set; }
[ForeignKey("Id")]
public int CustomerId { get; set; }
public virtual ICollection<ProductIngredient> ProductIngredient { get; set; }
}
Relationship:
public class ProductIngredient : BaseEntity<long>
{
[ForeignKey("Id")]
public long? ProductId { get; set; }
[ForeignKey("Id")]
public long? IngredientId { get; set; }
}
What I'd like to achieve is to populate my ProductDto with ProductData and List of ingredients, my current ProductDto looks like:
public class ProductDto
{
public long Id { get; set; }
public DateTime CretedOn { get; set; }
public DateTime UpdatedOn { get; set; }
public string Name { get; set; }
public string Barcode { get; set; }
public int ShelfLife { get; set; }
public int Weight { get; set; }
public bool HasAllergens { get; set; }
public int CustomerId { get; set; }
public IList<IngredientDto> Ingredients { get; set; }
}
I've found that I can use "ThenInclude" to include children of children, just don't know how to implement this to generic repository.
What I can do so far is just take a children then which for example I'm doing like that:
var results = await _productsRepository.FindAsync(p => p.Id == id, p => p.ProductIngredient);
Any help much appreciated.
Thanks!

Related

EF6 - Many-to-One Relationship not working

I have Products, Sub products, and more tables. You can see in the code, relationship not working I want it Product Class relationship with SubProduct but always the collection count is 0.
Product Class:
[Key]
public int Id { get; set; }
public bool Status { get; set; } = true;
public string StockCode { get; set; }
public int StockDecrease { get; set; }
public string Name { get; set; }
public int Amount { get; set; }
public int Desi { get; set; }
public string Barcode { get; set; }
public long Gtin { get; set; }
public string InvoiceName { get; set; }
public string EInvoiceName { get; set; }
public string Subtitle { get; set; }
public byte Kdv { get; set; }
public virtual Category Category { get; set; }
public string Description { get; set; }
public virtual ICollection<SubProduct> SubProducts { get; set; }
public virtual ICollection<ProductVariant> ProductVariants { get; set; }
Sub Product Class:
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public string StockCode { get; set; }
public virtual ProductBrand Brand { get; set; }
public virtual Company Company { get; set; }
public Abstract.Marketplace Marketplace { get; set; }
public string Barcode { get; set; }
public string Title { get; set; }
public string SubTitle { get; set; }
public bool IsConnected { get; set; }
public decimal Price { get; set; }
public decimal PriceDiscount { get; set; }
public string Description { get; set; }
public virtual ICollection<SubProductVariant> SubProductVariants { get; set; }
Repository Base:
public async System.Threading.Tasks.Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> filter)
{
await using var context = new TContext();
return await context.Set<TEntity>().Where(filter).SingleOrDefaultAsync();
}
Make sure to add using System.Data.Entity; to get the version of Include that takes in a lambda.
using System.Data.Entity;
query.Include(x => x.SubProducts)
and for more use ThenInclude or Include extention methods.
To define a method on the repository for this, you can use this example:
public static IQueryable<TSource> GetIQueryableWithIncludes<TSource>(Expression<Func<TSource, object>>[] includeProperties, IQueryable<TSource> result)
{
var newResult = result;
if (includeProperties.Any())
{
foreach (var includeProperty in includeProperties)
{
newResult = newResult.Include(includeProperty);
}
}
return newResult;
}
You can now pass a list of lamba expression like p => p.SubProjects to specify what you want to include.
You can further hide this by making an GetAll method that hides these includes from the outside user of your Domain
I would suggest to first study more about how to handle related entities.
You basically need to tell EF to load them in.
See documentation
Simplest example would something like this:
// Load all products.
var products= context.Products
.Include(b => b.SubProducts)
.ToList();
I solved with .Include() thanks for helping.
public async System.Threading.Tasks.Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> filter, Expression<Func<TEntity,object>> include=null)
{
await using var context = new TContext();
return await context.Set<TEntity>().Where(filter).Include(include).SingleOrDefaultAsync(filter);
}

Include in EF C#

I have three tables:
Member, business and business address
I'm able to fetch the business with member using Include, but I'm trying to get businessAddress, it is asking for direct relationship i guess.
Here is my models:
[Table("member")]
public partial class Member
{
public Member()
{
Business = new HashSet<Business>();
// BusinessAddress = new HashSet<BusinessAddress>();
}
[Key]
public int memberId { get; set; }
public int chapterid { get; set; }
public string title { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public bool IsActive { get; set; }
public string classification { get; set; }
public int SortOrder { get; set; }
public virtual ICollection<Business> Business { get; set; }
// public virtual ICollection<BusinessAddress> BusinessAddress { get; set; }
}
Business
[Table("Business")]
public partial class Business
{
public Business()
{
BusinessAddress = new HashSet<BusinessAddress>();
}
[Key]
public int BusinessID { get; set; }
public int? categoryid { get; set; }
public int? subcategoryid { get; set; }
public int memberid { get; set; }
public string businessname { get; set; }
public string dealingin { get; set; }
public int? sortOrder { get; set; }
[ForeignKey("memberid")]
public Member Member { get; set; }
public ICollection<BusinessAddress> BusinessAddress { get; set; }
}
BusinessAddress
[Table("BusinessAddress")]
public partial class BusinessAddress
{
[Key]
public int businessaddressid { get; set; }
public int businessid { get; set; }
[ForeignKey("businessid")]
public virtual Business Business { get; set; }
public string address { get; set; }
}
Below is my EF query:
var list = _dbContext.Member.Include("Business").OrderByDescending(x => x.SortOrder).ThenBy(x => x.firstname).ToList();
I want to get Member with business and BusinessAddresses. I have tried Include("BusinesADdress") that didn't work, Help me please.
Try using Include accepting lambda selector followed by ThenInclude:
var list = _dbContext.Member
.Include(m => m.Business)
.ThenInclude(b => b.BusinessAddress)
.OrderByDescending(x => x.SortOrder)
.ThenBy(x => x.firstname)
.ToList();
Also you can just append all needed relations via . in the Include call but in general I would recommend against it cause the first approach is type safe:
var list = _dbContext.Member
.Include("Business.BusinessAddress")
.OrderByDescending(x => x.SortOrder)
.ThenBy(x => x.firstname)
.ToList();

Updating entity tree with deep nested entities

My app deals with saving orders received from an external system. The order contains child items like line items, address, fulfillments, refunds > refund items etc.
Currently, I use an ugly looking code to detect what has changed in each entity by its External Id. Can someone recommend me a better way? :)
Following is a simplified entity model of Order
public class Order
{
public long Id { get; set; }
public string ExternalOrderId { get; set; }
public List<LineItem> LineItems { get; set; }
public List<Fulfillment> Fulfillments { get; set; }
public ShippingAddress ShippingAddress { get; set; }
public List<Refund> Refunds { get; set; }
public string FinancialStatus { get; set; }
public string FulfillmentStatus { get; set; }
}
public class LineItem
{
public long Id { get; set; }
public string ExternalLineItemId { get; set; }
public string SKU { get; set; }
public int Quantity { get; set; }
public long OrderId { get; set; }
}
public class Fulfillment
{
public long Id { get; set; }
public string ExternalFulfillmentId { get; set; }
public string Status { get; set; }
public string TrackingUrl { get; set; }
public long OrderId { get; set; }
}
public class ShippingAddress
{
public long Id { get; set; }
public string ExternalShippingAddressrId { get; set; }
public string Addres { get; set; }
public long OrderId { get; set; }
}
public class Refund
{
public long Id { get; set; }
public string ExternalRefundId { get; set; }
public List<RefundedItem> LineItems { get; set; }
public string CancelledReason { get; set; }
public long OrderId { get; set; }
}
public class RefundedItem
{
public long Id { get; set; }
public string ExternalRefundedItemId { get; set; }
public string SKU { get; set; }
public int Quantity { get; set; }
}
My sample code:
private async Task ManageFulfillments(long orderId, Order order)
{
if (order.Fulfillments == null || !order.Fulfillments.Any()) return;
var newFulfillmentIds = order.Fulfillments.Select(c => c.ExternalFulfillmentId).ToList();
var dbFulfillments = await _fulfillmentRepository.GetAll().IgnoreQueryFilters()
.Where(c => c.OrderId == orderId)
.Select(c => new { c.Id, c.ExternalFulfillmentId }).ToListAsync();
var dbFulfillmentIds = dbFulfillments.Select(c => c.ExternalFulfillmentId).ToList();
// Delete Fulfillments that are not present in new Fulfillments list
var deletedFulfillments = dbFulfillmentIds.Except(newFulfillmentIds).ToList();
if (deletedFulfillments.Any())
{
await _fulfillmentRepository.DeleteAsync(c =>
deletedFulfillments.Contains(c.ExternalFulfillmentId) && c.ExternalOrderId == orderId);
}
// Update existing Fulfillments ids
order.Fulfillments
.Where(c => dbFulfillmentIds.Contains(c.ExternalFulfillmentId))
.ToList()
.ForEach(async c =>
{
c.Id = dbFulfillments.Where(p => p.ExternalFulfillmentId == c.ExternalFulfillmentId)
.Select(p => p.Id).FirstOrDefault();
await _fulfillmentRepository.UpdateAsync(c);
});
// New Fulfillments will automatically be added by EF
}
I have similar code in place to update other entites as well and I'm not proud of it!

C# Join Clause with association table

I have 3 classes : (Personnel, Chirurgien and Operation)
public class Personnel
{
[Key]
public int CodePersonel { get; set; }
public FullName NomComplet { get; set; }
public Adresse Adress { get; set; }
public int Age { get; set; }
public ICollection<Operation> Operation { get; set; }
}
Chirurgien
public class Chirurgien : Personnel
{
public int Nbre_anne_Exp { get; set; }
public int NoteXP { get; set; }
}
and Operation:
public class Operation
{
public int OperationId { get; set; }
public DateTime DateDebut { get; set; }
public DateTime DateFin { get; set; }
public int Duree { get; set; }
public bool Etat { get; set; }
public string CIN { get; set; }
public ICollection<Personnel> Personel { get; set; }
public Patient Patients { get; set; }
public override string ToString()
{
return CIN;
}
}
And I have also created an association table "Membre"
HasMany(p => p.Personel).WithMany(v => v.Operation).Map(m => {
m.ToTable("Membre");
m.MapLeftKey("Operation");
m.MapRightKey("Personel");
});
How can I get Chirurgien list that have failed Operation (Operation Etat=false) ??
I used this code to return the full Chirurgien list:
public ICollection<Chirurgien> NoobDoctors()
{
var req = from t in ut.getRepository<Chirurgien>().GetAll()
select t;
return req.ToList();
}
Thank you
How can I get Chirurgien list that have failed Operation (Operation Etat=false) ??
You can use Operation navigation property with Any for filtering:
var result = from c in ut.getRepository<Chirurgien>().GetAll()
where c.Operation.Any(o => !o.Etat)
select c;
Since you have configured many-to-many relationship with an implicit junction table, EF will maintain the table (including query joins) for you.

How to create the function using Expression linq

I have the following class
public class ProdutoTipo : IAuditable
{
public Guid ID { get; set; }
public string Nome { get; set; }
public string MiniNome { get; set; }
public string Descricao { get; set; }
public string Link { get; set; }
public int? Ordem { get; set; }
public virtual Foto ImagemExibicao { get; set; }
public virtual ICollection<ProdutoCategoria> Categorias { get; set; }
public DateTime CreatedAt { get; set; }
public string CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public string UpdatedBy { get; set; }
public bool PaginaInicial { get; set; }
public ProdutoTipo() { ID = Guid.NewGuid(); }
}
I need a function that search the repository and returns true or false
But this search can be using any field of the class!
As far as I arrived
public bool Existe<TProperty, TComparer>(Expression<Func<ProdutoTipo, TProperty>> entityExpression, TComparer valor)
{
return Repository.ProdutoTipos.Any(p => /*entityExpression == valor ?????*/);
}
Would like to use the function like this ...
Existe(p => p.Nome, "Value to comparer!");
Thank you all!
I think you're looking for
Func<ProdutoTipo, TProperty> getter = entityExpression.Compile();
Repository.ProdutoTipos.Any(p => getter(p).Equals(valor));
But you might as well do this:
public bool Existe<TProperty, TComparer>(Expression<Func<ProdutoTipo, bool>> expression)
{
return Repository.ProdutoTipos.Any(expression);
}
And call:
Existe(p => p.Nome == "Value to comparer!");
Try:
public bool Existe<TProperty, TComparer>(Expression<Func<ProdutoTipo, TProperty>> entityExpression, TComparer valor)
{
entityExpression.Compile()(Repository.ProdutoTipos);
}

Categories

Resources