I'm using lazyloading to get all my related data and return json to my API. But I can't seem to get all my related data.
Not sure how to use the include syntax correctly either.
public class Recipe
{
public int Id { get; set; }
public string RecipeDescription { get; set; }
public int? HowManyPersons { get; set; }
public int? TimeCreated { get; set; }
public virtual ICollection<IngredientQuantity> IngredientQuantities { get; set;}
public int RecipeTypeId { get; set; }
public virtual RecipeType RecipeType { get; set; }
public string PreparationMethods { get; set; }
}
public class IngredientQuantity
{
public int id { get; set; }
public int IngredientId { get; set; }
[ForeignKey("IngredientId")]
public virtual Ingredient Ingredient { get; set; }
public string Amount { get; set; }
public int QuantityTypeId { get; set; }
[ForeignKey("QuantityTypeId")]
public virtual QuantityType QuantityType { get; set; }
public int RecipeId { get; set; }
[ForeignKey("RecipeId")]
public virtual Recipe Recipe { get; set; }
}
public class Ingredient
{
public int Id { get; set; }
public string IngredientDecsription { get; set; }
}
public async Task<IEnumerable<Recipe>> GetAll()
{
try
{
var recipe = await context.Recipe
.Include(r => r.RecipeType)
.Include(r => r.IngredientQuantities)
.ThenInclude(iq => iq.Ingredient)
.ToListAsync();
return recipe;
}
catch(Exception ex)
{
return null;
}
}
this is my json output:
{"results":[{"ingredientQuantities":[{"ingredient":{"id":1,"ingredientDecsription":"Spinazie"},"quantityType":{"id":1,"quantityTypeDescription":"Handen"}
You can configure Json.NET to ignore cycles that it finds in the object graph.Try to add below code in startup:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
Refer to Related data and serialization
Related
I am losing my mind trying to coerce data from Entity Framework Core and output the data using an ASP.NET Core Web API where the data will ultimately be consumed by a Blazor WASM app. The data store is a Postgresql database. My partner in crime is requesting the data be formatted specifically so that some child classes/objects are presented in summarized form(id/name) and others with full detail.
The first batch of code is my entity classes. Most of this is autogenerated database-first as I am working with an existing batch of Winforms apps and existing data:
//entity classes
public partial class Appuser
{
public int Appuserid { get; set; }
public string Familyname { get; set; }
public string Givennames { get; set; }
public string Login { get; set; }
public string? Hash { get; set; }
public string? Salt { get; set; }
public int? Oup { get; set; }
public short Active { get; set; }
public string? Email { get; set; }
}
public partial class Part
{
public int Partid { get; set; }
public int Partclassid { get; set; }
public string Partnumber { get; set; }
public string Description { get; set; }
public int Qty { get; set; }
public string? Uom { get; set; }
public string? Customernumber { get; set; }
public decimal? Width { get; set; }
public decimal? Length { get; set; }
public string? Comment { get; set; }
public bool? Pstexempt { get; set; }
public int? Sagecode { get; set; }
public bool? Active { get; set; }
public virtual Partclass Partclass { get; set; }
public virtual Sagecode? SagecodeNavigation { get; set; }
}
public partial class Partclass
{
public int Partclassid { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}
public partial class Purchaserequest
{
public int Purchaserequestid { get; set; }
public string Purchaserequestnumber { get; set; }
public int? Vendorid { get; set; }
public int? Enteredby { get; set; }
public DateTime Datecreated { get; set; }
public DateTime? Datesubmitted { get; set; }
public DateTime? Dateclosed { get; set; }
public int? Purchaserequeststatusid { get; set; }
public string? Comments { get; set; }
public string? Purpose { get; set; }
public string? Authcomments { get; set; }
public DateTime? Authactiondate { get; set; }
public int? Authority { get; set; }
public int? Urgencyid { get; set; }
public virtual Appuser? AuthorityNavigation { get; set; }
public virtual Appuser? EnteredbyNavigation { get; set; }
public virtual Purchaserequeststatus? Purchaserequeststatus { get; set; }
public virtual Urgency? Urgency { get; set; }
public virtual Vendor? Vendor { get; set; }
public virtual ICollection<Purchaserequestline> Purchaserequestlines { get; set; }
}
public partial class Purchaserequestline
{
public int Purchaserequestlineid { get; set; }
public int Purchaserequestid { get; set; }
public int? Partid { get; set; }
public string Uom { get; set; } = null!;
public string? Itemdescription { get; set; }
public string? Comments { get; set; }
public decimal? Price { get; set; }
public string? Vendorpartno { get; set; }
public string? Customerpartnumber { get; set; }
public decimal? Qty { get; set; }
public int? Sagecode { get; set; }
public virtual Part? Part { get; set; }
public virtual Sagecode? SagecodeNavigation { get; set; }
}
public partial class Purchaserequeststatus
{
public int Purchaserequeststatusid { get; set; }
public string Description { get; set; } = null!;
}
public partial class Sagecode
{
public int Code { get; set; }
public string Description { get; set; }
public int? Class { get; set; }
public int? Parent { get; set; }
public virtual Sagecodeclass? ClassNavigation { get; set; }
public virtual Sagecode? ParentNavigation { get; set; }
}
public partial class Sagecodeclass
{
public int Classid { get; set; }
public string Classname { get; set; } = null!;
public virtual ICollection<Sagecode> Sagecodes { get; set; }
}
public partial class Urgency
{
public int Urgencyid { get; set; }
public string? Name { get; set; }
}
public partial class Vendor
{
public int Vendorid { get; set; }
public string Vendorname { get; set; } = null!;
public string? Address { get; set; }
public string? Address2 { get; set; }
public string? City { get; set; }
public string? Provincecd { get; set; }
public string? Countrycd { get; set; }
public string? Postalcode { get; set; }
public string? Comments { get; set; }
public short Active { get; set; }
public short? Pstexempt { get; set; }
}
The following are my data transfer objects used to pass my data:
//DTO records
public record AppUserDTO
{
public int Appuserid { get; init; }
public string Familyname { get; init; }
public string Givennames { get; init; }
public string Login { get; init; }
public int? Oup { get; init; }
public short Active { get; init; }
public string? Email { get; init; }
}
public record LookupDTO
{
public int id { get; init; }
public string name { get; init; }
}
public record PartDTO
{
public int Partid { get; init; }
public virtual Partclass Partclass { get; init; }
public string Partnumber { get; init; }
public string Description { get; init; }
public int Qty { get; init; }
public string? Uom { get; init; }
public string? Customernumber { get; init; }
public decimal? Width { get; init; }
public decimal? Length { get; init; }
public virtual Pricemethod? MaterialpricemethodNavigation { get; init; }
public string? Comment { get; init; }
public bool? Pstexempt { get; init; }
public virtual Sagecode? SagecodeNavigation { get; init; }
public bool? Active { get; init; }
}
public record PartclassDTO
{
public int Partclassid { get; init; }
public string Name { get; init; }
public string? Description { get; init; }
}
public record PreqDTO
{
public int Purchaserequestid { get; init; }
public string Purchaserequestnumber { get; init; } = null!;
public virtual LookupDTO? Vendor { get; init; }
public virtual LookupDTO? EnteredbyNavigation { get; init; }
public DateTime Datecreated { get; init; }
public DateTime? Datesubmitted { get; init; }
public DateTime? Dateclosed { get; init; }
public virtual LookupDTO? Purchaserequeststatus { get; init; }
public string? Comments { get; init; }
public string? Purpose { get; init; }
public string? Authcomments { get; init; }
public DateTime? Authactiondate { get; init; }
public virtual LookupDTO? AuthorityNavigation { get; init; }
public virtual LookupDTO? Urgency { get; init; }
public virtual ICollection<PurchaseRequestLineDTO> Purchaserequestlines { get; init; }
public virtual ICollection<Purchaserequesturl> Purchaserequesturls { get; init; }
}
public record PurchaseRequestLineDTO
{
public int Purchaserequestlineid { get; init; }
public int Purchaserequestid { get; init; }
public virtual LookupDTO? Part { get; init; }
public string Uom { get; init; }
public string? Itemdescription { get; init; }
public string? Comments { get; init; }
public decimal? Price { get; init; }
public string? Vendorpartno { get; init; }
public string? Customerpartnumber { get; init; }
public decimal? Qty { get; init; }
public virtual SagecodeDTO? SagecodeNavigation { get; set; }
}
public record PurchaseRequestStatusDTO
{
public int Purchaserequeststatusid { get; init; }
public string Description { get; init; }
}
public record SagecodeDTO
{
public int Code { get; init; }
public string Description { get; init; }
public virtual Sagecodeclass? ClassNavigation { get; init; }
public virtual Sagecode? ParentNavigation { get; init; }
}
public record SagecodeclassDTO
{
public int Classid { get; init; }
public string Classname { get; init; }
}
public record UrgencyDTO
{
public int Urgencyid { get; init; }
public string? Name { get; init; }
}
public record VendorDTO
(
public int Vendorid { get; init; }
public string Vendorname { get; init; }
public string? Address { get; init; }
public string? Address2 { get; init; }
public string? City { get; init; }
public string? Provincecd { get; init; }
public string? Countrycd { get; init; }
public string? Postalcode { get; init; }
public string? Comments { get; init; }
public short Active { get; init; }
public short? Pstexempt { get; init; }
);
A partial listing of my AutoMapper configuration follows:
//AutoMapper Profile
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Part, PartDTO>();
CreateMap<Partclass, PartclassDTO>();
CreateMap<Vendor, VendorDTO>();
CreateMap<Sagecode, SagecodeDTO>();
CreateMap<Sagecodeclass, SagecodeclassDTO>();
CreateMap<Urgency, UrgencyDTO>();
CreateMap<Purchaserequestline, PurchaseRequestLineDTO>();
CreateMap<Purchaserequeststatus, PurchaseRequestStatuysDTO>();
CreateMap<Appuser, AppUserDTO>();
CreateMap<Purchaserequest, PreqDTO>();
CreateMap<Appuser, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Appuserid))
.ForMember(dst => dst.name, opt => opt.MapFrom(src => src.Givennames + " " + src.Familyname));
CreateMap<Purchaserequeststatus, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Purchaserequeststatusid))
.ForMember(dst => dst.name, opt => opt.MapFrom(src => src.Description));
CreateMap<Urgency, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Urgencyid))
.ForMember(dst => dst.name, opt => opt.MapFrom(src => src.Name));
CreateMap<Vendor, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Vendorid))
.ForMember(dst => dst.name, opt => opt.MapFrom(src => src.Vendorname));
CreateMap<Part, LookupDTO>()
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Partid))
.ForMember(dst => dst.id, opt => opt.MapFrom(src => src.Partnumber + " - " + src.Description));
CreateMap<Purchaserequestline, PurchaseRequestLineDTO>();
CreateMap<Purchaserequesturl, PurchaseRequestURLDTO>();
}
}
My partner in crime wants the data for a single purchase request pulled from the PurchaserequestController returning a fairly complete object graph. I am providing this data from my PurchaserequestRepository using:
//the repository does the magic to pull the data required
public async Task<Purchaserequest> GetPurchaseRequestById(int id, bool trackChanges)
{
return await FindByCondition(p => p.Purchaserequestid.Equals(id), trackChanges)
.Include(p => p.Purchaserequestlines)
.ThenInclude(l=> l.SagecodeNavigation)
.Include(p => p.Purchaserequesturls)
.Include(p => p.Vendor)
.Include(p => p.Purchaserequeststatus)
.Include(p => p.Urgency)
.Include(p => p.AuthorityNavigation)
.Include(p => p.EnteredbyNavigation)
.FirstOrDefaultAsync();
}
This is called from the PurchaserequestService:
//The controller uses the following Service call
// my service pulls the entity graph from the repository
// and formats it using the AutoMapper before returning to the caller as json
public async Task<PreqDTO> GetPurchaseRequestByIdAsync(int id, bool trackChanges)
{
try
{
var preq = await _repository.PurchaseRequests.GetPurchaseRequestById(id, trackChanges);
var dto = _mapper.Map<Purchaserequest, PreqDTO>(preq);
return dto;
}
catch (Exception ex)
{
_logger.LogError($"Something went wrong in the {nameof(GetPurchaseRequestByIdAsync)} service method {ex}");
throw;
}
}
The problem with all of this is that it feels like I am spinning my tires furiously for little benefit. I feel I am going to run into a lot of future issues with the AutoMapper mappings with multiple destination mappings for some source entities.
Am I doing this all wrong? Is there a better way to do this? Any ideas are much appreciated.
Sorry for the length of this post ... I am sure many will get tired of scrolling and run away.
I have an error "Value cannot be null (Parameter key)" using post and put methods.
So I have to send data using a Radzen Blazor dialog
I found that my many to many relationships between (product and predefinedElements), (markets and predefinedElements) and (actuation type and predefinedelements) cause "value cannot be null" exception but I can't find what's null here...
Here is how market, predefinedElements, products and actuationType are defined in the Model
Market :
namespace BTNLib.Shared.Models
{
public partial class Market
{
public Market()
{
PredefinedElements = new HashSet<PredefinedElement>();
}
[Key]
public int MarketId { get; set; }
public string? Name { get; set; }
[InverseProperty(nameof(PredefinedElement.Markets))]
public virtual ICollection<PredefinedElement> PredefinedElements { get; set; }
}
}
PredefinedElement :
namespace BTNLib.Shared.Models
{
public partial class PredefinedElement
{
public PredefinedElement()
{
ActuationTypes = new HashSet<ActuationType>();
Markets = new HashSet<Market>();
Products = new HashSet<Product>();
}
public string? SpoolRSideStd { get; set; }
[Key]
public int PredefinedElementId { get; set; }
public string? Designation { get; set; }
public string? ConfigFileLink { get; set; }
public string? SpoolRSideRs15Rs12 { get; set; }
public string? SpoolRSideRs12Rs08 { get; set; }
public int? SectionElementPositionId { get; set; }
public int? MaxFlow { get; set; }
public int? SectionElementTypeId { get; set; }
public string? InternalPilotOilSupply { get; set; }
public int? SectionBasicFunctionId { get; set; }
public int? SectionMouvmeentNamingId { get; set; }
public int? SectionCustomerPipingId { get; set; }
public string? PortType { get; set; }
public string? ConnectorType { get; set; }
public string? Voltage { get; set; }
public string? RpnSide { get; set; }
public int? SliceTypeId { get; set; }
public string? ShortInformation { get; set; }
public string? LongInformation { get; set; }
public string? Docupedia { get; set; }
public string? SectionPicture { get; set; }
public string? SectionPictureAxo { get; set; }
public string? MasterConfigFile { get; set; }
public string? Color { get; set; }
public int? EfatemplateId { get; set; }
public int? EfaAsmTemplateId { get; set; }
public int? EfaAgzTemplateId { get; set; }
public int? EfaGezTemplateId { get; set; }
public int? TieRodThicknessSide1 { get; set; }
public int? LThreadUnderNutSide1 { get; set; }
public int? LThreadAfterNutSide1 { get; set; }
public int? NutThicknessSide1 { get; set; }
public int? InitialAnchorLenghSide1 { get; set; }
public int? LThreadUnderNutSide2 { get; set; }
public int? LThreadAfterNutSide2 { get; set; }
public int? NutThicknessSide2 { get; set; }
public int? InitialAnchorLenghSide2 { get; set; }
public int? FlangeFaceInCavityId { get; set; }
public int? FlangeFaceOut1CavityId { get; set; }
public int? FlangeFaceOut2CavityId { get; set; }
public int? RequiredFlangeFaceCavity1id { get; set; }
public int? RequiredFlangeFaceCavity2id { get; set; }
public int? RequiredFlangeFaceCavity3id { get; set; }
public int? RequiredFlangeFaceCavity4id { get; set; }
public int? RequiredFlangeFaceCavity5id { get; set; }
public int? RequiredFlangeFaceCavity6id { get; set; }
public int? RequiredFlangeFaceCavity7id { get; set; }
public int? ImageId { get; set; }
public string? Actuation { get; set; }
public virtual EfaAgzTemplate? EfaAgzTemplate { get; set; }
public virtual EfaAsmTemplate? EfaAsmTemplate { get; set; }
public virtual EfaGezTemplate? EfaGezTemplate { get; set; }
public virtual Efatemplate? Efatemplate { get; set; }
public virtual Cavity? FlangeFaceInCavity { get; set; }
public virtual Cavity? FlangeFaceOut1Cavity { get; set; }
public virtual Cavity? FlangeFaceOut2Cavity { get; set; }
public virtual Image? Image { get; set; }
public virtual Cavity? RequiredFlangeFaceCavity1 { get; set; }
public virtual Cavity? RequiredFlangeFaceCavity2 { get; set; }
public virtual Cavity? RequiredFlangeFaceCavity3 { get; set; }
public virtual Cavity? RequiredFlangeFaceCavity4 { get; set; }
public virtual Cavity? RequiredFlangeFaceCavity5 { get; set; }
public virtual Cavity? RequiredFlangeFaceCavity6 { get; set; }
public virtual Cavity? RequiredFlangeFaceCavity7 { get; set; }
public virtual SectionBasicFunction? SectionBasicFunction { get; set; }
public virtual SectionCustomerPiping? SectionCustomerPiping { get; set; }
public virtual SectionElementPosition? SectionElementPosition { get; set; }
public virtual SectionElementType? SectionElementType { get; set; }
public virtual SectionMouvementNaming? SectionMouvmeentNaming { get; set; }
public virtual SliceType? SliceType { get; set; }
[InverseProperty(nameof(ActuationType.PredefinedElements))]
public virtual ICollection<ActuationType> ActuationTypes { get; set; }
[InverseProperty(nameof(Market.PredefinedElements))]
public virtual ICollection<Market> Markets { get; set; }
[InverseProperty(nameof(Product.PredefinedElements))]
public virtual ICollection<Product> Products { get; set; }
}
}
Product :
namespace BTNLib.Shared.Models
{
public partial class Product
{
public Product()
{
PredefinedElements = new HashSet<PredefinedElement>();
}
[Key]
public int ProductsId { get; set; }
public string? Name { get; set; }
[InverseProperty(nameof(PredefinedElement.Products))]
public virtual ICollection<PredefinedElement> PredefinedElements { get; set; }
}
}
Actuation Type :
namespace BTNLib.Shared.Models
{
public partial class ActuationType
{
public ActuationType()
{
PredefinedElements = new HashSet<PredefinedElement>();
}
[Key]
public int ActuationTypeId { get; set; }
public string? Type { get; set; }
[InverseProperty(nameof(PredefinedElement.ActuationTypes))]
public virtual ICollection<PredefinedElement> PredefinedElements { get; set; }
}
}
The database context is too long to be posted here if you need it I can post it in repsonse.
When I send the data of the predefinedElement is like that :
protected async Task Submit()
{
if (Designation == null)
{
await PredefinedElementService.CreatePredefinedElement(predefinedElement);
}
else
{
await PredefinedElementService.UpdatePredefinedElement(predefinedElement);
}
}
void OnChangeMarket(IEnumerable<Market> values)
{
predefinedElement.Markets.Clear();
foreach(var Market in values)
{
predefinedElement.Markets.Add(Market);
}
}
void OnChangeProduct(IEnumerable<Product> values)
{
predefinedElement.Products.Clear();
foreach(var Product in values)
{
predefinedElement.Products.Add(Product);
}
}
void OnChangeActuationType(IEnumerable<ActuationType> values)
{
predefinedElement.ActuationTypes.Clear();
foreach(var ActuationType in values)
{
predefinedElement.ActuationTypes.Add(ActuationType);
}
}
It calls Post or Put method via a service like that :
public async Task CreatePredefinedElement(PredefinedElement predefinedElement)
{
var result = await _http.PostAsJsonAsync("api/PredefinedElements",
predefinedElement);
Console.WriteLine(result);
await SetPredefinedElement(result);
}
public async Task UpdatePredefinedElement(PredefinedElement predefinedElement)
{
var result = await
_http.PutAsJsonAsync($"api/PredefinedElements/{predefinedElement.PredefinedElementId}",
predefinedElement);
await SetPredefinedElement(result);
}
And the controller API is something like that :
The error is displayed when _context.Add or _context.Update are called
[HttpPost]
public async Task<ActionResult<List<PredefinedElement>>>
PostPredefinedElement(PredefinedElement predefinedElements)
{
try
{
_dbContext.PredefinedElements.Add(predefinedElements);
}
catch(Exception ex)
{
throw ex;
}
await _dbContext.SaveChangesAsync();
return Ok(await GetdbPredefinedElement());
}
[HttpPut("{id}")]
public async Task<ActionResult<List<PredefinedElement>>>
UpdatePredefinedElement(PredefinedElement predefinedElement)
{
if (predefinedElement == null)
{
return NotFound("Aucun element de ce type");
}
else
{
_dbContext.Update(predefinedElement);
}
await _dbContext.SaveChangesAsync();
return Ok(await GetdbPredefinedElement());
}
private async Task<List<PredefinedElement>> GetdbPredefinedElement()
{
var result = await _dbContext.PredefinedElements.Include(p => p.Products)
.Include(p => p.Markets)
.Include(p => p.ActuationTypes)
.Include(p => p.Image)
.Include(p => p.SectionElementPosition)
.Include(p => p.SectionElementType)
.Include(p => p.SectionBasicFunction)
.Include(p => p.SectionMouvmeentNaming)
.Include(p => p.SectionCustomerPiping)
.Include(p => p.SliceType)
.Include(p => p.Efatemplate)
.Include(p => p.EfaAgzTemplate)
.Include(p => p.EfaAsmTemplate)
.Include(p => p.EfaGezTemplate)
.Include(p => p.FlangeFaceInCavity)
.Include(p => p.FlangeFaceOut1Cavity)
.Include(p => p.RequiredFlangeFaceCavity1)
.Include(p => p.RequiredFlangeFaceCavity2)
.Include(p => p.RequiredFlangeFaceCavity3)
.Include(p => p.RequiredFlangeFaceCavity4)
.Include(p => p.RequiredFlangeFaceCavity5)
.Include(p => p.RequiredFlangeFaceCavity6)
.Include(p => p.RequiredFlangeFaceCavity7).ToListAsync();
return result;
}
I can't figure out what cause the exception I only know that came from many to many relationships because when I don't add entity from this relationship in the collection there are no error displayed.
I tried everything like putting [key] everywhere in the model checking my DB if there was no problem.
This project is a merge from 2 projects so there is another database context where 2 tables are linked with the first context.
EDIT : the error only happens when I try to post another predefinedelement entity or modify one resulting two entities of predefinedelement have the same entity from the table Market, Product or ActuationType.
Exemple in the Joining Table Predefinedelement_Market
PredefinedElementId : 1 MarketId : 1
and
PredefinedElementId : 2 MarketId : 1
causing the error in Entity Framework
Thanks for the help.
SOLVED:
So to prevent this error you need to had to the controller before adding the element and saving :
predefinedElements.Markets = _dbContext.Markets.Where(x => predefinedElements.Markets.Contains(x)).ToList();
predefinedElements.Products = _dbContext.Products.Where(x => predefinedElements.Products.Contains(x)).ToList();
predefinedElements.ActuationTypes = _dbContext.ActuationTypes.Where(x => predefinedElements.ActuationTypes.Contains(x)).ToList();
I have 4 classes in dbcontext,it's EventRemind.cs Event.cs House.cs Customer.cs,the code like this:
public class EventRemind
{
[Key]
public Guid Id { get; set; }
public Guid CustomerEventId { get; set; }
public DateTime RemindTime { get; set; }
public bool HasRead { get; set; }
public DateTime CreatedTime { get; set; }
[ForeignKey("CustomerEventId")]
public virtual Event CustomerEvent { get; set; }
}
public class Event
{
[Key]
public Guid Id { get; set; }
public string Title { get; set; }
public int UserId { get; set; }
public int HouseId { get; set; }
[MaxLength(800)]
public string Content { get; set; }
public DateTime CreatedTime { get; set; }
[ForeignKey("HouseId")]
public virtual House House { get; set; }
[ForeignKey("UserId")]
public virtual Customer Customer { get; set; }
public virtual ICollection<EventRemind> EventReminds { get; set; }
}
public class House
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Event> HouseEvents { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Event> CustomerEvents { get; set; }
}
and my dbcontext is this:
public class DataContext:DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<EventRemind> EventReminds { get; set; }
public DbSet<Event> Events { get; set; }
public DbSet<House> Houses { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
that means an eventRemind include an event,an event include a house and a customer,now what puzzles me is that what should I do to get the House and Customer at the same time from EventReminds,what I want is this:
var query = _dataContext.EventReminds.Include(c => c.CustomerEvent)
.ThenInclude(c => c.Customer).ThenInclude(c => c.House //this get a compile error);
why dis this happen? Can somebody help me? Thanks in advance.
I think your last operator should be just Include. Try this:
var query = _dataContext.EventReminds
.Include(c => c.CustomerEvent)
.ThenInclude(c => c.Customer)
.Include(c => c.House);
You have to write code following way.
Way1
var query =
_dataContext.EventReminds.Include(c => c.CustomerEvent).ThenInclude(c => c.Customer)
.Include(c=> c.CustomerEvent).ThenInclude(c => c.House);
Way2 (If property is not collection then it is usefull)
var query =
_dataContext.EventReminds.Include(c=> c.CustomerEvent).
.Include(c=> c.CustomerEvent.Customer)
.Include(c=> c.CustomerEvent.House);
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!
I'm trying to create a view, which previously got an ID, which is working fine(checked in debugger, ID is correct), to invoke a method:
public ActionResult DetaljiNarudzbe(int id)
{
DetaljiNarudzbeViewModel model = new DetaljiNarudzbeViewModel();
model.Narudzba = ctx.Naruzbee.Where(x => x.Id == id).First();
model.StatusNarudzbe = ctx.StatusiNarudzbi.Where(x => x.Id == model.Narudzba.StatusNarudzbeId).FirstOrDefault();
model.Primaoc = ctx.Primaoci.Where(x => x.Id == model.Narudzba.PrimaocId).FirstOrDefault();
model.Adresa = ctx.Adrese.Where(x => x.Id == model.Narudzba.AdresaId).FirstOrDefault();
model.Grad = ctx.Gradovi.Where(x => x.Id == model.Adresa.GradId).FirstOrDefault();
model.StavkeNarudzbe = ctx.StavkeNarudzbi.Where(x => x.Narudzbe_Id == id).ToList();
model.Klijent = ctx.Klijenti.Where(x => x.Id == model.Narudzba.KlijentId).FirstOrDefault();
model.Korisnik = ctx.Korisnici.Where(x => x.Id == model.Klijent.KorisnikId).FirstOrDefault();
return View("DetaljiNarudzbe", model);
}
However, it keeps crashing at this part
model.StavkeNarudzbe = ctx.StavkeNarudzbi.Where(x => x.Narudzbe_Id == id).ToList();
It throws an exception, because for some reason, I think the context created another column called Narudzbe_Id1, which can't be null.
https://imgur.com/a/UFxXB - Image of the given exception
Further proof that it's an issue with dbcontext:
https://imgur.com/a/KEOe3
The extra column doesn't appear in the database on the SQL server's side, where I'm getting the data from.
If it helps, I'm posting the other relevant classes below:
public class StavkaNarudzbe : IEntity
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
public string Naziv { get; set; }
public int Tezina { get; set; }
public double Cijena { get; set; }
public int Narudzbe_Id { get; set; }
public virtual Narudzbe Narudzbe { get; set; }
}
public class MojKontekst : DbContext
{
public MojKontekst() : base("DostavaConnString")
{
}
public DbSet<Adresa> Adrese { get; set; }
public DbSet<Grad> Gradovi { get; set; }
public DbSet<DetaljiVozila> DetaljiVozilaa { get; set; }
public DbSet<Klijent> Klijenti { get; set; }
public DbSet<Korisnik> Korisnici { get; set; }
public DbSet<Kurir> Kuriri { get; set; }
public DbSet<Kvar> Kvarovi { get; set; }
public DbSet<Obavijest> Obavijesti { get; set; }
public DbSet<Narudzbe> Naruzbee { get; set; }
public DbSet<Posiljka> Posiljke { get; set; }
public DbSet<Prelazi> Prelazii { get; set; }
public DbSet<Primaoc> Primaoci { get; set; }
public DbSet<Skladiste> Skladista { get; set; }
public DbSet<StatusNarudzbe> StatusiNarudzbi { get; set; }
public DbSet<StavkaNarudzbe> StavkeNarudzbi { get; set; }
public DbSet<Vozilo> Vozila { get; set; }
public DbSet<VrstaVozila> VrsteVozila { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class DetaljiNarudzbeViewModel
{
public Klijent Klijent;
public Korisnik Korisnik;
public Narudzbe Narudzba;
public List<StavkaNarudzbe> StavkeNarudzbe;
public StatusNarudzbe StatusNarudzbe;
public Primaoc Primaoc;
public Adresa Adresa;
public Grad Grad;
}
public class Narudzbe : IEntity
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
public string SifraNarudzbe { get; set; }
public DateTime DatumNarudzbe { get; set; }
public bool Osigurano { get; set; }
public bool BrzaDostava { get; set; }
public int BrojPaketa { get; set; }
public int KlijentId { get; set; }
public virtual Klijent Klijent { get; set; }
public int AdresaId { get; set; }
public virtual Adresa Adresa { get; set; }
public Nullable<int> PosiljkaId { get; set; }
public virtual Posiljka Posiljka { get; set; }
public int StatusNarudzbeId { get; set; }
public virtual StatusNarudzbe StatusNarudzbe{ get; set; }
public int PrimaocId { get; set; }
public virtual Primaoc Primaoc { get; set; }
public Nullable<System.DateTime> VrijemeIsporuke { get; set; }
public int CijenaNarudzbe { get; set; }
}
Exception Text: Invalid column name Narudzbe_Id1
This is Entity Framework trying to follow it's standard naming conventions for relationship columns.
See: https://msdn.microsoft.com/en-us/library/jj819164(v=vs.113).aspx for more information this.
As you are using non-standard names for your foreign key columns (i.e. Narudzbe_Id should be NarudzbeId) you'll need to let EF know how to link up your models. Either rename the properties of your classes to follow this naming convention, or use Data Annotations to explicitly tell EF about your relationships.
For example, try adding a ForeignKey attribute (found in the System.Componentmodel.Dataannotations.Schema namespace) like so:
public class StavkaNarudzbe : IEntity
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
public string Naziv { get; set; }
public int Tezina { get; set; }
public double Cijena { get; set; }
public int Narudzbe_Id { get; set; }
[ForeignKey("Narudzbe_Id")]
public virtual Narudzbe Narudzbe { get; set; }
}