Entity Framework multiple relationships between tables - linq

I've have two tables in my project which are User and InvoiceLine.
It has been specified that an InvoiceLine can have a User known as a Checker.
My models are:
public class InvoiceLine : IEntity
{
public virtual int Id { get; set; }
public virtual int? CheckerId { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual DateTime CreatedDateTime { get; set; }
public virtual string LastModifiedByUserName { get; set; }
public virtual DateTime? LastModifiedDateTime { get; set; }
// Navigation properties
public virtual User Checker{ get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
public string CreatedByUserName { get; set; }
public DateTime CreatedDateTime { get; set; }
public string LastModifiedByUserName { get; set; }
public DateTime? LastModifiedDateTime { get; set; }
//Navigation properties
public virtual ICollection<InvoiceLine> InvoiceLines { get; set; }
}
So this was fine I have a 0..1 to many relationship from User to InvoiceLine.
This meant with Linq I could get the InvoiceLines the User needs to check via:
user.InvoiceLines
However there is another requirement that an InvoiceLine also has an Auditor so I modified the InvoiceLine to:
public class InvoiceLine : IEntity
{
public virtual int Id { get; set; }
public virtual int? CheckerId { get; set; }
public virtual int? AuditorId { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual DateTime CreatedDateTime { get; set; }
public virtual string LastModifiedByUserName { get; set; }
public virtual DateTime? LastModifiedDateTime { get; set; }
// Navigation properties}
public virtual User Checker { get; set; }
public virtual User Auditor { get; set; }
}
So what I was really wanting was to go:
user.InvoiceLines
and get the Checkers and Auditors or alternatively get them seperately via:
user.CheckerInvoiceLines
user.AuditorInvoiceLines
I'm getting null back from user.InvoiceLines though which is understandable.
Could someone please point me in the right direction on how to use Linq to get the InvoiceLines from the User?
Edit Update:
My model configuration code is like:
public class VectorCheckContext : DbContext
{
...
public DbSet<InvoiceLine> InvoiceLines { get; set; }
public DbSet<User> Users { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
}

You need to use fluent mappings to configure the relationships when EF can not resolve them by conventions.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
//other mappings
modelBuilder.Entity<InvoiceLine>()
.HasOptional(i => i.Checker)
.WithMany(u => u.CheckerInvoiceLines)
.HasForeignKey(i => i.CheckerId);
modelBuilder.Entity<InvoiceLine>()
.HasOptional(i => i.Auditor)
.WithMany(u => u.AuditorInvoiceLines)
.HasForeignKey(i => i.AuditorId);
}

Related

How do I define this relationship with code-first?

I'm trying to create a library of books.
I separated data and users context/database for security reasons (and clarity) but is it that useful?
Specially since BookOfUserEntity.Id should be really a composite key between UserId & BookId.
Something like :
public class BookOfUserEntity
{
[Key]
public Guid UserId { get; set; }
[Key]
public int BookId { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BookOfUserEntity>()
.HasKey(b => new { b.UserId, b.BookId });
}
But then if I create a composite key what should be HistoryEntity.BookOfUserEntityId type & value?
I also separated BookEntity & BookOfUserEntity to avoid duplicated data and reuse what can be reused (MainCoverArtId etc...), but this added complexity - should I go back to a simpler model?
public class BookEntity
{
public int Id { get; set; }
public Guid? MainCoverArtId { get; set; }
...
public virtual List<BookOfUserEntity>? BookOfUserEntities { get; set; } // Only purpose is to know how many Users have this very book in their list.
}
public class BookOfUserEntity // Bad name here don't mention it
{
public int Id { get; set; }
public Guid UserId { get; set; }
public int BookId { get; set; }
public BookEntity Book { get; set; } // Useful? Should be virtual?
public List<HistoryEntity>? HistoryEntities { get; set; }
...
}
public class HistoryEntity
{
public int Id { get; set; }
public int BookOfUserEntityId { get; set; }
public BookOfUserEntity BookOfUserEntity { get; set; } // Same questions
public int Vol { get; set; }
public double Chapter { get; set; }
public DateTime? ReadDate { get; init; }
}

Querying Many to Many relationship table to entities MVC

I have many to many relationship tables. Sube and User
namespace Odev.Entities
{
public class User
{
[Key]
public int Id { get; set; }
[Required]
[DisplayName("Kimlik No")]
[ StringLength(11)]
[Index(IsUnique = true)]
public string UserName { get; set; }
[ StringLength(8)]
[DisplayName("Şifre")]
public string Password { get; set; }
[DisplayName("Ad Soyad")]
public string NameSurname { get; set; }
[DisplayName("Bölüm")]
public string Bolum { get; set; }
[DisplayName("Dal")]
public string Dal { get; set; }
[DisplayName("Öğrenci No")]
public int OgrenciNo { get; set; }
public int RoleId { get; set; }
public virtual ICollection<Sube> Subes { get; set; }
public virtual List<Notification> Notifications { get; set; }
public virtual List<Homework> Homeworks { get; set; }
public virtual Role Role { get; set; }
}
}
namespace Odev.Entities
{
public class Sube
{
[Key]
public int Id { get; set; }
[DisplayName("Şube")]
public string Sube_Name { get; set; }
public virtual Homework Homework { get; set; }
public virtual ICollection<User> Users { get; set; }
}
}
namespace Odev.DataAccessLayer
{
public class DatabaseContext : DbContext
{
public DatabaseContext() : base("dataConnection")
{
Database.SetInitializer(new OdevInitializer());
}
public DbSet<User> Users { get; set; }
public DbSet<Homework> Homeworks { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Sube> Subes { get; set; }
public DbSet<Notification> Notifications { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
and my ViewModel
namespace Odev.Models{
public class ViewModel{
public string Sube_Name { get; set; }
public string NameSurname { get; set; }
public string UserName { get; set; }
public int UserId { get; set; }
public int SubeId { get; set; }
public User User { get; set; }
public Sube Sube { get; set; }
public Role Role { get; set; }
}}
I want to show User's SubeId:
How I select Sube's id ?
You need connect both entities with a foreign key.
Please, read this doc: Creating a More Complex Data Model for an ASP.NET MVC Application

Entity Framework not binding entity

I have the following db structure:
I am using EF6 to create the entities from database and have the following classes created by EF6:
public partial class Mechanic
{
public Mechanic()
{
this.MechanicAddresses = new HashSet<MechanicAddress>();
this.MechanicServices = new HashSet<MechanicService>();
}
public string ID { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public bool TwoFactorEnabled { get; set; }
public Nullable<System.DateTime> LockoutEndDateUtc { get; set; }
public bool LockoutEnabled { get; set; }
public int AccessFailedCount { get; set; }
public string UserName { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string MobileNumber { get; set; }
public Nullable<bool> IsMobile { get; set; }
public string Url { get; set; }
public string FaceBookUrl { get; set; }
public string TwitterUrl { get; set; }
public string Description { get; set; }
public string Discriminator { get; set; }
public bool IsEnabled { get; set; }
public bool IsAuthorised { get; set; }
public string Logo { get; set; }
public System.DateTime CreationTimestamp { get; set; }
public virtual ICollection<MechanicAddress> MechanicAddresses { get; set; }
public virtual ICollection<MechanicService> MechanicServices { get; set; }
}
public partial class MechanicAddress
{
public int ID { get; set; }
public string MechanicId { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
public string District { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public int CountryId { get; set; }
public System.DateTime CreationTimestamp { get; set; }
public bool IsPrimary { get; set; }
public Nullable<double> Latitude { get; set; }
public Nullable<double> Longitude { get; set; }
public System.Data.Entity.Spatial.DbGeography Location { get; set; }
public virtual Country Country { get; set; }
public virtual Mechanic Mechanic { get; set; }
}
public partial class MechanicService
{
public int ID { get; set; }
public string MechanicId { get; set; }
public string Service { get; set; }
public virtual Mechanic Mechanic { get; set; }
}
The data is correct so i expect to get data in all entities.
When i run the following linq query in my DAL:
Mechanic mech = context.Mechanics.Where(a => a.ID == id).Include(a => a.MechanicAddresses).Include(a => a.MechanicServices).FirstOrDefault();
It returns the mechanic and mechanicAddresses but mechanicServices is always empty (count == 0).
When i run the same query in LinqPad I get all entities filled as expected.
I have removed the edmx and re-created it but still get the same issue.
Please check if "MultipleActiveResultSets" is set to true and LazyLoadingEnabled is enabled in connection string. It may help.
And what about your OnModelCreating?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
It's not necessary to use Include if you have LazyLoading (virtual). And If it works fine in LinqPad try to do migration into empty DB (just test). And then try to get data from test DB.
The only way i was able to resolve this was to:
delete the EDMX
script the create for the mechanicsServices table
script the data
drop the mechanicsServices table
run the create table script from above
run the insert data script
regenerate the EDMX
This now works, WTF! Can't explain it.
I know it's always best to understand what went wrong but this one beat me.
I had same problem.
If you using git, please check .edmx file old version. SSDL content may be missing.

TryUpdateModel Error

First time I can add Allergies into my DB without a problem is As below screen.
But when I try to add 2nd record (after save the first record) then it gives below mentioned run time exception (is as below screen).
Run time Exception
An error occurred while saving entities that do not expose foreign key
properties for their relationships. The EntityEntries property will
return null because a single entity cannot be identified as the source
of the exception. Handling of exceptions while saving can be made
easier by exposing foreign key properties in your entity types. See
the InnerException for details.
Stack Trace (this is for when I try to add 2nd record for medical table.But it is same for Allergies Table also)
Violation of PRIMARY KEY constraint 'PK__Medicati__3214EC0768A0EA12'.
Cannot insert duplicate key in object 'dbo.Medications'. The statement
has been terminated.
Action Method
[HttpPost]
public ActionResult EditMedicalInfo(string providerKey, string ownerKey, string petKey)
{
var pet = Repository.GetPet(ownerKey, petKey);
if (TryUpdateModel(pet))
{
Repository.Save();
}
var url = Url.AbsoluteRouteUrl("PetDetail", new { controller = "customers", action = "detail", providerKey = providerKey, ownerKey = ownerKey, petKey = petKey }) + "#medical";
return Redirect(url);
}
Pet Model
public class Pet {
public Pet() { Id = Guid.NewGuid(); Created = DateTime.Now; }
public Guid Id { get; set; }
public virtual Owner Owner { get; set; }
[StringLength(50), Required]
public string Name { get; set; }
public string Key { get; set; }
public DateTime Created { get; set; }
[Display(Name = "Birth Date"), DataType(DataType.Date)]
public DateTime? BirthDate { get; set; }
[EnumDataType(typeof(PetType)), UIHint("EnumerationList")]
[Required]
public int Type { get; set; }
[Required]
public Guid BreedId { get; set; }
[Display(Name = "Breed"), ForeignKey("BreedId")]
public virtual Breed Breed { get; set; }
[EnumDataType(typeof(Gender)), UIHint("EnumerationList")]
[Required]
public int? Gender { get; set; }
public double Weight { get; set; }
[Display(Name = "License #")]
public string LicenseNumber { get; set; }
[Display(Name = "Microchip #")]
public string MicrochipNumber { get; set; }
public int? AgeValue { get { return (BirthDate.HasValue) ? (int)(DateTime.Today - BirthDate.Value).TotalDays : default(int?); } }
public string Age { get { return (BirthDate.HasValue) ? BirthDate.Value.ToAge() : "Unknown"; } }
public virtual ICollection<PetPhoto> Photos { get; set; }
public virtual ICollection<Appointment> Appointments { get; set; }
public virtual ICollection<MedicalRecordOrder> Orders { get; set; }
public virtual ICollection<PetDocument> Documents { get; set; }
public virtual ICollection<PetNote> Notes { get; set; }
public virtual ICollection<PetProvider> Providers { get; set; }
public virtual ICollection<PetService> PetServices { get; set; }
public Guid? Avatar { get; set; }
public virtual MedicalRecord Medical { get; set; }
public virtual BehavioralRecord Behavioral { get; set; }
public virtual DietRecord Diet { get; set; }
public Guid? EmergencyVeterinarianId { get; set; }
[ForeignKey("EmergencyVeterinarianId")]
public virtual Provider EmergencyVeterinarian { get; set; }
public virtual ICollection<PetContact> Contacts { get; set; }
[EnumDataType(typeof(ProfileCreatorType))]
public int ProfileCreator { get; set; }
[EnumDataType(typeof(PetClassification)), UIHint("EnumerationList")]
public int Classification { get; set; }
[UIHint("InsuranceCarrier")]
public virtual string InsuranceCarrier { get; set; }
// Non persisted extensions
/// <summary>
/// Non Persisted
/// </summary>
[NotMapped]
public List<AppointmentInfo> AppointmentInfos { get; set; }
/// <summary>
/// Non Persisted
/// </summary>
[NotMapped]
public List<AppointmentInfo> SiblingAppointmentInfos { get; set; }
public IList<ReservationRequest> ReservationRequests { get; set; }
[UIHint("QuickList")]
public virtual ICollection<SpecialInstruction> SpecialInstructions { get; set; }
public virtual PetSitterRestrictionPermission PetSitterRestrictionPermission { get; set; }
public virtual PetSitterBehavior PetSitterBehavior { get; set; }
public virtual PetSitterCleaningRecord PetSitterCleaningRecord { get; set; }
public virtual PetSitterNote PetSitterNote { get; set; }
}
Allergy Model
public class Allergy {
public Allergy() { Id = Guid.NewGuid(); }
[ScaffoldColumn(false)]
public Guid Id { get; set; }
public string Name { get; set; }
public string Treatment { get; set; }
}
How could I avoid above error when I try to add 2nd record ?

Data Annotation for foreign key relationship

I have two classes
public class Project
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int ManagerID { get; set; }
public int CoordID { get; set; }
[ForeignKey("ManagerID")]
public virtual Employee Manager { get; set; }
[ForeignKey("CoordID")]
public virtual Employee Coord { get; set; }
}
public class Employee
{
[Key]
public int EmpID { get; set; }
public string Name { get; set; }
[InverseProperty("ManagerID")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("CoordID")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
The ManagerID and CoordID map to the EmpID column of the Employee table.
I keep getting an error for Invalid Columns becauce EF is not able to map correctly. I think it is looking for wrong column.
I think InverseProperty is used to refer to the related navigation property, not the foreign key, e.g.
public class Employee
{
[Key]
public int EmpID { get; set; }
public int Name { get; set; }
[InverseProperty("Manager")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("Coord")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
Also, is there a reason why your names are ints and not strings?
Best guess would be to use fluent API in your context via OnModelCreating. By renaming the column, EF can't figure out the original object to map so it's confused. However, Fluent API allows you to manually specify the map using something like the following:
public class MyContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Project> Projects { get; set; }
protected override OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasRequired(x => x.Manager)
.WithMany(x => x.ManagerProjects)
.HasForeignKey(x => x.ManagerID);
modelBuilder.Entity<Project>()
.HasRequired(x => x.Coord)
.WithMany(x => x.CoordProjects)
.HasForeignKey(x => x.CoordID);
}
}

Resources