Entity Framework how to map composite entities? - entity-framework

I'm newbie in EF and i have such question:
I have four Entities, which describe the elements of the address:
public partial class Region : BaseEntity
{
private ICollection<RegionCity> _regionCities;
public string Code { get; set; }
public string Name { get; set; }
public int Timezone { get; set; }
public virtual ICollection<RegionCity> RegionCities
{
get
{
return _regionCities ?? (_regionCities = new List<RegionCity>());
}
protected set { _regionCities = value; }
}
}
public partial class RegionCity : BaseEntity
{
private ICollection<CityStreet> _cityStreets;
public int RegionId { get; set; }
public string CityName { get; set; }
public virtual Region Region { get; set; }
public virtual ICollection<CityStreet> CityStreets
{
get { return _cityStreets ?? (_cityStreets = new List<CityStreet>()); }
protected set { _cityStreets = value; }
}
}
public partial class CityStreet : BaseEntity
{
public int CityId { get; set; }
public string StreetName { get; set; }
public virtual RegionCity City { get; set; }
}
public partial class FullAddress : BaseEntity
{
public int CityStreetId { get; set; }
public string HouseNum { get; set; }
public string OfficeNum { get; set; }
public virtual CityStreet Street { get; set; }
}
And I have mapping classes:
public class RegionMap : EntityTypeConfiguration<Region>
{
public RegionMap()
{
this.ToTable("Region");
this.HasKey(r => r.Id);
this.Property(r => r.Name).HasMaxLength(64);
}
}
public class RegionCityMap : EntityTypeConfiguration<RegionCity>
{
public RegionCityMap()
{
this.ToTable("RegionCity");
this.HasKey(rc => rc.Id);
this.HasRequired(rc => rc.Region)
.WithMany(r => r.RegionCities)
.HasForeignKey(rc => rc.RegionId)
.WillCascadeOnDelete(false);
}
}
public class CityStreetMap : EntityTypeConfiguration<CityStreet>
{
public CityStreetMap()
{
this.ToTable("CityStreet");
this.HasKey(cs => cs.Id);
this.HasRequired(cs => cs.City)
.WithMany(c => c.CityStreets)
.HasForeignKey(cs => cs.CityId)
.WillCascadeOnDelete(false);
}
}
public class FullAddressMap : EntityTypeConfiguration<FullAddress>
{
public FullAddressMap()
{
this.ToTable("FullAddress");
this.HasKey(fu => fu.Id);
this.Property(fu => fu.HouseNum).HasMaxLength(16);
this.HasRequired(fu => fu.Street)
.WithMany()
.HasForeignKey(fu => fu.CityStreetId)
.WillCascadeOnDelete(false);
}
}
How i can write mapping class, if i will change class FullAddress as show follow, so that it will justifies its name:
public partial class FullAddress : BaseEntity
{
public int CityStreetId { get; set; }
public string HouseNum { get; set; }
public virtual Region Region { get; set; }
public virtual RegionCity City { get; set; }
public virtual CityStreet Street { get; set; }
}
Can i to map RegionCity through CityStreet and then Region through RegionCity?

You can use NotMapped properties to traverse the hierarchy. Or you can declare extra Navigaion Properties and Foreign Keys that skip levels.
If you change the key structure to use compound keys (which IMO is a best practice here anyway), you can do either one.
eg
public class BaseEntity
{
}
public partial class Region : BaseEntity
{
public int RegionID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public int Timezone { get; set; }
public virtual ICollection<RegionCity> RegionCities { get; } = new HashSet<RegionCity>();
}
public partial class RegionCity : BaseEntity
{
[Key(), Column(Order=0)]
public int RegionId { get; set; }
[Key(), Column(Order = 1)]
public int RegionCityId { get; set; }
public string CityName { get; set; }
[ForeignKey("RegionId")]
public virtual Region Region { get; set; }
public virtual ICollection<CityStreet> CityStreets { get; } = new HashSet<CityStreet>();
}
public partial class CityStreet : BaseEntity
{
[Key(), Column(Order = 0)]
public int RegionId { get; set; }
[Key(), Column(Order = 1)]
public int RegionCityId { get; set; }
[Key(), Column(Order = 3)]
public int CityStreetId { get; set; }
public string StreetName { get; set; }
[ForeignKey("RegionId,RegionCityId")]
public virtual RegionCity RegionCity { get; set; }
public virtual ICollection<FullAddress> FullAddresses { get; } = new HashSet<FullAddress>();
}
public partial class FullAddress : BaseEntity
{
[Key(), Column(Order = 0)]
public int RegionId { get; set; }
[Key(), Column(Order = 1)]
public int RegionCityId { get; set; }
[Key(), Column(Order = 3)]
public int CityStreetId { get; set; }
[Key(), Column(Order = 4)]
public int FullAddressId { get; set; }
public string HouseNum { get; set; }
public string OfficeNum { get; set; }
[ForeignKey("RegionId,RegionCityId,CityStreetId")]
public virtual CityStreet Street { get; set; }
[NotMapped]
public virtual RegionCity RegionCity
{
get
{
return this.Street.RegionCity;
}
set
{
this.Street.RegionCity = value;
}
}
[NotMapped]
public virtual Region Region
{
get
{
return this.Street.RegionCity.Region;
}
set
{
this.Street.RegionCity.Region = value;
}
}
}

Related

Entity-framework The column name is specified more than once in the SET clause or column list of an INSERT

I have entity
public class ImageTeam
{
public int Id { get; set; }
public int TeamID { get; set; }
public Team Team { get; set; }
public int PostTeamID { get; set; }
public string Image { get; set; }
public int ImageType { get; set; }
public int StatusPublic { get; set; }
public int StatusActive { get; set; }
public DateTime CreatedAt { get; set; }
}
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public int NoMember { get; set; }
public float Score { get; set; }
public int StatusActive { get; set; }
public int TeamType { get; set; }
public virtual List<TeamGroup> ListMember { get; set; }
public virtual List<ImageTeam> ListAvatar { get; set; }
public virtual List<ImageTeam> ListBanner { get; set; }
public DateTime CreatedAt { get; set; }
}
config data context
modelBuilder.Entity<Team>(entity =>
{
entity.HasMany(x => x.ListAvatar)
.WithOne(t => t.Team)
.HasForeignKey(pv => pv.TeamID);
});
when I post the data insert a new record entity ImageTeam then it show exception
I need to do...Help me
In the Team class you add another relation ListBanner to ImageTeam class ,you have not set an foreign key for it, so EF automatically creates a TeamID and because TeamId already in the class, it's throw exception . You also need to set an foreign key for second relation.
public class ImageTeam
{
public int Id { get; set; }
public int TeamID { get; set; }
public Team Team { get; set; }
public int BannerTeamId { get; set; }
public Team BannerTeam { get; set; }
public int PostTeamID { get; set; }
public string Image { get; set; }
public int ImageType { get; set; }
public int StatusPublic { get; set; }
public int StatusActive { get; set; }
public DateTime CreatedAt { get; set; }
}
entity.HasMany(x => x.ListAvatar)
.WithOne(t => t.Team)
.HasForeignKey(pv => pv.TeamID).OnDelete(DeleteBehavior.Restrict);
entity.HasMany(x => x.ListBanner)
.WithOne(t => t.BannerTeam)
.HasForeignKey(pv => pv.BannerTeamId).OnDelete(DeleteBehavior.Restrict);
I have found a solution:
edit Team entity:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public int NoMember { get; set; }
public float Score { get; set; }
public int StatusActive { get; set; }
public int TeamType { get; set; }
public virtual List<TeamGroup> ListMember { get; set; }
public virtual List<ImageTeam> ListImage { get; set; }
public DateTime CreatedAt { get; set; }
}
*no config data context
create new model: TeamViewModel
public class TeamViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int NoMember { get; set; }
public float Score { get; set; }
public int StatusActive { get; set; }
public int TeamType { get; set; }
public virtual List<TeamGroupViewModel> ListMember { get; set; }
public virtual List<ImageTeam> ListImage { get; set; }
public string AvatarUrl { get; set; }
public virtual List<ImageTeam> ListAvatar { get; set; }
public string BannerUrl { get; set; }
public virtual List<ImageTeam> ListBanner { get; set; }
public virtual List<ImageTeam> ListPost { get; set; }
}
in controller :
[Route("api/[controller]/{id}/view")]
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
var team = _teamService.GetById(id);
var model = _mapper.Map<TeamViewModel>(team);
model = parserImageTeam(model);
return Ok(model);
}
[Route("api/[controller]/{UserId}/view-teams")]
[HttpGet("{UserId}")]
public IActionResult GetAllTeamOfUser(int UserId)
{
// list teams
var teams = _teamService.GetTeamOfUser(UserId);
var _teams = _mapper.Map<IList<TeamViewModel>>(teams);
var newTeams = new List<TeamViewModel>();
foreach (TeamViewModel team in _teams)
{
newTeams.Add(parserImageTeam(team));
}
return Ok(newTeams);
}
private TeamViewModel parserImageTeam(TeamViewModel teamModel)
{
var imageAvatars = new List<ImageTeam>();
var imageBanners = new List<ImageTeam>();
var imagePosts = new List<ImageTeam>();
bool avt = false, banner = false;
foreach (ImageTeam image in teamModel.ListImage)
{
if (image.ImageType == Constants.ImageType.IMAGE_AVATAR_TEAM)
{
image.Image = parserUrlImage(image);
imageAvatars.Add(image);
if (!avt)
{
teamModel.AvatarUrl = image.Image;
avt = true;
}
}
if (image.ImageType == Constants.ImageType.IMAGE_BANNER_TEAM)
{
image.Image = parserUrlImage(image);
imageBanners.Add(image);
if (!banner)
{
teamModel.BannerUrl = image.Image;
banner = true;
}
}
if (image.ImageType == Constants.ImageType.IMAGE_POST_TEAM)
{
image.Image = parserUrlImage(image);
imagePosts.Add(image);
banner = true;
}
}
teamModel.ListAvatar = imageAvatars;
teamModel.ListBanner = imageBanners;
teamModel.ListPost = imagePosts;
return teamModel;
}
private string parserUrlImage(ImageTeam model)
{
string url = Configuration.GetValue<string>("BaseVariables:BaseUrl");
// another controller handle request (ImagesController)
return model.Image = url + "/Images/" + Constants.ImageType.getFolderName(model.ImageType).ToLower() + "/" + model.TeamID + "?ImageType=" + model.ImageType + "&imageName=" + model.Image;
}

many to many entity framework + Compose Primary Key

Hi friends I am having problems with a relationship Much to Much with Compose Primary Key.
I have the following:
public class Empleado
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key, Column(Order = 0)]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Nombre { get; set; }
[Key, Column(Order = 1)]
public int? IdentificacionId { get; set; }
public Identificacion Identificacion { get; set; }
[Required]
[StringLength(11)]
[Key, Column(Order = 2)]
public string NoIdentificacion { get; set; }
}
// Entidad relación
public class EmpleadoNomina
{
public int EmpleadoId { get; set; }
public int NominaId { get; set; }
public decimal Salario { get; set; }
public int DescuentoLey { get; set; }
public decimal? SalarioIngresoEgreso { get; set; }
public Nomina Nomina { get; set; }
public Empleado Empleado { get; set; }
}
// FluentApi
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Constraint combinado TipoId + NoID
modelBuilder.Entity<Empleado>().HasKey(x => new { x.IdentificacionId, x.NoIdentificacion });
// Relación
modelBuilder.Entity<EmpleadoNomina>().HasKey(k => new { k.NominaId, k.EmpleadoId });
}
The problem arises when the relationship table is created. To this is added the columns Employee_IdentificationId, Employee_NoIdentification. And the EmployeeId column without foreignkey.
The other problem is: I can't use .Find(id); example: db.Empleados.Find(15); This gives an error because it requires me to pass the three keys.
I just want to remove the extra columns Employee_IdentificationId, Employee_NoIdentification and only use EmpleadoId.
Don't use a composite key on Empleado - just use ID as its key. Same for Nomina. The composite key is used on the bridge table. Also, since you are already using fluent code you don't need the annotations. Behavior can be odd when you mix.
public class Empleado
{
// This will be identity key by convention
public int Id { get; set; }
// These could be set in fluent code
[Required]
[StringLength(100)]
public string Nombre { get; set; }
public string NoIdentificacion { get; set; }
// This will be an optional FK by convention
public int? IdentificacionId { get; set; }
public Identificacion Identificacion { get; set; }
public virtual ICollection<Nomina> Nominas { get; set; }
}
public class Nomina
{
// This will be identity key by convention
public int Id { get; set; }
public string XXXXXX { get; set; }
... etc
public virtual ICollection<Empleado> Empleados { get; set; }
}
public class EmpleadoNomina
{
public int EmpleadoId { get; set; }
public int NominaId { get; set; }
public decimal Salario { get; set; }
public int DescuentoLey { get; set; }
public decimal? SalarioIngresoEgreso { get; set; }
public Nomina Nomina { get; set; }
public Empleado Empleado { get; set; }
}
// FluentApi
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Empleado>()
.HasMany<Nomina>(e => e.Nominas)
.WithMany(c => c.Empleado)
.Map(cs =>
{
cs.MapLeftKey("Id");
cs.MapRightKey("Id");
cs.ToTable("EmpleadoNomina");
});
}
See here
EDIT: OK, If you need to keep the composite key on Empleado, then you will need to reference it with a composite FK. So you need to add the other 2 FK fields:
// Entidad relación
public class EmpleadoNomina
{
public int EmpleadoId { get; set; }
public int IdentificacionId { get; set; }
public string NoIdentificacion { get; set; }
public int NominaId { get; set; }
public decimal Salario { get; set; }
public int DescuentoLey { get; set; }
public decimal? SalarioIngresoEgreso { get; set; }
public Nomina Nomina { get; set; }
public Empleado Empleado { get; set; }
}
Then the fluent code:
modelBuilder.Entity<EmpleadoNomina>()
.HasRequired(en => en.Empleado)
.WithMany()
.HasForeignKey(en => new {en.EmpleadoId, en.IdentificacionId , en.NoIdentificacion });
Also, I am not sure IdentificacionId can be nullable. See here.
I solved it with Index Dataanotations to create the Unique Composited Index instead of a Composited primary key (this was responsible of my problem).
I removed the composite keys from the main class and added a list of EmployeeNomine to the two classes of entities.
I changed everything as shown below and now it is working very well. This what I wanted to do from the beginning.
// Class 2
public class Empleado
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Nombre { get; set; }
[Index("IX_Identificacion", 1, IsUnique = true)]
public int? IdentificacionId { get; set; }
public Identificacion Identificacion { get; set; }
[Required]
[StringLength(11)]
[Index("IX_Identificacion", 2, IsUnique = true)]
public string NoIdentificacion { get; set; }
public List<EmpleadoNomina> EmpleadoNominas { get; set; }
}
// Class 1
public class Nomina
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(200)]
public string Descripcion { get; set; }
public int Frecuencia { get; set; }
public int Dia { get; set; }
public List<EmpleadoNomina> EmpleadoNominas { get; set; }
}
// Relation Entity (Table)
public class EmpleadoNomina
{
public int EmpleadoId { get; set; }
public int NominaId { get; set; }
public decimal Salario { get; set; }
public int DescuentoLey { get; set; }
public decimal? SalarioIngresoEgreso { get; set; }
public Nomina Nomina { get; set; }
public Empleado Empleado { get; set; }
}
// FluentApi
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Nominas -> Empleados
modelBuilder.Entity<EmpleadoNomina>().HasKey(k => new { k.NominaId, k.EmpleadoId });
modelBuilder.Entity<EmpleadoNomina>().HasRequired(e => e.Empleado).WithMany(n => n.EmpleadoNominas).HasForeignKey(r => r.EmpleadoId);
modelBuilder.Entity<EmpleadoNomina>().HasRequired(n => n.Nomina).WithMany(n => n.EmpleadoNominas).HasForeignKey(n => n.NominaId);
}
It's I always wanted to do. thanks for everything

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; }
}

TPC doesnt work in CodeFirst-Migration

I have below class :
public abstract class BaseAttachment
{
public Guid BaseAttachmentId { get; set; }
public string FileName { get; set; }
public string FileExtension { get; set; }
public string FileSize { get; set; }
public Folder Folder { get; set; }
public Guid? FolderId { get; set; }
public byte[] RowVersion { get; set; }
}
And I have below Table and Inherite BaseAttachment :
public class ProductGallery : BaseAttachment
{
public ProductHeader ProductHeader { get; set; }
public Guid? ProductHeaderId { get; set; }
}
and this :
public class Attachment:BaseAttachment
{
}
when Run Project and Create Database I have Below Tables :
BaseAttachment,ProductCategory
instead Of One Table (BaseAttachment) .
why ?
I was a config Class Like this for ProductCategoryConfig and Set TableName with below :
public class ProductGalleryConfig:EntityTypeConfiguration<ProductGallery>
{
public ProductGalleryConfig()
{
ToTable("ProductGallery");
Property(row => row.Code).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(row => row.RowVersion).IsRowVersion();
HasRequired(row => row.ProductHeader).WithMany(row => row.Galleries).HasForeignKey(row => row.ProductHeaderId).WillCascadeOnDelete(false);
}
}
and Comment below Line and its fix.
// ToTable("ProductGallery");

EF Adding an additional FK?

I have the following 2 entities:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Fixture> Fixtures { get; set; }
}
public class Fixture
{
public int Id { get; set; }
public Result Result { get; set; }
public int HomeTeamId { get; set; }
public int AwayTeamId { get; set; }
public virtual Team HomeTeam { get; set; }
public virtual Team AwayTeam { get; set; }
}
I have then mapped it like so:
public class FixtureMap : EntityTypeConfiguration<Fixture>
{
public FixtureMap()
{
HasRequired(x => x.AwayTeam).WithMany().HasForeignKey(x => x.AwayTeamId);
HasRequired(x => x.HomeTeam).WithMany().HasForeignKey(x => x.HomeTeamId);
}
}
But when I add a migration, EF is creating an additional FK and column to my Fixture table and I've no idea why? How can I tell it not too?
As you can see its added a column called Team_Id and created an FK from it even tho I have specified the relationship in the mapping?
use this code:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
[InverseProperty("HomeTeam")]
public virtual ICollection<Fixture> HomeFixtures { get; set; }
[InverseProperty("AwayTeam")]
public virtual ICollection<Fixture> AwayFixtures { get; set; }
}
public class Fixture
{
public int Id { get; set; }
public Result Result { get; set; }
public int HomeTeamId { get; set; }
public int AwayTeamId { get; set; }
[InverseProperty("HomeFixtures")]
[ForeignKey("HomeTeamId ")]
public virtual Team HomeTeam { get; set; }
[InverseProperty("AwayFixtures")]
[ForeignKey("AwayTeamId")]
public virtual Team AwayTeam { get; set; }
}
And :
public class FixtureMap : EntityTypeConfiguration<Fixture>
{
public FixtureMap()
{
HasRequired(x => x.AwayTeam).WithMany().HasForeignKey(x => x.AwayTeamId).willCascadeOnDelete(false);
HasRequired(x => x.HomeTeam).WithMany().HasForeignKey(x => x.HomeTeamId);
}
}