i have the following problem, i need access to items of sales line from salesheader, when i try access by entity works fine by lazy loading, but i try map with Automapper 6
canĀ“t access to Item from sales header
thanks
public class SalesHeader
{
public int DocumentNo { get; set; }
public virtual ICollection<PostedSalesLine> SalesLines { get; set; }
}
public class SalesLine
{
public int LineNo { get; set; }
public int DocumentNo { get; set; }
public int ItemId { get; set; }
public virtual Item Item { get; set; }
public int Quantity { get; set; }
public decimal Amount { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public decimal UnitCost { get; set; }
public decimal UnitPrice { get; set; }
}
var result = unitOfWork.SalesHeader.GetById(documenNo);
Mapper.Initialize(cfg => cfg.CreateMap<SalesHeader, SalesHeaderDTO>()
return Mapper.Map<SalesHeaderDTO>(result);
Done!
Dont use lazy loading, it creates a mess of proxys
public IEnumerable<SalesHeader> GetAllFullDocuments()
{
return SalesContext.SalesHeader.Include(sh => sh.SalesLines.Select(i => i.Item))
.Include(sh => sh.SellToCustomer)
.Include(sh => sh.BillToCustomer)
.ToList();
}
Related
I'm creating a KPI dashboard that displays the total income from orders. The business logic is that each item is linked to a type of event with a many to many relationship , and linked to a supplier type via a one to many relationship. And there are different suppliers which sell these items based on the supplier type. There is also a table that links suppliers to their order by using the orderItemId and supplierId. What I'm trying to achieve is the following:
Get all orders that have been successfully fulfilled and get their order items.
Get the supplier order items from the table I mentioned above using the order items.
Once I have the supplier order items, I want to group them by the supplierId, and iem event type id so that I can display the items that each supplier sold of each event type.
Supplier
Event Type
List of items
Supplier
Event Type
List of items
The above is what I want to happen. I managed to group them by supplier id but I'm struggling to group them by eventTypeId because of the many to many relationship between the item and the event types.
Here are the models:
public partial class Item
{
public Item()
{
Favorites = new HashSet<Favorite>();
ItemDetails = new HashSet<ItemDetail>();
ItemEventTypes = new HashSet<ItemEventType>();
OrderItems = new HashSet<OrderItem>();
SupplierItems = new HashSet<SupplierItem>();
}
[Key]
public int Id { get; set; }
[StringLength(250)]
public string Image { get; set; }
public string Description { get; set; }
[StringLength(200)]
public string Title { get; set; }
public int? CategoryId { get; set; }
[Column("isDeleted")]
public bool? IsDeleted { get; set; }
public double? Price { get; set; }
public int? EventTypeId { get; set; }
public int? NumberOfGuestsId { get; set; }
public double? DaberniPrice { get; set; }
public double? RegularPrice { get; set; }
public int? Tax { get; set; }
[Column("SupplierTypeID")]
public int? SupplierTypeId { get; set; }
[Column("SortID")]
public int? SortId { get; set; }
public bool? IsDisabled { get; set; }
public int? Min { get; set; }
public int? Max { get; set; }
public int? Increment { get; set; }
public bool? IsShisha { get; set; }
public bool? IsSoldByPackage { get; set; }
[Column("ImageAR")]
[StringLength(250)]
public string ImageAr { get; set; }
[Column("DescriptionAR")]
public string DescriptionAr { get; set; }
[Column("TitleAR")]
[StringLength(250)]
public string TitleAr { get; set; }
public int? Capacity { get; set; }
[ForeignKey(nameof(CategoryId))]
[InverseProperty(nameof(Catrgory.Items))]
public virtual Catrgory Category { get; set; }
[ForeignKey(nameof(NumberOfGuestsId))]
[InverseProperty(nameof(NumberOfGuest.Items))]
public virtual NumberOfGuest NumberOfGuests { get; set; }
[ForeignKey(nameof(SupplierTypeId))]
[InverseProperty("Items")]
public virtual SupplierType SupplierType { get; set; }
[InverseProperty(nameof(Favorite.Item))]
public virtual ICollection<Favorite> Favorites { get; set; }
[InverseProperty(nameof(ItemDetail.Item))]
public virtual ICollection<ItemDetail> ItemDetails { get; set; }
[InverseProperty(nameof(ItemEventType.Item))]
public virtual ICollection<ItemEventType> ItemEventTypes { get; set; }
[InverseProperty(nameof(OrderItem.Item))]
public virtual ICollection<OrderItem> OrderItems { get; set; }
[InverseProperty(nameof(SupplierItem.Item))]
public virtual ICollection<SupplierItem> SupplierItems { get; set; }
}
public partial class ItemEventType
{
[Key]
public int Id { get; set; }
public int? EventTypeId { get; set; }
public int? ItemId { get; set; }
public bool? IsDeleted { get; set; }
[ForeignKey(nameof(EventTypeId))]
[InverseProperty("ItemEventTypes")]
public virtual EventType EventType { get; set; }
[ForeignKey(nameof(ItemId))]
[InverseProperty("ItemEventTypes")]
public virtual Item Item { get; set; }
}
public partial class SupplierAssignedOrderItem
{
[Key]
public int Id { get; set; }
[Column("SupplierID")]
public int? SupplierId { get; set; }
[Column("ItemID")]
public int? ItemId { get; set; }
public int? Status { get; set; }
[Column(TypeName = "datetime")]
public DateTime? CreatedDate { get; set; }
[ForeignKey(nameof(ItemId))]
[InverseProperty(nameof(OrderItem.SupplierAssignedOrderItems))]
public virtual OrderItem Item { get; set; }
[ForeignKey(nameof(Status))]
[InverseProperty(nameof(OrderStatus.SupplierAssignedOrderItems))]
public virtual OrderStatus StatusNavigation { get; set; }
[ForeignKey(nameof(SupplierId))]
[InverseProperty("SupplierAssignedOrderItems")]
public virtual Supplier Supplier { get; set; }
}
Any Help is appreciated. Thanks.
I tried with EF Core,you could get orders and Supplier as follow :
DBcontext:
public class EFCoreDbContext : DbContext
{
public EFCoreDbContext(DbContextOptions<EFCoreDbContext> options)
: base(options)
{
}
public DbSet<Item> Item { get; set; }
public DbSet<Order> Order { get; set; }
public DbSet<Supplier> Supplier { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Item>().ToTable("Item");
modelBuilder.Entity<Order>().ToTable("Order");
modelBuilder.Entity<OrderItem>().HasKey(x => new { x.ItemId, x.OrderId });
modelBuilder.Entity<OrderItem>().HasOne(x => x.Item).WithMany(x => x.OrderItems).HasForeignKey(x => x.ItemId);
modelBuilder.Entity<OrderItem>().HasOne(x => x.Order).WithMany(x => x.OrderItems).HasForeignKey(x => x.OrderId);
modelBuilder.Entity<OrderItem>().ToTable("OrderItem");
modelBuilder.Entity<Supplier>().HasMany(x => x.Orders).WithOne(x => x.Supplier).HasForeignKey(x => x.SupplierId);
modelBuilder.Entity<Supplier>().ToTable("Supplier");
modelBuilder.Entity<SupplierItem>().HasKey(x => new { x.ItemId, x.SupplierId });
modelBuilder.Entity<SupplierItem>().HasOne(x => x.Item).WithMany(x => x.SupplierItems).HasForeignKey(x => x.ItemId);
modelBuilder.Entity<SupplierItem>().HasOne(x => x.Supplier).WithMany(x => x.SupplierItems).HasForeignKey(x => x.SupplierId);
modelBuilder.Entity<SupplierItem>().ToTable("SupplierItem");
}
controller:
var orderlist = _context.Order.Include(p => p.OrderItems).ThenInclude(q => q.Item).ToList();
var supplierlist = _context.Supplier.Include(p => p.SupplierItems).ThenInclude(q => q.Item).ToList();
And I think it'll be better if you remove some properties from your itemclass and add them to SupplierItem class,such as the price property and Event Type property.
an item may have different prices if you buy from different supplies,also in different days. If the event type is used to describe the state of the trade ,it should be removed as well.
public class Item
{
public Item()
{
OrderItems = new List<OrderItem>();
SupplierItems = new List<SupplierItem>();
}
public int ItemId { get; set; }
public string ItemName { get; set; }
public List<OrderItem> OrderItems { get; set; }
public List<SupplierItem> SupplierItems { get; set; }
}
public class Supplier
{
public Supplier()
{
Orders = new List<Order>();
SupplierItems = new List<SupplierItem>();
}
public int SupplierId { get; set; }
public string SupplierName { get; set; }
public List<Order> Orders { get; set; }
public List<SupplierItem> SupplierItems { get; set; }
}
public class SupplierItem
{
public Item Item { get; set; }
public int ItemId { get; set; }
public Supplier Supplier { get; set; }
public int SupplierId { get; set; }
public double SupplierItemPrice { get; set; }
}
If you really want to group the supplier list by two properties
you could try:
var somesupplierslist = supplierlist.GroupBy(x => new { x.SupplierId, x.SupplierName }).ToList();
I want to add 2 entities at once. When I want to add an offer, let the offer detail be added at the same time.
But let's add more than one detail to a quote.
I can't do this. I will be glad if you help.
public class Offer
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int CompanyContactId { get; set; }
public int OfferNumber { get; set; }
public string Annotations { get; set; }
public string CommercialConditions { get; set; }
public string TimeInformation { get; set; }
public decimal ProfitRate { get; set; }
public DateTime Date { get; set; }
public DateTime ValidityDate { get; set; }
public Currency Currency { get; set; }
public Status Status { get; set; }
//Navigation Property
public virtual Company Company { get; set; }
public virtual CompanyContact CompanyContact { get; set; }
public virtual List<OfferDetail> OfferDetail { get; set; }
public class OfferDetail
{
//public int Id { get; set; }
public int OfferId { get; set; }
public int RowNumber { get; set; }
public int Quantity { get; set; }
public string Description { get; set; }
public decimal UnitPrice { get; set; }
public decimal TotalPrice { get; set; }
public string Definition { get; set; }
public Boolean Optional { get; set; }
public decimal UnitProfit { get; set; }
public decimal UnitCost { get; set; }
//public Currency Currency { get; set; }
//Navigation Properties
public Offer Offer { get; set; }
When I add the offer table to the database, the offer details are also added. But let another detail be added to a quote.
public async Task<Result> AddOffer(OfferInfo offerInfo)
{
try
{
var vOffer = new Offer
{
Id = offerInfo.Id,
CompanyId = offerInfo.CompanyId,
CompanyContactId = offerInfo.CompanyContactId,
OfferNumber = offerInfo.OfferNumber,
Annotations = offerInfo.Annotations,
CommercialConditions = offerInfo.CommercialConditions,
TimeInformation = offerInfo.TimeInformation,
ProfitRate = offerInfo.ProfitRate,
Date = offerInfo.Date,
ValidityDate = offerInfo.ValidityDate,
}; _context.Offers.Add(vOffer);
await _context.SaveChangesAsync();
return Result.PrepareSuccess();
}
catch (Exception vEx)
{
return Result.PrepareFailure(vEx.Message);
}
}
I can add from the model I created here.
This way I want to add both. Thanks in advance.
If you assign list of OfferDetail to Offer details entities will automatically be added to adequate table.
It should be something like this:
var vOffer = new Offer
{
// set all your properties of offer here
OfferDetails = new []
{
new OfferDetail {/*init details here*/},
// you can add more of details objects here
}
}
Chicken and egg problem, but there is a solution if you define constructors in OfferDetail that require the Offer and then add themselves to the List<OfferDetail>.
public class Offer
{
public Offer()
{
OfferDetail = new List<OfferDetail>();
}
//Navigation Property
public List<OfferDetail> OfferDetail { get; }
}
public class OfferDetail
{
public OfferDetail(Offer offer)
{
Offer = offer;
offer.OfferDetail.Add(this);
}
public int OfferId { get => Offer.Id; }
//Navigation Properties
public Offer Offer { get; }
}
When attempting a straight forward projection using Entity Framework Core and Linq, I am getting an "Argument types do not match" exception.
I have looked into possible causes and have narrowed it down to the Select that is causing the error (see below). There is a GitHub issue describing a similar situation with simple types and optional navigation entities, but none of the suggested solutions have worked for me. It is not a nullable type and I have tried casting or using Value on any child properties. I have also tried setting the relationship to required in the DbContext which isn't exactly ideal.
Here is the Linq query in the repository:
return await _dashboardContext.PresetDashboardConfig
.Where(config => config.DashboardTypeId == dashboardType && config.OrganisationType = organisationType)
.GroupBy(config => config.GroupId)
.Select(config => new DashboardConfigDTO
{
DashboardType = config.First().DashboardTypeId,
OrganisationId = organisationId,
WidgetGroups = config.Select(group => new WidgetGroupDTO
{
Id = group.Id,
Name = group.GroupName,
TabOrder = group.TabOrder,
// Problem Select below:
Widgets = group.Widgets.Select(widget => new WidgetConfigDTO
{
IndicatorId = widget.IndicatorId,
ScopeId = widget.ScopeId.ToString(),
ParentScopeId = widget.ParentScopeId.ToString(),
WidgetType = widget.WidgetType,
WidgetSize = widget.WidgetSize,
Order = widget.Order
})
})
})
.SingleOrDefaultAsync();
And the entities:
public class DashboardConfig
{
public int Id { get; set; }
public int DashboardTypeId { get; set; }
public int OrganisationType {get; set; }
public int GroupId { get; set; }
public string GroupName { get; set; }
public int TabOrder { get; set; }
}
public class PresetDashboardConfig : DashboardConfig
{
public ICollection<PresetWidgetConfig> Widgets { get; set; }
}
public class WidgetConfig
{
public int Id { get; set; }
public int IndicatorId { get; set; }
public long ScopeId { get; set; }
public long? ParentScopeId { get; set; }
public int WidgetType { get; set; }
public int WidgetSize { get; set; }
public int Order { get; set; }
}
public class PresetWidgetConfig : WidgetConfig
{
public int PresetDashboardConfigId { get; set; }
}
And finally, the DbContext ModelBuilder:
modelBuilder.Entity<PresetDashboardConfig>(entity =>
{
entity.Property(e => e.GroupName)
.HasMaxLength(32)
.IsUnicode(false);
entity.HasMany(e => e.Widgets)
.WithOne();
});
Below are the DTO classes as per Henk's comment:
public class DashboardConfigDTO
{
public int DashboardType { get; set; }
public int OrganisationId { get; set; }
public IEnumerable<WidgetGroupDTO> WidgetGroups { get; set; }
}
public class WidgetGroupDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int TabOrder { get; set; }
public IEnumerable<WidgetConfigDTO> Widgets { get; set; }
}
public class WidgetConfigDTO
{
public int IndicatorId { get; set; }
public string ScopeId { get; set; }
public string ParentScopeId { get; set; }
public int WidgetType { get; set; }
public int WidgetSize { get; set; }
public int Order { get; set; }
}
I have list of lines, the lines contains list of calls (call has DestinationNumber property)
here's my line model
public class Line
{
public Line()
{
Calls = new List<Call>();
Messages = new List<Sms>();
Payments = new List<Payment>();
}
public int LineId { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public Package Package { get; set; }
public ICollection<Call> Calls { get; set; }
public ICollection<Sms> Messages { get; set; }
public ICollection<Payment> Payments { get; set; }
public string LineNumber { get; set; }
public LineStatus Status { get; set; }
[DataType(DataType.DateTime)]
public DateTime? CreatedDate { get; set; }
[DataType(DataType.DateTime)]
public DateTime? RemovedDate { get; set; }
}
and here's my call model
public class Call
{
public int CallId { get; set; }
public int LineId { get; set; }
public Line Line { get; set; }
public DateTime DateOfCall { get; set; }
public int Duration { get; set; }
public string DestinationNumber { get; set; }
}
I want to Count() the number of unique calls
I tried something like that..
Lines.Sum(line=>line.Calls.Sum(call=>call.DestinationNumber.Where(/*if DestiantionNumber not counted before*/))
GroupBy approach this picks all calls by their DestinationNumber which exist only once x.Count() == 1 and counts them Count()
int result = line.Calls.GroupBy(x => x.DestinationNumber).Count(x => x.Count() == 1);
I think something like this should work:
var count = lines.SelectedMany(l => l.Calls)
.Select(c => c.DestinationNumber)
.Distinct()
.Count();
I hav been using automapper for sometime trying figure out how to handle different situation. I came across below situation and need some help figuring out the best approach. Below are my EF related classes;
public sealed class Invoice
{
public int InvoiceID { get; set; }
public DateTime InvoiceDate { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public double? DiscountAmt { get; set; }
public Transaction InvoiceTransaction { get; set; }
public int TransactionID { get; set; }
}
public sealed class Transaction
{
public Transaction()
{
this.TransactionItems = new List<TransactionDetail>();
}
public int TransactionID { get; set; }
public DateTime TransactionDate { get; set; }
public DateTime TransactionLogDate { get; set; }
public TransactionType TransactionType { get; set; }
public IList<TransactionDetail> TransactionItems { get; set; }
public Invoice RefferingInvoice { get; set; }
public string Remarks { get; set; }
}
public sealed class TransactionDetail
{
public int TransactionID { get; set; }
public string ProductItemcode { get; set; }
public Product Product { get; set; }
public double Qty
{
get
{
return Math.Abs(this.StockChangeQty);
}
}
public double StockChangeQty { get; set; }
public double? UnitPrice { get; set; }
}
public sealed class Product
{
public Product()
{
this.StockTransactions = new List<TransactionDetail>();
}
public string ItemCode { get; set; }
public string ProductName { get; set; }
public string Manufacturer { get; set; }
public double UnitPrice { get; set; }
public IList<TransactionDetail> StockTransactions { get; set; }
public double Qty
{
get
{
if (this.StockTransactions.Count == 0)
{
return 0;
}
else
{
return this.StockTransactions.Sum(x => x.StockChangeQty);
}
}
}
public bool Discontinued { get; set; }
}
These are my view model classes;
public class InvoiceReportViewModel
{
public InvoiceReportViewModel()
{
LineItems = new List<InvoiceReportLineItemViewModel>();
}
public int InvoiceID { get; set; }
public DateTime InvoiceDate { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public double? DiscountAmt { get; set; }
public string Remarks { get; set; }
public string StringInvoiceNo
{
get
{
return InvoiceID.ToString("########");
}
}
public IList<InvoiceReportLineItemViewModel> LineItems { get; set; }
}
public class InvoiceReportLineItemViewModel
{
public string ItemCode { get; set; }
public string ProductName { get; set; }
public string Manufacturer { get; set; }
public double? UnitPrice { get; set; }
public double Qty { get; set; }
public double LineTotal
{
get
{
if (UnitPrice.HasValue)
{
return UnitPrice.Value * Qty;
}
else
{
return 0;
}
}
}
}
My requirement is to convert the Invoice EF object to InvoiceReportViewModel object.
To do this I need to setup the profile. This is where I run into a problem; as it's not straight forward. The only way I see this done is by create my own Resolver by extending TypeConverter and manually doing the conversion by overriding ConvertCore method.
If there another way of getting this done (something with less work)???
Also I feel I could Map TransactionDetails EF class to InvoiceReportLineItemViewModel class by using the Mapper.CreateMap()..ForMember(...
But how can I use the mapper to convert it within the ConvertCore method?
Thanks in advance
In your case I do not see any requirements to use any custom converters.
You can convert Invoice EF object to InvoiceReportViewModel using simple Mapper.CreateMap like following:
public class InvoiceProfile: Profile
{
protected override void Configure()
{
Mapper.CreateMap<Invoice, InvoiceReportViewModel>()
.ForMember(c => c.CustomerName, op => op.MapFrom(v => v.CustomerName))
.ForMember(c => c.DiscountAmt, op => op.MapFrom(v => v.DiscountAmt))
.ForMember(c => c.InvoiceDate, op => op.MapFrom(v => v.InvoiceDate))
.ForMember(c => c.LineItems, op => op.MapFrom(v => v.InvoiceTransaction.TransactionItems));
Mapper.CreateMap<TransactionDetail, InvoiceReportLineItemViewModel>()
.ForMember(c => c.ProductName, op => op.MapFrom(v => v.Product.ProductName))
.ForMember(c => c.Qty, op => op.MapFrom(v => v.Qty))
//and so on;
}
}
Do not forget to register "InvoiceProfile"