I am currently working on a personal project where I want to map the UserTransaction to GetAllTransactionRes and return all UserTransaction from my database when the API/transaction is hit. Each time I use the API/transaction endpoint I got this error
System.Collections.Generic.List`1[ProjectName.Modules.Transaction.Core.DTO.GetAllTransactionRes]
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. Path: $.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.OrderedProducts.UserTransaction.TransactionId.
This is the UserTransaction Entity
public class UserTransaction
{
public int TransactionId { get; set; }
public DateTime Date { get; set; }
public virtual ICollection<OrderedProduct> OrderedProducts { get; set; }
}
This is the Ordered Product Entity
public class OrderedProduct
{
public int Id { get; set; }
public string Product { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public bool Returned { get; set; }
public int TransactionId { get; set; }
public virtual UserTransaction UserTransaction { get; set; }
}
This is my mapper. GetAllTransactionRes and AllOrderedProductDTO is the exact copy of UserTransaction and OrderedProduct Entity.
CreateMap<UserTransaction, GetAllTransactionRes>().ForMember(s => s.OrderedProducts, c => c.MapFrom(m => m.OrderedProducts));
CreateMap<OrderedProduct, AllOrderedProductDTO>();
Since I am using MediatR. This is my handler for my GetAllTransactionQuery
public async Task<ICollection<GetAllTransactionRes>> Handle(GetAllTransactionQuery request, CancellationToken cancellationToken)
{
var Transactions = await _context.UserTransactions.Include(ut => ut.OrderedProducts).ToListAsync();
var mapped = _mapper.Map<ICollection<UserTransaction>, ICollection<GetAllTransactionRes>>(Transactions);
return mapped;
}
Before using automapper I used the .include method from efcore which gives me the same error I searched for answers and a person commented in a StackOverflow question that i should not return DB entities directly in my API. This is the question where the said comment is posted
What am I doing wrong? Thanks
The issue is that your AllOrderedProductDTO and your GetAllTransactionRes data transfer objects have a circular reference - transactions contains products which contain the transaction which contains the products...
Here are the DTO classes I would recommend to break out of the cycle:
public class GetAllTransactionRes
{
public int TransactionId { get; set; }
public DateTime Date { get; set; }
// be sure to use the products DTO here and not the entity because the entity has the loop
public virtual ICollection<AllOrderedProductDTO> OrderedProducts { get; set; }
}
public class AllOrderedProductDTO
{
public int Id { get; set; }
public string Product { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public bool Returned { get; set; }
public int TransactionId { get; set; }
// do not include the transaction entity or DTO here so that we avoid the loop
//public virtual UserTransaction UserTransaction { get; set; }
}
I have a similar issue. I solve this problem by adding decoration.
Reference documents:https://learn.microsoft.com/en-us/ef/core/querying/related-data/serialization
public class OrderedProduct
{
public int Id { get; set; }
public string Product { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public bool Returned { get; set; }
public int TransactionId { get; set; }
[JsonIgnore]
public virtual UserTransaction UserTransaction { get; set; }
}
Related
Given two tables, TrackingTag and TrackingTagStatusUpdate:
public class TrackingTag
{
public int ID { get; set; }
}
public class TrackingTagStatusUpdate
{
public int ID { get; set; }
public int TrackingTagID { get; set; }
public TrackingTag TrackingTag { get; set; }
public int Epoch { get; set; } //32-bit
[MaxLength(32)]
public string APConnectedSSID { get; set; }
}
As there will be many TrackingTagStatusUpdates, I want to add a field "LatestStatusUpdate" to TrackingTag, for performance reasons.
public class TrackingTag
{
public int ID { get; set; }
public int? LatestStatusUpdateID { get; set; }
public TrackingTagStatusUpdate LatestStatusUpdate { get; set; }
}
LatestStatusUpdate is optional, as it may not be set if there are not yet any Status Updates for the Tag.
Entity Framework Core complains that "The child/dependent side could not be determined for the one-to-one relationship between 'TrackingTag.LatestStatusUpdate' and 'TrackingTagStatusUpdate.TrackingTag'.". I then add
modelBuilder.Entity<TrackingTag>().HasOne(x => x.LatestStatusUpdate).WithOne(x => x.TrackingTag).HasForeignKey<TrackingTagStatusUpdate>(x => x.TrackingTagID);
to OnModelCreating(ModelBuilder modelBuilder), however this results in Entity Framework Core creating a relationship with a Unique constraint, which will not work as there will be many TrackingTagStatusUpdate with the same TrackingTagID.
How do I do this correctly?
This seems to have worked:
public class TrackingTag
{
public int ID { get; set; }
[ForeignKey(nameof(LatestStatusUpdate))]
public int? LatestStatusUpdateID { get; set; }
public TrackingTagStatusUpdate LatestStatusUpdate { get; set; }
}
I'm not sure how to achieve the same with the Fluent API though.
I'm working on a trucking API using Entity Framework (EF) Core. Basic CRUD operations are working fine using the repository pattern. There is an error in
configurations I am implementing, however.
I want to obtain multiple trailers and trucks associated with single load, reflecting the one-to-many relationship.
public class LoadConfiguration : IEntityTypeConfiguration<Load>
{
public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<Load> builder)
{
builder.Property(p=>p.Id).IsRequired();
builder.HasOne(t=>t.Customer).WithMany().HasForeignKey(p=>p.CustomerId);
builder.Property(p=>p.LoadedFrom).IsRequired();
builder.HasMany(p=>p.Trailer).WithOne().HasForeignKey(t=>t.TrailerId);
builder.HasMany(p=>p.Truck).WithOne().HasForeignKey(t=>t.TruckId);
builder.Property(p=>p.Destination).IsRequired();
}
}
public class Truck:BaseEntity
{
public int PlateNo { get; set; }
public string ModelName { get; set; }
public Location StateCode { get; set; }
public int PollutionCertificateValidity { get; set; }
public int DateOfPurchase { get; set; }
public int FitnessCertificateValidity { get; set; }
}
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public Trailer Trailer { get; set; }
public int TrailerId { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
public string Destination { get; set; }
}
public class Trailer:BaseEntity
{
public int TrailerCapacity { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
}
public class BaseEntity
{
public int Id { get; set; }
}
A one-to-many relationship is defined by using navigation collections, that has the capacity to hold many Trucks and Trailers. You can choose the collection type freely, but I would suggest ICollection generic type.
Modify your Load class as follows:
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public string Destination { get; set; }
// navigation collections
public ICollection<Trailer> Trailers { get; set; }
public ICollection<Truck> Trucks { get; set; }
}
You will then be able to set up the relationship in your LoadConfiguration class by using
the pluralized name:
builder.HasMany(p=>p.Trailers).WithOne();
builder.HasMany(p=>p.Trucks).WithOne();
.. even though EF Core will be smart enough to figure out the relation by convention so the fluent configuration is redundant.
I am using EF Core together with ASP NET Core for my server, and when I am trying to update an existing value in the database I receive the following error:
Cannot insert explicit value for identity column in table 'TeambuildingType' when IDENTITY_INSERT is set to OFF.
What I am doing is this:
creating a Teambuilding element, with the foreign key for the TeamBuildingTypeId set to NULL initially
creating two TeambuildingType directly from the SQL Management Studio using INSERT INTO.... (the Id is auto incremented for both the Teambuilding and TeambuildingType)
trying to update the existing Teambuilding by adding either the TeambuildingTypeId like this: team.TeambuildingTypeId = 1 or team.Type = (object fetched from the database in the same context)
receiving the error from above in a catch
Here is my code:
TeamBuilding.cs
public class TeamBuilding
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public double? TargetBudget { get; set; }
public TeambuildingStatus? Status { get; set; }
public int? TeambuildingTypeId { get; set; }
public virtual TeambuildingType Type { get; set; }
public int? LocationId { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<Participant> Participants { get; set; }
public virtual ICollection<Room> Rooms { get; set; }
public int TimePollId { get; set; }
public virtual TimePoll TimePoll { get; set; }
}
TeambuildingType.cs
public class TeambuildingType
{
[Key]
public int Id { get; set; }
public string Type { get; set; }
public virtual ICollection<TeamBuilding> Teambuildings { get; set; }
}
TeamBuildingForUpdateDto.cs
public class TeamBuildingForUpdateDto
{
[Required]
public int Id { get; set; }
public string Title { get; set; }
[DataType(DataType.DateTime)]
public DateTime StartDate { get; set; }
[DataType(DataType.DateTime)]
public DateTime EndDate { get; set; }
public LocationViewModel Location { get; set; }
public TeambuildingStatus Status { get; set; }
public double TargetBudget { get; set; }
public TeamBuildingTypeDto Type { get; set; }
}
The update controller method:
[HttpPut]
public IActionResult UpdateTeamBuilding([FromBody]TeamBuildingForUpdateDto teamBuildingForUpdateDto)
{
try
{
var existingTeamBuilding = _service.GetByID(teamBuildingForUpdateDto.Id);
if (existingTeamBuilding == null)
{
return NotFound("There is no team buiding with such an ID");
}
_service.UpdateTeamBuilding(teamBuildingForUpdateDto);
return Ok();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
The service method:
public TeamBuildingForUpdateDto UpdateTeamBuilding(TeamBuildingForUpdateDto teamBuildingDto)
{
var teamBuilding = _repositoryTeam.GetByID(teamBuildingDto.Id);
var type = _repositoryType.GetByID(teamBuildingDto.Type.Id);
Mapper.Map(teamBuildingDto.Type, type);
Mapper.Map(teamBuildingDto, teamBuilding);
teamBuilding.Type = type;
//OR
//teamBuilding.TeambuildingTypeId = type.Id;
//Neither from above works
_repositoryTeam.Edit(teamBuilding);
_repositoryTeam.Commit();
return teamBuildingDto;
}
Context using the Fluent API:
modelBuilder.Entity<Models.TeamBuilding>()
.HasOne(t => t.Type)
.WithMany(ty => ty.Teambuildings)
.HasForeignKey(t => t.TeambuildingTypeId);
modelBuilder.Entity<TeambuildingType>().ToTable("TeambuildingType");
modelBuilder.Entity<Models.TeamBuilding>().ToTable("TeamBuilding");
public DbSet<TeambuildingType> TeambuildingTypes { get; set; }
public DbSet<Models.TeamBuilding> TeamBuildings { get; set; }
I also don't receive this error on those models only, I receive the same thing on anything that uses FK and on where I try to insert a new value in there.
The relationship is one to many between the TeamBuilding and the TeambuildingType
What am I doing wrong?
I fixed the problem. As per mcbowes suggestion I checked the AutoMapper and what I send from the server, and I saw that I was trying to assign a TeamBuildingType in my TeamBuilding Type field, then trying to do the update.
I fixed the problem by not assigning any TeamBuildingType to the Type field (making it being null) and assigning only the TeamBuildingType primary key to the TeambuildingTypeId field. Now it does the update.
Thanks mcbowes for the suggestion.
You can see my previous question which related with many to many relation but with auto generated mapping table.
I have 2 model, HrTraining and HrPerson. Any people can be assigned to one or more Trainings. You can see my model as below
public class HrTraining
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapTrainingPerson> HrMapTrainingPerson { get; set; }
}
public class HrMapTrainingPerson
{
public int Id { get; set; }
public string Status { get; set; }
public int HrTrainingId { get; set; }
public int HrPersonId { get; set; }
public virtual HrTraining HrTraining { get; set; }
public virtual HrPerson HrPerson { get; set; }
}
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapTrainingPerson> HrMapTrainingPerson { get; set; }
}
How can i take all training objects which assingned to a person with efficient way.
So you want to find a person, and get all the trainings assigned to it? There are lot of ways.. but using your models, this could be something like this
var trPersons = dbContext.HrPerson.Find(idPerson).HrMapTrainingPerson.ToList();
foreach(var trPerson in trPersons) {
var training = trPerson.HrTraining;
//do what you want, here you can get trPerson.HrTraining.Name for instance
}
I am trying to return as JSON the fully deep object (with all of the foreign key relationships filled in) but I am getting nulls for all the referenced objects.
Here is the call to get the object:
public ActionResult GetAll()
{
return Json(ppEFContext.Orders, JsonRequestBehavior.AllowGet);
}
And here is the Order object itself:
public class Order
{
public int Id { get; set; }
public Patient Patient { get; set; }
public CertificationPeriod CertificationPeriod { get; set; }
public Agency Agency { get; set; }
public Diagnosis PrimaryDiagnosis { get; set; }
public OrderApprovalStatus ApprovalStatus { get; set; }
public User Approver { get; set; }
public User Submitter { get; set; }
public DateTime ApprovalDate { get; set; }
public DateTime SubmittedDate { get; set; }
public Boolean IsDeprecated { get; set; }
}
I have not yet found a good resource on using EF 4.1 Annotations. If you could suggest a good one, that has the answer, you could give me the link and that would be enough of an answer for me!
Regards,
Guido
Update
I added the virtual keyword as per Saxman and am now
dealing with the circular reference
error issue.
Add the virtual keyword before your related entities:
public class Order
{
public int Id { get; set; }
public virtual Patient Patient { get; set; }
public virtual CertificationPeriod CertificationPeriod { get; set; }
public virtual Agency Agency { get; set; }
public virtual Diagnosis PrimaryDiagnosis { get; set; }
public virtual OrderApprovalStatus ApprovalStatus { get; set; }
public virtual User Approver { get; set; }
public virtual User Submitter { get; set; }
public DateTime ApprovalDate { get; set; }
public DateTime SubmittedDate { get; set; }
public Boolean IsDeprecated { get; set; }
}
You might end up with a A circular reference was detected while serializing an object... error if your objects have references of each other. In that case, you will need to create a ViewModel or something similar to overcome this problem. Or use LINQ to project an anonymous object.
Read about Loading Related Objects