Entity Framework - 6, Parent Key propagation to grand child object - entity-framework

public class Submission
{
public Submission()
{
this.VisitOccurrences = new HashSet<VisitOccurrence>();
}
public int SubmissionKey { get; set; } //PK
public string Version { get; set; }
public string ClientName { get; set; }
public string UserName { get; set; }
public DateTime SubmissionStartDate { get; set; }
public virtual ICollection<VisitOccurrence> VisitOccurrences { get; set; }
}
public class VisitOccurrence
{
public VisitOccurrence()
{
this.Observations = new HashSet<VoObservation>();
}
public int VisitOccurrenceKey { get; set; } //PK
//public int SubmissionKey { get; set; } //FK from submission . This is commented as this value inserted by EF in table. Refer to configuration below.
public string VisitConcept { get; set; }
public DateTime? VisitStartDate { get; set; }
public TimeSpan? VisitStartTime { get; set; }
public DateTime? VisitEndDate { get; set; }
public TimeSpan? VisitEndTime { get; set; }
public virtual ICollection<Observation> Observations { get; set; }
}
public class Observation
{
public int ObservationKey { get; set; } //PK
//public int VisitOccurrenceKey { get; set; } //FK from VisitOccurrence. This is commented as this value inserted by EF in table. Refer to configuration below.
//public int SubmissionKey { get; set; } // ***there is no relation on this column but I need to propagate value from parent class Submission to this grand child column when inserted Submission. What is the best way to do it?***
public string Element { get; set; }
public string ObservationConcept { get; set; }
public DateTime? ObservationDate { get; set; }
public TimeSpan? ObservationTime { get; set; }
}
public class SubmissionConfiguration : EntityTypeConfiguration<Submission>
{
public SubmissionConfiguration()
{
ToTable("Submission");
HasKey(p => p.Id).Property(p => p.Id).HasColumnName("SubmissionKey");
HasMany(p => p.VisitOccurrences).WithOptional().Map(m => m.MapKey("SubmissionKey"));
}
}
public class VisitOccurrenceConfiguration : EntityTypeConfiguration<VisitOccurrence>
{
public VisitOccurrenceConfiguration()
{
ToTable("VisitOccurrence");
Property(p => p.Id).HasColumnName("VisitOccurrenceKey");
HasMany(p => p.Observations).WithOptional().Map(m => m.MapKey("VisitOccurrenceKey"));
}
}
public class ObservationConfiguration : EntityTypeConfiguration<Observation>
{
public VoObservationConfiguration()
{
ToTable("Observation");
}
}
//Repository Method to insert Submission
public virtual void AddOrUpdate(Submission model)
{
Context.Entry(model).State = EntityState.Added;
Context.SaveChanges();
}
I have a Parent entity Submission, a Child entity VisitOccurrence and Grand child entity Observation. My configurations works When I inserted a Submission with child and grand child entities in it. All key columns populated in related tables. However, The above code inserts Submission and the identity generated in SubmissionKey is inserted in VisitOccurence and thus the VisitOccurenceKey in Observation table. But I'm looking for Configuration that could propagate SubmissionKey from VisitOccurence to Observation though there is no relation constraint added in database. please help.

Related

EFCore Generic Repository and UnitOfWork Design Pattern

when im trying to create new data and save it, im getting error at the
public int Complete()
{
return _context.SaveChanges();
}
and error is saying me that:
The value of 'Agency.ID' is unknown when attempting to save changes. This is because the property is also part of a foreign key for which the principal entity in the relationship is not known. .
i have a Base class like that:
public class Base
{
protected Base()
{
CreatedDate = DateTime.Now;
IsDeleted = false;
ModifiedDate = null;
}
public int ID { get; set; }
public int? CreatedUserId { get; set; }
public int? ModifiedUserId { get; set; }
public string CreatedUserType { get; set; }
public string ModifiedUserType { get; set; }
public DateTime? CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public bool? IsActive { get; set; }
public bool? IsDeleted { get; set; }
}
i have a Agency class like that :
public class Agency : Base
{
public Agency()
{
AgencyIsComplated = false;
}
[Required, StringLength(255)]
public string AgencyName { get; set; }
[Required, StringLength(255)]
public string AgencyPhoto { get; set; }
[Required, StringLength(255)]
public string AgencyEMail { get; set; }
[Required, StringLength(13)]
public string AgencyPhone { get; set; }
[StringLength(13)]
public string AgencyBPhone { get; set; }
[StringLength(255)]
public string AgencyInfo { get; set; }
[Required, StringLength(255)]
public string AgencyTitle { get; set; }
[Required, StringLength(255)]
public string AgencyLink { get; set; }
public int AgencyExportArea { get; set; } // Join table ile yapılacak,ayrı bir tabloda tutulacak
[Required, StringLength(255)]
public string AgencyInstagram { get; set; }
public string AgencyTwitter { get; set; }
public string AgencyFacebook { get; set; }
public string AgencyLinkedin { get; set; }
public string AgencyYoutube { get; set; }
public bool AgencyIsComplated { get; set; }
[ForeignKey("CompanyID")]
public Company Company { get; set; }
[ForeignKey("LogID")]
public Log Log { get; set; }
public virtual ICollection<AgencyCompany> AgencyCompanies { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Log> Logs { get; set; }
}
public class AgencyConfiguration : IEntityTypeConfiguration<Agency>
{
public void Configure(EntityTypeBuilder<Agency> builder)
{
builder.HasKey(agency => agency.ID);
builder.HasMany(a => a.Logs)
.WithOne(a => a.Agency)
.HasForeignKey(a=>a.ID)
.OnDelete(DeleteBehavior.Restrict);
builder.HasMany(us => us.Users)
.WithOne(us => us.Agency)
.HasForeignKey(au=>au.ID)
.OnDelete(DeleteBehavior.Restrict);
builder.HasMany(ac => ac.AgencyCompanies)
.WithOne(ac => ac.Agency)
.OnDelete(DeleteBehavior.Restrict);
}
}
and i have got a UnitOfWork like that:
public class UnitOfWork : IUnitOfWork
{
private TradeTurkDBContext _context;
public UnitOfWork(TradeTurkDBContext context)
{
_context = context;
RepositoryAgency = new RepositoryAgency(_context);
}
public IRepository Repository { get; private set; }
public IRepositoryAgency RepositoryAgency { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
im inheriting that ID on my Base Model.
the problem is getting solved when im not defining ID in the base model but i allready set up my mapping on it.
so how can i solve that error without using AgencyID in the Agency model ?
The foreign key is in the details (or child) table. Therefore, e.g. a user, should have an AgencyId as foreign key.
builder.Entity<User>()
.HasOne(u => u.Agency)
.WithMany(a => a.Users)
.HasForeignKey(u => u.AgencyId)
.OnDelete(DeleteBehavior.Restrict);
This key automatically points to the primary key of the master (or parent) table.
User.ID is a primary key. User.AgencyId is a foreign key which (automatically) relates to the primary key Agency.ID.
E.g. see: Configure One-to-Many Relationships using Fluent API in Entity Framework Core

EF Core Returns one Record where Many are Expected when Using Foreign Key Relationship

I have a database that stores data regarding Facilities, Doctors, and revenue for both of the previous items - FacilityRevenue and DoctorRevenue. There are also FaciltyMaster and DoctorMaster tables that have a one to many relationship with the FacilityRevenue and DoctorRevenue tables. That is, one doctor or facility master record is related to many DoctorId or FacilityId records in the FacilityRevenue and DoctorRevenue tables. I've attempted to place foreign key relationships so that DoctorId on DoctorRevenue relates to DoctorId on DoctorMaster and FacilityId on FacilityRevenue relates to FacilityId on FaclityMaster. However, I'm not confident that Entity Framework is reading this as such.
The model for each is as follows:
public partial class FacilityMaster
{
public FacilityMaster()
{
DoctorRevenue = new HashSet<DoctorRevenue>();
FacilityRevenue = new HashSet<FacilityRevenue>();
}
[Key]
public int FacilityId { get; set; }
public string FacilityName { get; set; }
public virtual ICollection<DoctorRevenue> DoctorRevenue { get; set; }
public virtual ICollection<FacilityRevenue> FacilityRevenue { get; set; }
}
public partial class DoctorMaster
{
public DoctorMaster()
{
DoctorRevenue = new HashSet<DoctorRevenue>();
}
[Key]
public int DoctorId { get; set; }
public string DoctorName { get; set; }
public string DoctorSpecialty { get; set; }
public virtual ICollection<DoctorRevenue> DoctorRevenue { get; set; }
}
public partial class DoctorRevenue
{
[Key]
public int RecordId { get; set; }
public int DoctorId { get; set; }
public int FacilityId { get; set; }
public string FacilityName { get; set; }
public string DoctorName { get; set; }
public DateTime? Date { get; set; }
public decimal? DoctorInvoices { get; set; }
public decimal? TotalRevenue { get; set; }
public virtual DoctorMaster Doctor { get; set; }
public virtual FacilityMaster Facility { get; set; }
}
public partial class FacilityRevenue
{
[Key]
public int RecordId { get; set; }
public int FacilityId { get; set; }
public string FacilityName { get; set; }
public DateTime Date { get; set; }
public decimal? TotalInvoices { get; set; }
public decimal? TotalRevenue { get; set; }
public virtual FacilityMaster Facility { get; set; }
}
I have configured, in part, my FacilityRevenueRepository as follows:
public IEnumerable<FacilityRevenue> GetFacRevenues(Int32 pageSize, Int32 pageNumber, String name)
{
var query = _context
.Set<FacilityRevenue>()
.AsQueryable()
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize);
if (!String.IsNullOrEmpty(name))
{
query = query.Where(item => item.FacilityName.Contains(name));
}
return query;
}
The relevant portion of my FacilityRevenueController is as follows:
[HttpGet]
[Route("GetFacilityRevenues")]
public async Task<IActionResult> GetFacilityRevenues(Int32? pageSize = 10, Int32? pageNumber = 1, String FacilityName = null)
{
var response = new ListModelResponse<FacRevViewModel>() as IListModelResponse<FacRevViewModel>;
try
{
response.PageSize = (Int32)pageSize;
response.PageNumber = (Int32)pageNumber;
response.Model = await Task.Run(() =>
{
return FacilityRevenueRepository
.GetFacRevenues(response.PageNumber, response.PageSize, FacilityName)
.Select(item => item.ToViewModel())
.ToList();
});
response.Message = String.Format("Total Records {0}", response.Model.Count());
}
catch (Exception ex)
{
response.DidError = true;
response.ErrorMessage = ex.Message;
}
return response.ToHttpResponse();
}
The DbContext is as follows:
public partial class ERPWAGDbContext : DbContext
{
public ERPWAGDbContext(DbContextOptions<ERPWAGDbContext> options)
:base(options)
{ }
public DbSet<DoctorMaster> Doctors { get; set; }
public DbSet<FacilityMaster> Facilities { get; set; }
public DbSet<DoctorRevenue> DoctorRevenue { get; set; }
public DbSet<FacilityRevenue> FacilityRevenue { get; set; }
}
When I run this using dotnet run, Postman returns just one record for GetFacilityRevenues, where several hundred are expected.
How do I ensure that all records for a given facility are returned, and likewise for doctors, when my GetFacilities and GetDoctors API methods are called?

Alternative to List.Include in EF

I am having circular reference due to the EF code below. I have a Parent table with 2 childs and their grandchildren respectively. Is there any alternative to using Include, as I read something about Navigation property and it includes the parents in each of the child, causing the serialization having circular reference problem.
Error Message:
A circular reference was detected while serializing an object of type 'Product'.
This part is only to get retrieval and I don't need the parent reference back to each of the child. I debug inside and saw that inside each of the child, it has the parent reference back.
var query = db.Products.Include(c => c.ProductTriggers.Select(b => b.ProductTriggerItems))
.Include(d => d.ProductsExtra.Select(e => e.ProductAllotments.Select(m => m.ProductAllotmentDetails))).AsNoTracking();
return query.ToList();
Product Class
public partial class Product
{
public Product()
{
this.ProductExtras = new HashSet<ProductExtra>();
this.ProductTriggers = new HashSet<ProductTrigger>();
}
public int ProductID { get; set; }
public string ProductCode { get; set; }
public string ProductName { get; set; }
public virtual ICollection<ProductProduct> ProductExtras { get; set; }
public virtual ICollection<ProductTrigger> ProductTriggers { get; set; }
}
ProductExtra Class
public partial class ProductExtra
{
public ProductExtra()
{
this.ProductAllotments = new HashSet<ProductAllotment>();
}
public int ProductExtraID { get; set; }
public int ProductID { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<ProductAllotment> ProductAllotments { get; set; }
}
ProductAllotment Class
public partial class ProductAllotment
{
public ProductAllotment()
{
this.ProductAllotmentDetails = new HashSet<ProductAllotmentDetail>();
}
public int ProductAllotmentID { get; set; }
public int ProductExtraID { get; set; }
public virtual ProductExtra ProductExtra { get; set; }
public virtual ICollection<ProductAllotmentDetail> ProductAllotmentDetails { get; set; }
}
ProductTrigger Class
public partial class ProductTrigger
{
public ProductTrigger()
{
this.AddOnTriggerItems = new HashSet<ProductTriggerItem>();
}
public int ProductTriggerID { get; set; }
public int ProductID { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<ProductTriggerItem> ProductTriggerItems { get; set; }
}
ProductTriggerItem Class
public partial class ProductTriggerItem
{
public int ProductTriggerItemID { get; set; }
public int ProductTriggerID { get; set; }
public virtual ProductTrigger ProductTrigger { get; set; }
}

Returning Entity with its children

Hi I am trying to return all vehicles with their recorded mileage through an api using ASP.Net Core with the following code:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.Include(m=>m.Mileages).ToList();
}
However this only returns the first vehicle with its mileages and not the others (there are five dummy vehicles in the db all with an initial mileage).
If I change the code to:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.ToList();
}
it returns the full list of vehicles but no mileage.
My class files are:
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<Mileage> Mileages { get; set; }
}
and
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public Vehicle Vehicle { get; set; }
}
thanks for looking!
Tuppers
you can have them auto-load (lazy loading) using proxies... but for that, your foreign entities and collections must be marked virtual in your POCOs:
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public virtual Vehicle Vehicle { get; set; }
}
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public virtual ICollection<Mileage> Mileages { get; set; }
}
The proxy creation and lazy loading turned on, but that's the default in EF6.
https://msdn.microsoft.com/en-us/data/jj574232.aspx
Let me know if this works.
Well after a lot of searching I managed to find a solution. I used the following:
[HttpGet]
public IEnumerable<VehicleDto> Get()
{
var query = _context.Vehicles.Select(v => new VehicleDto
{
Registration = v.Registration,
Make = v.Make,
Model = v.Model,
Marked = v.Marked,
Mileages = v.Mileages.Select(m => new MileageDto
{
MileageDate = m.MileageDate,
RecordedMileage = m.RecordedMileage
})
.ToList(),
})
.ToList();
return (IEnumerable<VehicleDto>) query.AsEnumerable();
this doesn't seem to be the most elegant way of doing this, if anyone could offer any advice but it does return what is required.
The DTO's look like:
public class VehicleDto
{
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<MileageDto> Mileages { get; set; }
}
and
public class MileageDto
{
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
}
Thanks for taking the time to look at this
Tuppers

Sum related properties in entity framework

I have a problem with sum of navigation properties using entity framework
Here is my example classes
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ObservableCollection<Call> Calls { get; set; }
[NotMapped]
public decimal TotalCallDuration { get { return Calls.Sum(c => c.Value); } }
}
public class Call
{
public int Id { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public decimal Value { get; set; }
public DateTime Date { get; set; }
}
This works well but when i have hundreds of records it is very slow
How can i make this faster but without losing functionality?
Thanks
what you want to do is:
customer.TotalCallDuration = context.Call.Sum(x => x.Value).Where(x => x.CustomerID == customer.Id);