How to Mapping TPH in ownsmany - entity-framework

I have three classes for abstract Payment.
public class CardPayment : Payment
{
public CardPayment(Guid distributeId, Guid invoiceId, int userId, decimal amount, DateTime dateTime, long serialNumbner, long terminalNumber, long referenceNumber, DateTime bankDateTime) : base(distributeId, invoiceId, userId, amount, dateTime)
public long SerialNumber { get;private set; }
public long TerminalNumber { get; private set; }
public long ReferenceNumber { get; set; }
public DateTime BankDateTime { get; set; }
}
public class Payment: EntityBase<Guid>
{
public Guid DistributeId { get; private set; }
public Guid InvoiceId { get; private set; }
public int UserId { get; private set; }
public decimal Amount { get; set; }
public DateTime DateTime { get; set; }
public PaymentType Type { get; set; }
}
public class CashPayment : Payment
{
public CashPayment(Guid distributeId, Guid invoiceId, int userId, decimal amount, DateTime dateTime) : base(distributeId, invoiceId, userId, amount,dateTime)
{
}
}
public class ChequePayment : Payment
{
public string SerialNumber { get; private set; }
public int BankId { get; private set; }
public string BankBranchName { get; set; }
public string NationalCode { get; set; }
public string BankBranchCode { get; set; }
public DateTime DueDate { get; set; }
public string BankAccountNumber { get; set; }
public string SayddiNumber { get; set; }
}
These show above.
I use ef-core. I want to use TPH for persistence.
public class Invoice : EntityBase<Guid>, IAggregateRoot<Invoice>
{
protected List<Payment> _payments = new List<Payment>();
public IReadOnlyCollection<Payment> Payments => _payments.AsReadOnly();
}
On the other hand, I use the base class for mapping Aggregate roots like this.
public abstract class EntityMappingBase<TEntity> : IEntityTypeConfiguration<TEntity>
where TEntity : class,IEntityBase, IAggregateRoot<TEntity>
{
public abstract void Configure(EntityTypeBuilder<TEntity> builder);
}
}
public class InvoiceMapping : EntityMappingBase<Invoice>
{
public override void Configure(EntityTypeBuilder<Invoice> builder)
{
Initial(builder);
builder.OwnsMany(a => a.Payments, map =>
{
map.Property(i => i.Id)
.ValueGeneratedNever();
map.HasKey(i => i.Id);
map.WithOwner().HasForeignKey("InvoiceId");
map.UsePropertyAccessMode(PropertyAccessMode.Field);
ToTable(map);
});
}
}
But I don't map these classes into The Invoice Class.

Related

Entity Framework get all data from table with foreign key

Please help. I can not resolve the issue - the category name is null.
Thanks a lot.
The models
public class Product
{
[Key]
public int ProductID { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public int CategoryID { get; set; }
[ForeignKey("CategoryID")]
public virtual Category Category { get; set; }
}
public class Category
{
[Key]
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
`
Product Controller
public async Task<IEnumerable<ProductDto>> GetProductsAsync()
{
List<Product> products = await _db.Products
.Include(u => u.Category)
.ToListAsync();
return _mapper.Map<List<ProductDto>>(products);
}
public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<ProductDto, Product>().ReverseMap();
}
}
public class ProductDto
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public string CategoryName { get; set; }
}
How to resolve null fields?
Change your MappingProfiles like below:
public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<Product, ProductDto>()
.ForMember(d => d.CategoryName, a => a.MapFrom(s => s.Category.CategoryName))
.ReverseMap()
.ForPath(b => b.Category, o => o.MapFrom(dto => (Category)null));
}
}

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

Could not find the implementation of the query pattern for source type. Join not found

I don't know if I am doing this right. I have 2 tables Property and PropertyTypes. Each Property has 1 PropertyType. I am using a foreign key constraint. But on the creation of the controller, I get this error already:
"Could not find an implementation of the query pattern for source type 'DbSet'.'Join not found'
Please see my code below:
[Table("Property.Property")]
public class Property
{
[Key]
public int PropertyId { get; set; }
[StringLength(50)]
public string PropertyName { get; set; }
public int? Owner { get; set; }
public string Cluster { get; set; }
public string PropertyNumber { get; set; }
public string RegionCode { get; set; }
public string ProvinceCode { get; set; }
public string MunicipalCode { get; set; }
public string BarangayCode { get; set; }
public DateTime? DateAdded { get; set; }
public DateTime? DateModified { get; set; }
public int PropertyTypeId { get; set; }
public PropertyType PropertyType { get; set; }
[NotMapped]
public string Type { get; set; }
}
[Table("Property.Types")]
public class PropertyType
{
[Key]
public int PropertyTypeId { get; set; }
[StringLength(50)]
public string Type { get; set; }
public DateTime? DateAdded { get; set; }
public DateTime? DateModified { get; set; }
public List<Property> Properties { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false) {}
// DB Sets
public DbSet<Property> Properties { get; set; }
public DbSet<PropertyType> PropertyTypes { get; set; }
}
Controller
public class PropertyController : ApiController
{
[HttpGet]
[Authorize]
[Route("api/getproperties")]
public async Task<List<Property>> GetProperties()
{
using(var db = new ApplicationDbContext())
{
var properties = await (from p in db.Properties
join pt in db.PropertyTypes
on p.PropertyTypeId equals pt.PropertyTypeId
select new
{
PropertyId = p.PropertyId,
PropertyName = p.PropertyName,
Owner = p.ProertyOwner,
Cluster = p.Cluster,
PropertyNumber = p.PropertyNumber,
RegionCode = p.RegionCode,
ProvinceCode = p.ProvinceCode,
MunicipalCode = p.MunicipalCode,
BarangayCode = p.BarangayCode,
DateAdded = p.DateAdded,
DateModified = p.DateModified,
PropertyTypeId = p.PropertyTypeId,
type = pt.Type
}
).ToListAsync();
return properties;
}
}
}
Can you please show me the right way to do this? Thank you.

How do I get a response based on two different IDs in my API?

public class Report
{
[Key]
public int ReportId { get; set; }
[ForeignKey("Subjects")]
public int SubjectId { get; set; }
public Subjects Subjects { get; set; }
[ForeignKey("Teacher")]
public int TeacherId { get; set; }
public Teacher Teacher { get; set; }
[ForeignKey("MarkType")]
public int MarkTypeId { get; set; }
public MarkType MarkType { get; set; }
}
public class Teacher
{
[Key]
public int TeacherId { get; set; }
[MaxLength(50)]
[Required]
public string FName { get; set; }
[MaxLength(50)]
[Required]
public string LName { get; set; }
}
public class Student
{
[Key]
public int StudentId { get; set; }
[MaxLength(50)]
[Required]
public string FName { get; set; }
[MaxLength(50)]
[Required]
public string LName { get; set; }
[ForeignKey("Grade")]
public int GradeId { get; set; }
public Grade Grade { get; set; }
}
public class Grade
{
[Key]
public int GradeId { get; set; }
public int StudentGrade { get; set; }
}
public class Subjects
{
[Key]
public int SubjectId { get; set; }
[MaxLength(50)]
[Required]
public string SubjectName { get; set; }
}
public class Terms
{
[Key]
public int TermId { get; set; }
public int Term { get; set; }
}
public class MarkType
{
[Key]
public int MarkTypeId { get; set; }
[MaxLength(20)]
[Required]
public string TypeName { get; set; }
}
public class StudentMark
{
[Key]
public int StudentMarkId { get; set; }
[ForeignKey("Report")]
public int ReportId { get; set; }
public Report Report { get; set; }
[ForeignKey("Student")]
public int StudentId { get; set; }
public Student Student { get; set; }
public int Mark { get; set; }
[ForeignKey("Terms")]
public int TermId { get; set; }
public Terms Terms { get; set; }
}
In the API I am making I want to have the ability to use two different IDs to get a more specific response.
var report = ReportDBContext.StudentMark
.Include(p => p.Student.Grade).Include(p => p.Report)
.Include(p => p.Terms).Include(a => a.Report.Subjects).Include(a => a.Terms)
.Include(a => a.Report.MarkType).Include(a => a.Report.Teacher).ToList();
This allowed me to get StudentMark as well as it's related entities but I want to have the ability to use The student's Id and the Term's Id to get a student's marks for that term and all the subjects related to the student. I am a beginner to Web API so please let me know if I need to add more context.
If you want to query by either StudentId or TermId, I suggest that you provide two different endpoints for these two different queries. Use LINQ Where to check your conditions.
public StudentMark[] GetMarksByStudentId(int studentId) {
return ReportDBContext.StudentMark
/* .Include(...) */
.Where(mark => mark.StudentId == studentId)
.ToArray();
}
public StudentMark[] GetMarksByTermId(int termId) {
return ReportDBContext.StudentMark
/* .Include(...) */
.Where(mark => mark.TermId == termId)
.ToArray();
}
If you want to query by StudentId and TermId simultaneously, introduce a query object to encapsulate your parameters. You can test for multiple conditions in the Where clause with AND &&.
public StudentMark[] FindMarks(StudentMarkQuery query) {
return ReportDBContext.StudentMark
/* .Include(...) */
.Where(mark => mark.StudentId == query.StudentId
&& mark.TermId == query.TermId)
.ToArray();
}
The StudentMarkQuery class is introduced so you can add additional parameters without changing the overall signature of the endpoint:
public class StudentMarkQuery {
public int StudentId { get; set; }
public int TermId { get; set; }
}

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?