ArgumentNullException: Value cannot be null. Parameter name: key on Include Statement - c#

Worked a lot with EF 6.x (via designer) and now started on a new project using EF Core.
I'm getting an error that says value cannot be null, not sure exactly what I'm doing wrong. I've got rid of a lot of fields for brevity as there are hundreds.
All these tables are views via synonyms that connect to a different database. I can get it to work fine, if I do each individual call to a database, but as soon as I do include. I get an error on that line. The error I'm getting is
ArgumentNullException: Value cannot be null.
Parameter name: key
System.Collections.Generic.Dictionary.FindEntry(TKey key)
OnGetAsync
var equipment = _context.EMEMs.Include(x => x.EMEDs).Where(x => x.KeyID.ToString() == key);
EMEM = await equipment.Include(x => x.EMCM).ThenInclude(x=>x.EMCDs).FirstOrDefaultAsync();
EMEM
public class EMEM
{
public byte? EMCo { get; set; }
[Display(Name = "Equipment Code")]
public string Equipment { get; set; }
public string Type { get; set; }
public string Category { get; set; }
public Guid? UniqueAttchID { get; set; }
[Key]
public long KeyID { get; set; }
[NotMapped] public string EquipmentDetails => $"{Equipment.Trim()} - {Description} - {VINNumber}";
public virtual IEnumerable<EMWH> EMWHs { get; set; }
public virtual EMCM EMCM { get; set; }
public virtual IEnumerable<udEMED> EMEDs { get; set; }
}
EMCM
public class EMCM
{
[Key]
public long KeyID { get; set; }
public byte? EMCo { get; set; }
public string Category { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public virtual IEnumerable<EMEM> EMEMs { get; set; }
public virtual IEnumerable<udEMCD> EMCDs { get; set; }
}
udEMCD
public class udEMCD
{
[Key]
public long KeyID { get; set; }
public byte? Co { get; set; }
public string Category { get; set; }
public string DocumentCategory { get; set; }
public int Seq { get; set; }
public Guid? UniqueAttchID { get; set; }
public virtual udEMDC EMDC { get; set; }
public virtual EMCM EMCM { get; set; }
public virtual IEnumerable<HQAT> HQATs { get; set; }
}
Context
modelBuilder.Entity<EMEM>().ToTable("EMEM").HasOne(x => x.EMCM).WithMany(x => x.EMEMs).HasForeignKey(x => new { x.EMCo, x.Category }).HasPrincipalKey(x => new { x.EMCo, x.Category });
modelBuilder.Entity<EMEM>().ToTable("EMEM").HasMany(x => x.EMEDs).WithOne(x => x.EMEM).HasForeignKey(x => new { x.Co, x.Equipment }).HasPrincipalKey(x => new { x.EMCo, x.Equipment });
modelBuilder.Entity<EMCM>().ToTable("EMCM").HasMany(x => x.EMCDs).WithOne(x => x.EMCM)
.HasForeignKey(x => new { x.Co, x.Category }).HasPrincipalKey(x => new { x.EMCo, x.Category });
modelBuilder.Entity<udEMCD>().ToTable("udEMCD").HasOne(x => x.EMDC).WithMany(x => x.EMCDs)
.HasForeignKey(x => x.DocumentCategory).HasPrincipalKey(x => x.Category);
modelBuilder.Entity<udEMDC>().ToTable("udEMDC").HasMany(x => x.EMEDs).WithOne(x => x.EMDC).HasForeignKey(x => new{ x.DocumentCategory}).HasPrincipalKey(x => new{ x.Category});
modelBuilder.Entity<udEMED>().ToTable("udEMED");
modelBuilder.Entity<EMWH>().ToTable("EMWH");
modelBuilder.Entity<EMWI>().ToTable("EMWI");
modelBuilder.Entity<HQAT>().HasOne(x => x.EMWH).WithMany(x => x.HQATs).HasForeignKey(x => x.UniqueAttchID)
.HasPrincipalKey(x => x.UniqueAttchID);
modelBuilder.Entity<EMWH>().HasOne(x => x.EMEM).WithMany(x => x.EMWHs)
.HasForeignKey(x => new {x.EMCo, x.Equipment}).HasPrincipalKey(x => new {x.EMCo, x.Equipment});
EDIT: I added nullable KeyID's just to test prior to uploading and still didn't work.

I think the error is that you're declaring the Key as nullable, which it should never happen.
[Key]
public long? KeyID { get; set; }
change your code to this...
[Key]
public long KeyID { get; set; }

Related

EF7 all related entities in ICollection loaded twice

I have two entities, Function and Department with a Many to Many table in between.
public class Department
{
public Department()
{
DepartmentFunctions = new HashSet<DepartmentFunction>();
PersonFunctions = new HashSet<PersonFunction>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<DepartmentFunction> DepartmentFunctions { get; set; }
public DateTime? DeletedAt { get; set; }
}
public class Function
{
public Function()
{
DepartmentFunctions = new HashSet<DepartmentFunction>();
FunctionTaskProfiles = new HashSet<FunctionTaskProfile>();
PersonFunctions = new HashSet<PersonFunction>();
}
public int Id { get; set; }
public string Name { get; set; }
public float? ItemOrder { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual ICollection<DepartmentFunction> DepartmentFunctions { get; set; }
public virtual ICollection<FunctionTaskProfile> FunctionTaskProfiles { get; set; }
public virtual ICollection<PersonFunction> PersonFunctions { get; set; }
public DateTime? DeletedAt { get; set; }
}
public class DepartmentFunction
{
public int FunctionId { set; get; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
public virtual Function Function { get; set; }
}
When the departmentfunction on either entity is loaded, either with eager loading or lazy loading, every unique DepartmentFunction is listed twice. This problem occured after updating from EF core 5 to EF core 7. I have not changed anything with that update.
I have tried explicitly configuring the relation like this:
modelBuilder.Entity<DepartmentFunction>(entity =>
{
entity.HasKey(e => new {FunctieId = e.FunctionId, AfdelingId = e.DepartmentId})
.HasName("PK__Afdeling__591131EC594F97A1");
entity.HasOne(d => d.Department)
.WithMany(p => p.DepartmentFunctions)
.HasForeignKey(d => d.DepartmentId)
.OnDelete(DeleteBehavior.ClientSetNull);
entity.HasOne(d => d.Function)
.WithMany(p => p.DepartmentFunctions)
.HasForeignKey(d => d.FunctionId)
.OnDelete(DeleteBehavior.ClientSetNull);
I have also seen this solution, however, implementing this one would involve changing hundreds of references.

EF Core group by problem, duplicated rows

The problem is that when the method runs, it creates rows for the CariHareket table, no matter how many rows of the tables linked to the Kontrat.
I can't group by because EF Core doesn't support it (EF Core v3.1.9)
hareketService.GetAll(Predicate())//table name is CariHareket
.Include(s => s.Cari)
.Include(s => s.HedefHareketCariVirman)
.Include(s => s.HareketCari)
.Include(s => s.Kontrat).ThenInclude(s => s.KontratKalemler)
.Include(s => s.Kontrat).ThenInclude(s => s.KontratTarihBaglantilar)
.Include(s => s.Kontrat).ThenInclude(s => s.FaturaTalimativeTurkceFatura)
.Include(s => s.Kontrat).ThenInclude(s => s.AraciCari)
CariHareket and Kontrat model classes:
public class CariHareket
{
[Key]
public int ID { get; set; }
[ForeignKey(nameof(CariID))]
public virtual Cariler Cari { get; set; }
public int? CariID { get; set; }
[ForeignKey(nameof(KontratID))]
public virtual KontratUst Kontrat { get; set; }
public int? KontratID { get; set; }
[ForeignKey(nameof(HareketCariID))]
public virtual Cariler HareketCari { get; set; }
public int? HareketCariID { get; set; }
[ForeignKey(nameof(HedefHareketCariVirmanID))]
public virtual Cariler HedefHareketCariVirman { get; set; }
public int? HedefHareketCariVirmanID { get; set; }
...
}
public class Kontrat
{
...
[InverseProperty("Kontrat")]
public virtual BindingList<KontratMasraflarNotlar> KontratMasraflarNotlar { get; set; }
[InverseProperty("Kontrat")]
public virtual BindingList<KontratKalem> KontratKalemler { get; set; }
[InverseProperty("Kontrat")]
public virtual BindingList<KontratTarihBaglantilar> KontratTarihBaglantilar { get; set; }
[InverseProperty("Kontrat")]
public virtual BindingList<KontratKasa> KontratKasa { get; set; }
[InverseProperty("Kontrat")]
public virtual BindingList<KontratKonteyner> KontratKonteyner { get; set; }
[InverseProperty("Kontrat")]
public virtual BindingList<KontratTurkceFaturaKalem> FaturaTalimativeTurkceFatura { get; set; }
}

Mapping a nested collections using Automapper

I have been trying to map my entities to my viewmodels with AutoMapper. And faced problems with nested collection mapping.
The Source
public class Consignment
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<ConsignmentLine> ConsignmentLines { get; set; }
public ICollection<ConsignmentDocument> ConsignmentDocuments { get; set; }
}
public class ConsignmentLine
{
public Guid Id { get; set; }
public Guid ConsignmentId { get; set; }
public ICollection<ConsignmentDocument> ConsignmentDocuments { get; set; }
}
public class ConsignmentDocument
{
public Guid Id { get; set; }
public Guid ConsignmentId { get; set; }
public Guid ConsignmentLineId { get; set; }
public string DocumentName { get; set; }
}
public class ConsignmentLineViewModel
{
public Guid Id { get; set; }
public Guid ConsignmentId { get; set; }
public ICollection<ConsignmentDocumentViewModel> ConsignmentDocuments { get; set; }
}
public class ConsignmentDocumentViewModel
{
public Guid Id { get; set; }
public Guid ConsignmentId { get; set; }
public Guid ConsignmentLineId { get; set; }
public string DocumentName { get; set; }
}
The destination
public class ConsignmentDetailsViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<ConsignmentLineViewModel> ConsignmentLines { get; set; }
public ICollection<ConsignmentDocumentViewModel> ConsignmentDocuments { get; set; }
}
I can map consignmentDocuments for each consignment very easily but while mapping consignmentlines for each consignment i am getting an "AutoMapper Exception". I know the exception is being generated because of each consignmentLine has it's own collection of consignmentDocuments.
Right now my automapper profile
CreateMap<Consignment, ConsignmentDetailsViewModel>()
.ForMember(vm => vm.consignmentLineViewModel, opt => opt.MapFrom(model => model.ConsignmentLine.ToList()))
.ForMember(vm => vm.consignmentDocumentViews, opt => opt.MapFrom(model => model.ConsignmentDocument.ToList()));
How can I map all of them to the ConsignmentViewModel class?
Resolved the problem.
The solution is to create a map for ConsignmentLine to get the collection of ConsignmentDocuments.
CreateMap<Consignment, ConsignmentDetailsViewModel>()
.ForMember(vm => vm.consignmentLineViewModel, opt => opt.MapFrom(model => model.ConsignmentLine))
.ForMember(vm => vm.consignmentDocumentViews, opt => opt.MapFrom(model => model.ConsignmentDocument));
CreateMap<ConsignmentLine, ConsignmentLineViewModel>()
.ForMember(vm => vm.consignmentDocumentViews, opt => opt.MapFrom(model => model.ConsignmentDocument));
If you act simply without thinking too complex in AutoMapper transactions, you can perform all your transactions.
Example:
CreateMap<Consignment, ConsignmentDetailsViewModel>();
CreateMap<ConsignmentLine, ConsignmentLineViewModel>();
CreateMap<ConsignmentDocument, ConsignmentDocumentViewModel>();

Automapper not mapping all fields that are configured

I have a db objects that i would like to map to my view object in my application, but not every property getting mapped.
here is my Automapper set up:
Here is my View class that i would like AutoMapper to map to
public class CustomerDetails
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Dob { get; set; }
public DateTime CreateDate { get; set; }
public decimal Balance { get; set; }
public List<Email> Emails { get; set; }
public List<Address> Addresses { get; set; }
public class Email
{
public Guid Id { get; set; }
public string EmailName { get; set; }
}
public class Address
{
public Guid Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Country { get; set; }
}
}
Here is db classes:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual CustomerBalance Balance { get; set; }
public virtual ICollection<Email> Emails { get; set; }
public virtual ICollection<Address> Address { get; set; }
public class Email
{
public Guid Id { get; set; }
public string EmailName { get; set; }
public bool IsPrimary { get; set; }
public virtual Customer Customer { get; set; }
}
public class Address
{
public Guid Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Country { get; set; }
}
}
here is how i set up my configuration for AutoMapper
Mapper.Initialize(config =>
{
config.AddProfile<CustomerProfile>();
});
public class CustomerProfile : Profile
{
public CustomerProfile()
{
CreateMap<DataModels.Phone, Phone>();
CreateMap<DataModels.Email, Email>();
CreateMap<DataModels.Address, Address>();
CreateMap<DataModels.CustomerPin, Pin>()
.ForMember(x => x.PinNumber, y => y.MapFrom(s => s.Pin))
.ForMember(x => x.Id, y => y.MapFrom(s => s.Id))
;
CreateMap<Customer, CustomerDetails>()
.ForMember(x => x.Phones, y => y.MapFrom(s => s.Phones))
.ForMember(x => x.Emails, y => y.MapFrom(s => s.Emails))
.ForMember(x => x.Balance, y => y.MapFrom(s => s.Balance.Balance))
.ForMember(x => x.Pins, y => y.MapFrom(s => s.Pin))
.ForMember(x => x.Addresses, y => y.MapFrom(s => s.Address))
;
CreateMap<Customer, CustomerDetails>().ReverseMap();
}
}
The strange thing is is that everything getting mapped as expected, except the Addressesproperty and Balance on my CustomerDetails.cs. The list collection is null even though i specified to map it from a member. However, email List is getting mapped appropriately.
Am I missing something?

Double foreign key generation

This is a followup-question of this question, where i had a similar problem. But this is solved now by default foreign key convention.
My problem now is (in short), that my migrations generates
int ReferencedEntityID;
int ReferencedEntity_ReferencedEntityID;
where one is an integer property in my model and the other one is a virtual property.
My migrations generates this:
"dbo.Contracts",
c => new
{
ContractId = c.Int(nullable: false, identity: true),
PricePerUnit = c.Double(nullable: false),
Unit = c.Int(nullable: false),
Currency = c.Int(nullable: false),
ClientId = c.Int(nullable: false),
CompanyId = c.Int(nullable: false),
ArticleId = c.Int(nullable: false),
Client_ClientId = c.Int(),
Article_ArticleId = c.Int(),
})
As you can see, Client & Article are referenced twice.
Here are my models
public class Client {
public Client() { }
[Key]
public int ClientId { get; set; }
public string Number { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string Memo { get; set; }
public bool isMerchant { get; set; }
public string Name
{
get
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
public int? MerchantReferenceId { get; set; }
public virtual Client MerchantReference { get; set; }
[Required]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Article {
public Article() { }
[Key]
public int ArticleId { get; set; }
[Required]
public string Code { get; set; }
public string Name { get; set; }
public bool TrackStock { get; set; }
public int CurrentStock { get; set; }
public double? Price { get; set; }
[Required]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
[Required]
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Contract {
public Contract() { }
[Key]
public int ContractId { get; set; }
public double PricePerUnit { get; set; }
public int Unit { get; set; }
public int Currency { get; set; }
[Required]
public int ClientId { get; set; }
// [ForeignKey("ClientId")]
public virtual Client Client { get; set; }
[Required]
public int CompanyId { get; set; }
//[ForeignKey("CompanyID")]
public virtual Company Company { get; set; }
[Required]
public int ArticleId { get; set; }
// [ForeignKey("ArticleId")]
public virtual Article Article { get; set; }
}
Here is my OnModelCreating()
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// modelBuilder.Entity<Contract>().HasRequired(bm => bm.Company).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<Contract>().HasRequired(bm => bm.Article).WithMany().HasForeignKey(dl => dl.ArticleId).WillCascadeOnDelete(false);//.Map( dl => dl.MapKey("ArticleId"))
modelBuilder.Entity<Contract>().HasRequired(bm => bm.Client).WithMany().HasForeignKey(dl => dl.ClientId).WillCascadeOnDelete(false);//.Map(dl => dl.MapKey("ClientId"))
modelBuilder.Entity<Article>().HasRequired(bm => bm.Company).WithMany().HasForeignKey(dl => dl.CompanyId).WillCascadeOnDelete(false);//.Map(dl => dl.MapKey("CompanyId"))
modelBuilder.Entity<Measurement>().HasRequired(bm => bm.Company).WithMany().HasForeignKey(dl => dl.CompanyId).WillCascadeOnDelete(false); //.Map(dl => dl.MapKey("CompanyId"))
modelBuilder.Entity<Order>().HasRequired(bm => bm.Client).WithMany().HasForeignKey(dl => dl.ClientId).WillCascadeOnDelete(false); //.Map(dl => dl.MapKey("ClientId"))
modelBuilder.Entity<Order>().HasRequired(bm => bm.Article).WithMany().HasForeignKey(dl => dl.ArticleId).WillCascadeOnDelete(false);//.Map(dl => dl.MapKey("ArticleId"))
modelBuilder.Entity<IncomingMeasurement>().HasRequired(bm => bm.client).WithMany().HasForeignKey(dl => dl.ClientId).WillCascadeOnDelete(false);//.Map(dl => dl.MapKey("ClientId"))
modelBuilder.Entity<Client>().HasOptional(c => c.MerchantReference).WithMany().HasForeignKey(dl => dl.MerchantReferenceId); //.Map(dl => dl.MapKey("MerchantReferenceId"))
//Required fields
base.OnModelCreating(modelBuilder);
}
What do i have to do, to create them both:
Required
Both in one property in my db-schema (as it should)
It is OK, even recommended, to have primitive FK properties (like ArticleId) accompanying the "real" references. In EF this is called a foreign key association as opposed to an independent association where there is only a reference (like Article.Company).
So you can keep your model the way it is. You just have to specify the foreign keys.
I tried with a few classes in the model of your previous question and this produced the desired results:
modelBuilder.Entity<Article>().HasMany(a => a.Contracts)
.WithRequired(c => c.Article)
.HasForeignKey(c => c.ArticleID).WillCascadeOnDelete(false);
modelBuilder.Entity<Client>().HasMany(c => c.Contracts)
.WithRequired(c => c.Client)
.HasForeignKey(c => c.ClientID).WillCascadeOnDelete(false);
modelBuilder.Entity<Company>().HasMany(c => c.Articles)
.WithRequired(a => a.Company)
.HasForeignKey(c => c.CompanyID).WillCascadeOnDelete(false);
Note that I turned around the definitions because when I did it your way, but with HasForeignKey it still duplicated the FK fields. I'm not sure why.

Categories

Resources