Property Foreign Key can not be set to NULL - c#

I have 4 tables
public class StokGroup:BaseModel
{
public string GroupName { get; set; }
public List<GroupFeature> GroupFeatures { get; set; }
public List<StokCard> StokCards { get; set; }
}
public class GroupFeature : BaseModel
{
public int StokGroupId { get; set; }
public StokGroup StokGroup { get; set; }
public string FeatureName { get; set; }
public List<StokCardAdditionalFeatureValue> StokCardAdditionalFeatureValues { get; set; }
}
public class StokCard : BaseModel
{
public string IPMCode { get; set; }
public int StokGroupId { get; set; }
public StokGroup StokGroup { get; set; }
.......................................
}
public class StokCardAdditionalFeatureValue:BaseModel
{
public int StokCardId { get; set; }
public StokCard StokCard { get; set; }
public int GroupFeatureId { get; set; }
public GroupFeature GroupFeature { get; set; }
public string Value { get; set; }
}
And my BaseModel is the class that has Primary Key ID all classes inherit from BaseModel
public class BaseModel
{
[Key]
public int Id { get; set; }
}
And I have also :
modelBuilder.Entity<StokCard>()
.HasOne(o => o.StokGroup)
.WithMany(m => m.StokCards)
.OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<GroupFeature>()
.HasOne(o => o.StokGroup)
.WithMany(m => m.GroupFeatures);
modelBuilder.Entity<StokCardAdditionalFeatureValue>()
.HasOne(o => o.StokCard)
.WithMany(m => m.StokCardAdditionalFeatureValues)
.OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<StokCardAdditionalFeatureValue>()
.HasOne(o => o.GroupFeature)
.WithMany(m => m.StokCardAdditionalFeatureValues);
Now the problem is when the stokgroup is deleted StokCard is deleted automatically because of the relationship
How can I achieve this problem ?
I set NULL to StokGroupId on StokCards table but migration FAILED
Would you mind please help ?
Thanks in advance

Related

EF Core related data is null with AutoMapper

In my app one case can have many companies.
My models:
public class Case
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public IList<CaseCompany> CaseCompanies { get; set; }
}
public class CaseInput
{
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public IList<CaseCompanyInput> CaseCompanyInputs { get; set; }
}
public class Company
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
}
public class CompanyInput
{
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
}
public class CaseCompany
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid CaseId { get; set; }
public Guid CompanyId { get; set; }
public class Case Case { get; set; }
public class Company Company { get; set; }
}
public class CaseCompanyInput
{
public Guid Id { get; set; }
public Guid CaseId { get; set; }
public Guid CompanyId { get; set; }
public class CaseInput CaseInput { get; set; }
public class CompanyInput CompanyInput { get; set; }
}
AutoMapperProfiles.cs:
// In Startup.cs: services.AddAutoMapper(typeof(AutoMapperProfiles));
public class AutoMapperProfiles : Profile
{
public AutoMapperProfiles()
{
CreateMap<Case, CaseInput>().ReverseMap();
CreateMap<CaseCompany, CaseCompanyInput>().ReverseMap();
CreateMap<Company, CompanyInput>().ReverseMap();
}
}
EditCase.cs:
private readonly DBContext _dbContext;
private readonly IMapper _mapper;
[BindProperty]
public CaseInput CaseInput { get; set; }
public EditCase(DBContext dbContext, IMapper mapper)
{
_dbContext = dbContext;
_mapper = mapper;
}
public async Task<IActionResult> OnGetAsync(Guid caseId)
{
var getCase = await _dbContext.Cases.Include(x => x.CaseCompanies).ThenInclude(x => x.Company).FirstOrDefaultAsync(x => x.Id == caseId);
CaseInput = _mapper.Map<CaseInput>(getCase);
Console.WriteLine(getCase.CaseCompanies[0].Company.Name) // gets the company name
Console.WriteLine(CaseInput.CaseCompanyInputs[0].CompanyInput.Name) // CaseInput.Name is not null but CaseCompanyInputs is null
return Page();
}
I've also tried:
CaseInput = await _dbContext.Cases.ProjectTo<CaseInput>(_mapper.ConfigurationProvider).FirstOrDefaultAsync(x => x.Id == caseId);
and
CaseInput = await _dbContext.Cases.Include(x => x.CaseCompanies).ThenInclude(x => x.Company).ProjectTo<CaseInput>(_mapper.ConfigurationProvider).FirstOrDefaultAsync(x => x.Id == caseId);
with the same result: CaseCompanyInputs is null.
My best guess is that it's an error in the relations of the input models, but I just can't see it. I believe I've followed the naming conventions to make the relations right.
Any help would be appreciated.
For automapping to work, you have to rename ur destination property name the same as the source property name
public class CaseInput
{
public Guid Id { get; set; }
public string Name { get; set; }
//public IList<CaseCompanyInput> CaseCompanyInputs { get; set; }
public IList<CaseCompanyInput> CaseCompanies { get; set; }
}
public class CaseCompanyInput
{
public Guid Id { get; set; }
public Guid CaseId { get; set; }
public Guid CompanyId { get; set; }
//public CaseInput CaseInput { get; set; }
public CaseInput Case { get; set; }
//public CompanyInput CompanyInput { get; set; }
public CompanyInput Company { get; set; }
}
If you don't want to change the property names, change ur configuration to the following.
CreateMap<Case, CaseInput>()
.ForMember(dest => dest.CaseCompanyInputs, opt => opt.MapFrom(src => src.CaseCompanies));
CreateMap<CaseCompany, CaseCompanyInput>()
.ForMember(dest => dest.CaseInput, opt => opt.MapFrom(src => src.Case))
.ForMember(dest => dest.CompanyInput, opt => opt.MapFrom(src => src.Company));

EF Core multiple navigation properties to same table with fluent api

I searched a lot but no luck to find a solution to my entities.
I have 2 entities. Device & PersonEnterExit
I have 2 Collection of PersonEnterExit in Device entity.
When I run database update it says me you have same entity with different name. do manually name them. how do i do that ? I want to do this with fluent api in onModelCreating.
Devices.cs
using System;
using System.Collections.Generic;
namespace DesTech.Core.Entities
{
public class Device : BaseEntity
{
public bool? IsConnected { get; set; }
public string Name { get; set; }
public string Ip { get; set; }
public int Port { get; set; }
public DateTime? LastConnectTime { get; set; }
public DateTime? ChangeClockBatteryWarningTime { get; set; }
public bool HasFp { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<DeviceOptLog> DeviceOptLog { get; set; }
public virtual ICollection<DevicePerson> DevicePerson { get; set; }
public virtual ICollection<DeviceTimeZone> DeviceTimeZone { get; set; }
public virtual ICollection<PersonEnterExit> PersonEnterExitEnDevice { get; set; }
public virtual ICollection<PersonEnterExit> PersonEnterExitExDevice { get; set; }
public virtual ICollection<PersonPassLog> PersonPassLog { get; set; }
}
}
PersonEnterExit.cs
using System;
namespace DesTech.Core.Entities
{
public class PersonEnterExit : BaseEntity
{
public int Type { get; set; }
public int? PersonId { get; set; }
public DateTime? NoEnterTime { get; set; }
public int? EnDeviceId { get; set; }
public DateTime? EnVerifyDate { get; set; }
public string EnPlate { get; set; }
public int? ExDeviceId { get; set; }
public DateTime? ExVerifyDate { get; set; }
public virtual Device EnDevice { get; set; }
public virtual Device ExDevice { get; set; }
public virtual Person Person { get; set; }
}
}
You can use one of these
FluentApi
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Device>()
.HasMany(a => a.PersonEnterExitEnDevice)
.WithOne(a => a.EnDevice)
.HasForeignKey(a => a.EnDeviceId).OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Device>()
.HasMany(a => a.PersonEnterExitExDevice)
.WithOne(a => a.ExDevice)
.HasForeignKey(a => a.ExDeviceId).OnDelete(DeleteBehavior.Restrict);
}
Metadata. you can use InverseProperty to define the relationships.
namespace DesTech.Core.Entities
{
public class PersonEnterExit : BaseEntity
{
public int Type { get; set; }
public int? PersonId { get; set; }
public DateTime? NoEnterTime { get; set; }
public int? EnDeviceId { get; set; }
public DateTime? EnVerifyDate { get; set; }
public string EnPlate { get; set; }
public int? ExDeviceId { get; set; }
public DateTime? ExVerifyDate { get; set; }
[InverseProperty("PersonEnterExitEnDevice")]
public virtual Device EnDevice { get; set; }
[InverseProperty("PersonEnterExitExDevice")]
public virtual Device ExDevice { get; set; }
public virtual Person Person { get; set; }
}
}
namespace DesTech.Core.Entities
{
public class Device : BaseEntity
{
public bool? IsConnected { get; set; }
public string Name { get; set; }
public string Ip { get; set; }
public int Port { get; set; }
public DateTime? LastConnectTime { get; set; }
public DateTime? ChangeClockBatteryWarningTime { get; set; }
public bool HasFp { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<DeviceOptLog> DeviceOptLog { get; set; }
public virtual ICollection<DevicePerson> DevicePerson { get; set; }
public virtual ICollection<DeviceTimeZone> DeviceTimeZone { get; set; }
[InverseProperty("EnDevice")]
public virtual ICollection<PersonEnterExit> PersonEnterExitEnDevice { get; set; }
[InverseProperty("ExDevice")]
public virtual ICollection<PersonEnterExit> PersonEnterExitExDevice { get; set; }
public virtual ICollection<PersonPassLog> PersonPassLog { get; set; }
}
}
Like you mentioned, you need to explicitly configure both relationships. Otherwise, EF will get confused.
build.Entity<PersonEnterExit>()
.HasOne(e => e.EnDevice)
.WithMany(d => d.PersonEnterExitEnDevice);
build.Entity<PersonEnterExit>()
.HasOne(e => e.ExDevice)
.WithMany(d => d.PersonEnterExitExDevice);
See if this helps. You may need to explicitly configure foreign keys if this doesn't work.
Okey I combined two answers and its works now. I reply my question for maybe others use same approach. If i mistake please warn me. But it works now.
builder.HasMany(a => a.PersonEnterExitEnDevice)
.WithOne(a => a.EnDevice)
.HasForeignKey(a => a.EnDeviceId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasMany(a => a.PersonEnterExitExDevice)
.WithOne(a => a.ExDevice)
.HasForeignKey(a => a.ExDeviceId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(d => d.EnDevice)
.WithMany(e => e.PersonEnterExitEnDevice)
.HasForeignKey(d => d.EnDeviceId)
.HasConstraintName("FK_PersonEnterExit_DeviceEn");
builder.HasOne(d => d.EnLocation)
.WithMany(e => e.PersonEnterExitEnLocation)
.HasForeignKey(d => d.EnLocationId)
.HasConstraintName("FK_PersonEnterExit_LocationEx");

One to Many relationship always bring me empty

I try to use Entity Framework with code first and fluent api to implement a one to many relationship
I have two classes
namespace Mantenimiento.Business.Entities
{
public class Personal : Entity
{
[Key]
public int Id { get; set; }
public int? Dni { get; set; }
public string Nombre { get; set; }
public string Apellido { get; set; }
public string Cuil { get; set; }
public string Legajo { get; set; }
[ForeignKey("Dni")]
public ICollection<ContactoEmergencia> Contacto { get; set; }
}
namespace Mantenimiento.Business.Entities
{
public class ContactoEmergencia : Entity
{
[Key]
public int Id { get; set; }
public int? Dni { get; set; }
public string ApellidoNombre { get; set; }
public string Vinculo { get; set; }
public string Domicilio { get; set; }
public string telefono { get; set; }
public string Comentario { get; set; }
public int CreateUserId { get; set; }
[ForeignKey("Dni")]
public virtual Personal Personal { get; set; }
}
}
This is my dbContext
#region personals
modelBuilder.Entity<Personal>().ToTable("InfoPersonal").HasKey(t => t.Id);
modelBuilder.Entity<Personal>().Property(c => c.Id).UseSqlServerIdentityColumn().IsRequired();
modelBuilder.Entity<Personal>().Property(c => c.CreatedDate).HasDefaultValue(DateTime.Now);
modelBuilder.Entity<Personal>().Property(c => c.LastModifiedDate).HasDefaultValue(DateTime.Now);
modelBuilder.Entity<Personal>().Property(c => c.Deleted).HasDefaultValue(false);
modelBuilder.Entity<Personal>().HasMany<ContactoEmergencia>(c => c.Contacto).WithOne(p => p.Personal).HasForeignKey(s => s.Dni);
#endregion
#region contactoEmergencias
modelBuilder.Entity<ContactoEmergencia>().ToTable("InfoEmergencia").HasKey(d => d.Dni);
modelBuilder.Entity<ContactoEmergencia>().Property(c => c.CreatedDate).HasDefaultValue(DateTime.Now);
modelBuilder.Entity<ContactoEmergencia>().Property(c => c.LastModifiedDate).HasDefaultValue(DateTime.Now);
modelBuilder.Entity<ContactoEmergencia>().Property(c => c.Deleted).HasDefaultValue(false);
#endregion
And my query is
return await _context.personals
.Include(c => c.Contacto)
.Where(p => p.Deleted == false)
.OrderBy(s => s.Apellido)
.ToListAsync(
);
But the properties is always empty.
i need to relate Personal.Di with Contacto.Dni, i had to change the key?
You should remove ForeignKey attribute from Personal entity. In one to many relationship only child entity could accept ForeignKey.

Violation of PRIMARY KEY constraint, even though im not adding or modyfying anything in the context

My program is parsing an xml file and adding orders from this file to the mssql database. Before he adds those orders to database he analyses them if there are any duplicates that need to be dealt with.
foreach (var newOrderXml in newOrdersList)
{
var dupesInDb = _context.OrdersXml.Include(o=>o.OrderXmlItems)
.Where(o => o.OrX_ORDERNR.Contains(newOrderXml.OrX_ORDERNR))
.ToList();
_context.SaveChanges();
}
Program loops through all of the new orders in newOrderList and gets the list of duplicates with linq query. If there are 0 duplicates and nothing gets returned, everything works fine, but if a single duplicate is returned then SaveChanges method will throw an exception "Violation of PRIMARY KEY constraint PK_dbo.SewingCardBundles, Cannot insert duplicate key in object 'dbo.SewingCardBundles'. The duplicate key value is (1).", even though im not adding or modyfying anything in the context. I dont really know what is happening, all im doing is getting, im not changing anything, im not creating new objects. This exception happens exactly at this spot, if i try to save changes before this linq query then nothing bad happens but if i try it after this linq query i get the exceptions. So where does those changes to context come from?
Here are my models:
public class OrderXml
{
public OrderXml()
{
OrderXmlItems = new List<OrderXmlItem>();
}
public int OrX_Id { get; set; }
public string OrX_ORDERNR { get; set; }
public string OrX_REFERGB { get; set; }
public int? OrX_CUSTOMERNUM { get; set; }
public string OrX_DNAME { get; set; }
public string OrX_DADR { get; set; }
public string OrX_DPCODE { get; set; }
public string OrX_POSTALCODE { get; set; }
public string OrX_COUNTRY { get; set; }
public string OrX_PHONE { get; set; }
public string OrX_EMAIL { get; set; }
public int? OrX_LANG { get; set; }
public int? OrX_CUSTGRP { get; set; }
public int? OrX_QUALITCON { get; set; }
public string OrX_SHIPVIA { get; set; }
public string OrX_DATE1 { get; set; }
public string OrX_DATE2 { get; set; }
public string OrX_DELIVGB { get; set; }
public string OrX_SORT { get; set; }
public int? OrX_CURLAB { get; set; }
public List<OrderXmlItem> OrderXmlItems { get; set; }
public Adress Adress { get; set; }
}
public OrderXmlItem()
{
SewingCardBundle = new SewingCardBundle();
}
public int OxI_Id { get; set; }
public int? OxI_PRODUCT { get; set; }
public int? OxI_ORDERLINE { get; set; }
public int? OxI_QUANTITY { get; set; }
public int? OxI_TYPE { get; set; }
public string OxI_TPFABNR { get; set; }
public string OxI_TPFABDEF { get; set; }
public string OxI_TPFABNAME { get; set; }
public int? OxI_CURDIR { get; set; }
public int? OxI_CURWIDTH { get; set; }
public int? OxI_CURHEIGHT { get; set; }
public int? OxI_WORKMETH { get; set; }
public int? OxI_FOLDTYPE { get; set; }
public decimal? OxI_FOLDFACT { get; set; }
public int? OxI_CURBAND { get; set; }
public int? OxI_CURHEAD { get; set; }
public int? OxI_CURBOTSEAM { get; set; }
public int? OxI_PACKWLEFT { get; set; }
public int? OxI_PACKWRIGHT { get; set; }
public decimal? OxI_NRSTROL { get; set; }
public decimal? OxI_NRSTROR { get; set; }
public int? OxI_LINTYP { get; set; }
public string OxI_LINCOL { get; set; }
public int? OxI_EMBSORT { get; set; }
public int? OxI_EXTRA { get; set; }
public int? OxI_PRODUCE { get; set; }
public int? OxI_PACKSORT { get; set; }
public int? OxI_CURMODEL { get; set; }
public string OxI_BARCODE { get; set; }
public string OxI_EXTRAINF { get; set; }
public int? OxI_RAILTYP { get; set; }
public int? OxI_RAILCONT { get; set; }
public int? OxI_RAILCONTSIDE { get; set; }
public decimal? OxI_FABSTROTOT { get; set; }
public decimal? OxI_FABSTROLEFT { get; set; }
public decimal? OxI_FABSTRORIGHT { get; set; }
public int? OxI_FABUNDSIZ { get; set; }
public int? OxI_FABTOTSIZ { get; set; }
public int? OxI_LINSTROTOT { get; set; }
public int? OxI_LINUNDSIZ { get; set; }
public int? OxI_LINTOTSIZ { get; set; }
public decimal? OxI_FABWIDTH { get; set; }
public int? OxI_CHILDSFT { get; set; }
public int? OxI_FOLDSORT { get; set; }
public int? OxI_EMBLENGTH { get; set; }
public int? OxI_PACKMETH { get; set; }
public int OrderXmlId { get; set; }
public OrderXml OrderXml { get; set; }
public SewingCardBundle SewingCardBundle { get; set; }
}
public class SewingCardBundle
{
public SewingCardBundle()
{
FlamanSewingCards = new List<FlamandzkaSewingCard>();
FlamandzkaBrytaSewingCards = new List<FlamandzkaBrytaSewingCard>();
OczkaSewingCards = new List<OczkaSewingCard>();
OczkaBrytaSewingCards = new List<OczkaBrytaSewingCard>();
WellenbandSewingCards = new List<WellenbandSewingCard>();
WellenbandBrytaSewingCards = new List<WellenbandBrytaSewingCard>();
PodwiazkaSewingCards = new List<PodwiazkaSewingCard>();
TunelSewingCards = new List<TunelSewingCard>();
}
public int SwC_Id { get; set; }
public OrderXmlItem OrderXmlItem { get; set; }
public List<FlamandzkaSewingCard> FlamanSewingCards { get; set; }
public List<FlamandzkaBrytaSewingCard> FlamandzkaBrytaSewingCards { get; set; }
public List<OczkaSewingCard> OczkaSewingCards { get; set; }
public List<OczkaBrytaSewingCard> OczkaBrytaSewingCards { get; set; }
public List<WellenbandSewingCard> WellenbandSewingCards { get; set; }
public List<WellenbandBrytaSewingCard> WellenbandBrytaSewingCards { get; set; }
public List<PodwiazkaSewingCard> PodwiazkaSewingCards { get; set; }
public List<TunelSewingCard> TunelSewingCards { get; set; }
}
and my Fluent API configurations for those models:
public class OrderXmlConfiguration : EntityTypeConfiguration<OrderXml>
{
public OrderXmlConfiguration()
{
HasKey(o => o.OrX_Id);
Property(o => o.OrX_ORDERNR).IsRequired();
Property(o => o.OrX_REFERGB).IsRequired();
Property(o => o.OrX_CUSTOMERNUM).IsRequired();
Property(o => o.OrX_DNAME).IsRequired();
Property(o => o.OrX_DPCODE).IsRequired();
Property(o => o.OrX_POSTALCODE).IsRequired();
Property(o => o.OrX_COUNTRY).IsRequired();
Property(o => o.OrX_LANG).IsRequired();
Property(o => o.OrX_CUSTGRP).IsRequired();
Property(o => o.OrX_SHIPVIA).IsRequired();
Property(o => o.OrX_CURLAB).IsRequired();
HasMany(i => i.OrderXmlItems)
.WithRequired(o => o.OrderXml)
.HasForeignKey(o => o.OrderXmlId)
.WillCascadeOnDelete(true);
}
}
public class OrderXmlItemConfiguration : EntityTypeConfiguration<OrderXmlItem>
{
public OrderXmlItemConfiguration()
{
HasKey(o => o.OxI_Id);
Property(p => p.OxI_Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(p => p.OxI_PRODUCT).IsRequired();
Property(p => p.OxI_ORDERLINE).IsRequired();
Property(p => p.OxI_QUANTITY).IsRequired();
Property(p => p.OxI_TYPE).IsRequired();
Property(p => p.OxI_CURDIR).IsRequired();
Property(p => p.OxI_CURWIDTH).IsRequired();
Property(p => p.OxI_CURHEIGHT).IsRequired();
Property(p => p.OxI_WORKMETH).IsRequired();
Property(p => p.OxI_FOLDTYPE).IsRequired();
Property(p => p.OxI_FOLDFACT).IsRequired();
Property(p => p.OxI_PACKWLEFT).IsRequired();
Property(p => p.OxI_PACKWRIGHT).IsRequired();
Property(p => p.OxI_BARCODE).IsRequired();
HasRequired(i => i.SewingCardBundle)
.WithRequiredPrincipal( s=> s.OrderXmlItem)
.WillCascadeOnDelete(true);
}
}
public class SewingCardBundleConfiguration : EntityTypeConfiguration<SewingCardBundle>
{
public SewingCardBundleConfiguration()
{
HasKey(s => s.SwC_Id);
HasMany(s=>s.FlamanSewingCards)
.WithRequired(c=>c.SewingCardBundle)
.WillCascadeOnDelete(true);
HasMany(s => s.FlamandzkaBrytaSewingCards)
.WithRequired(c => c.SewingCardBundle)
.WillCascadeOnDelete(true);
HasMany(s => s.OczkaBrytaSewingCards)
.WithRequired(c => c.SewingCardBundle)
.WillCascadeOnDelete(true);
HasMany(s => s.OczkaSewingCards)
.WithRequired(c => c.SewingCardBundle)
.WillCascadeOnDelete(true);
HasMany(s => s.WellenbandSewingCards)
.WithRequired(c => c.SewingCardBundle)
.WillCascadeOnDelete(true);
HasMany(s => s.WellenbandBrytaSewingCards)
.WithRequired(c => c.SewingCardBundle)
.WillCascadeOnDelete(true);
HasMany(s => s.TunelSewingCards)
.WithRequired(c => c.SewingCardBundle)
.WillCascadeOnDelete(true);
HasMany(s => s.PodwiazkaSewingCards)
.WithRequired(c => c.SewingCardBundle)
.WillCascadeOnDelete(true);
}
}
I'm not sure why you are calling SaveChanges in the first place (it is not needed), but once you get your data from database, context will track them (i.e. have them cached).
Since, you haven't specified AsNoTracking in your query, SaveChanges method will try to save entities which are being tracked which will lead to your "primary key violation" exception.
To circumvent the issue, you can just specify AsNoTracking:
var dupesInDb = _context
.OrdersXml.Include(o=>o.OrderXmlItems)
.Where(o => o.OrX_ORDERNR.Contains(newOrderXml.OrX_ORDERNR))
.AsNoTracking()
.ToList();

EF Code first Generic Entity & EntityTypeConfiguration

I'm about to create a Generic Entity and EntityTypeConfiguration for my entities. here are my classes:
IEntity
public interface IEntity<T>
{
T Id { get; set; }
}
public interface IAuditableEntity<T>
{
DateTime CreatedAt { get; set; }
Membership.User CreatedBy { get; set; }
int? CreatedById { get; set; }
DateTime? DeletedAt { get; set; }
Membership.User DeletedBy { get; set; }
int? DeletedById { get; set; }
T RevisionParentId { get; set; }
bool isLastVersion { get; set; }
}
Entity.cs
public abstract class BaseEntity
{
}
public abstract class Entity<T> : BaseEntity, IEntity<T>
{
public virtual T Id { get; set; }
}
public abstract class AuditableEntity<T> : Entity<T>, IAuditableEntity<T>
{
public DateTime CreatedAt { get; set; }
public virtual Membership.User CreatedBy { get; set; }
public int? CreatedById { get; set; }
public DateTime? DeletedAt { get; set; }
public virtual Membership.User DeletedBy { get; set; }
public int? DeletedById { get; set; }
public T RevisionParentId { get; set; }
public bool isLastVersion { get; set; }
}
The problem is when I try to define a generic EntityTypeConfiguration of AuditableEntity, because :
public class AuditableEntityConfig<T> : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<T> where T : AuditableEntity<int>
{
public AuditableEntityConfig()
{
HasOptional(x => x.CreatedBy).WithMany().HasForeignKey(x => x.CreatedById);
HasOptional(x => x.DeletedBy).WithMany().HasForeignKey(x => x.DeletedById);
Property(x => x.DeletedAt).IsOptional();
Property(x => x.RevisionParentId).IsOptional();
}
}
public class User : AuditableEntity<long>
{
}
You see I had to AuditableEntity<int> which is wrong and I have no idea what to replace to get it work.
AuditableEntity<int> should be something like AuditableEntity<T> and T can be string, int, guid, long, ...
UPDATE
as suggested by Mike answer, I made changes and updated my question:
public class User : AuditableEntity<int>
{
[Index("IX_uniqueUsername", IsUnique = true)]
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public bool Active { get; set; }
public virtual List<Role> Roles { get; set; }
public virtual List<UserGroup> Groups { get; set; }
public virtual UserProfile Profile { get; set; }
public bool isSuperAdmin { get; set; }
}
public class UserConfig : AuditableEntityConfig<User, int>
{
public UserConfig()
{
ToTable("Account_Users");
Property(x => x.Username).HasMaxLength(200).IsRequired();
Property(x => x.Password).HasMaxLength(200).IsRequired();
Property(x => x.Email).HasMaxLength(200);
HasMany(x => x.Roles).WithMany(x => x.Users).Map(x =>
{
x.ToTable("Account_UserRoles");
x.MapLeftKey("UserId");
x.MapRightKey("RoleId");
});
HasMany(x => x.Groups).WithMany(x => x.Users).Map(x =>
{
x.ToTable("Account_UserGroups");
x.MapLeftKey("UserId");
x.MapRightKey("GroupId");
});
}
}
I get this error now for the RevisionParentId property:
The type 'TK' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration<TStructuralType>.Property<T>(System.Linq.Expressions.Expression<System.Func<TStructuralType,T>>)
in this line:
Property(x => x.RevisionParentId).IsOptional();
where T : AuditableEntity<T> will cause recursive type checking. Please try
UPDATED
public class AuditableEntityConfig<T, TK> : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<T>
where T : AuditableEntity<TK> where TK : struct { }

Categories