Entity Framework duplicated column issue - c#

I have a basic model like that :
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
public List<Contact> Contacts { get; set; }
public Contact PrincipalContact { get; set; }
public int? PrincipalContactId { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public Account Account { get; set; }
public int? AccountId { get; set; }
}
Entity Framework creates two columns on the table Contacts: Account_Id and AccountId.
The column AccountId is always null. I don't know why I have this behavior
What is the proper way to achieve this with Entity Framework ? I tried to add the [ForeignKey] attribute but it doesn't change anything.
Thank you.

In the case of one-to-one relationships, you will need to provide some additional
information so that Code First knows which entity is the principal and which is
the dependent.
public class Account
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public List<Contact> Contacts { get; set; }
[ForeignKey("PrincipalContact")]
public int? PrincipalContactId { get; set; }
public virtual Contact PrincipalContact { get; set; }
}
public class Contact
{
[Key]
[ForeignKey("AccountOf")]
public int Id { get; set; }
public string Name { get; set; }
public virtual Account AccountOf { get; set; }
}

The Account_Id column is created automatically by EF based in the one-to-many relationship. If you don't specify nothing, by convention, EF will recognize the fact that your Navigation property is called Account and your ForeignKey will be called AccountId, but due to you have a property with the same name, EF changes it to Account_Id.
To create both relationships that you need I recommend you modify the model as I show below:
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
public List<Contact> Contacts { get; set; }
public Contact PrincipalContact { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public Account Account { get; set; }
public int? AccountId { get; set; }
}
Then, In your Context you can configure the relationships explicitly using Fluent Api.
public class YourContext : DbContext
{
public IDbSet<Account> Accounts { get; set; }
public IDbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>()
.HasOptional(c => c.Account)
.WithMany(e => e.Contacts)
.HasForeignKey(a => a.AccountId);
modelBuilder.Entity<Account>()
.HasOptional(c => c.PrincipalContact)
.WithMany()
.Map(c => c.MapKey("PrincipalContactId"));
}
}
Update
If you want to keep the PrincipalContactId property on the Account class, you should map the relationship this way:
modelBuilder.Entity<Account>()
.HasOptional(c => c.PrincipalContact)
.WithMany().HasForeignKey(a => a.PrincipalContactId);

Related

Define relationship in EF Core

How do I define the relationships here with EF Core?
I have an Employee table which has multiple Jobs
public class Employee
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<HourlyRate> DefaultRate { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
public DateTime DateHired { get; set; }
public List<PhoneNumber> PhoneNumbers { get; set; }
public List<Address> Addresses { get; set; }
public List<Job> Jobs { get; set; }
public bool Deleted { get; set; }
}
And the Job class has an Employee object to navigate back to the employee and the Job has multiple Directors which are also Employees
public class Job
{
public int JobId { get; set; }
public Employee Employee { get; set; }
public JobType Type { get; set; }
public Department Department { get; set; }
public List<Employee> Directors { get; set; }
public bool Active { get; set; }
public decimal HourlyRate { get; set; }
public string Note { get; set; }
public bool Deduction { get; set; }
public int? DeductionPercent { get; set; }
}
This is my DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasMany(employee => employee.Jobs)
.WithOne(i => i.Employee);
}
Initially the Job only had a single Director and everything was good but the requirement has changed to have multiple directors and ef removed the Director column from the Job table and added a JobId column to the Employee table but the problem is that if i add that director to a second job by job.Directors.Add(director) EF overrides the job id of the of the director and the director is being removed from the previous job
I am using EF Core 2.2
if a Job has only 1 Employee but multiple Directors (also Employee)
add public int EmployeeId {get; set;} to your Job class and add this
modelBuilder
.Entity<Job>()
.HasMany(p => p.Directors)
.WithMany(p => p.Jobs));
also, change List<> to ICollection<>
You should tell EF through fluent API that there's a 1-to-many relationship from Employee to Job. Otherwise, EF may get confused.
The many-to-many relationship needs a junction table and matching entity in the model which you'll also need to configure through fluent API. You'll define two 1-to-many relationships from Employee and Job to that new entity. EF core does not directly support such relationships before 5.0.
If you are targeting SQL, then you need to mark at least one of the relationships as OnDelete(CascadeBehavior.NoAction). Otherwise, your model will generate invalid table defintions which will raise errors at creation time.
Update:
The junction table would be defined something like this.
public class Employee
{
// ... other stuff
public List<EmployeeJob> EmployeeJobs { get; set; }
}
public class Job
{
// ... other stuff
public List<EmployeeJob> EmployeeJobs { get; set; }
}
public class EmployeeJob
{
public int EmployeeId { get; set; }
public int JobId { get; set; }
public Employee Employee { get; set; }
public Job Job { get; set; }
}
// goes in DbContext
modelBuilder.Entity<EmployeeJob>.HasKey(x => new { x.EmployeeId, x.JobId });
Try to use this code. Since your employee can have one or many jobs I added the table EmployeeJob and many-to-many relations. I think you just need to add IsDirector flag to Employee or maybe better something like an EmployeeType:
public class Employee
{
public Employee()
{
EmployeeJobs = new HashSet<EmployeeJob>();
}
[Key]
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
public DateTime DateHired { get; set; }
public bool Deleted { get; set; }
[InverseProperty(nameof(EmployeeJob.Employee))]
public virtual ICollection<EmployeeJob> EmployeeJobs { get; set; }
}
public class Job
{
public Job()
{
EmployeeJobs = new HashSet<EmployeeJob>();
}
[Required]
public int JobId { get; set; }
public bool Active { get; set; }
public decimal HourlyRate { get; set; }
public string Note { get; set; }
public bool Deduction { get; set; }
public int? DeductionPercent { get; set; }
[InverseProperty(nameof(EmployeeJob.Job))]
public virtual ICollection<EmployeeJob> EmployeeJobs { get; set; }
}
public class EmployeeJob
{
[Key]
public int Id { get; set; }
public int EmployeeId { get; set; }
[ForeignKey(nameof(EmployeeId))]
[InverseProperty(nameof(EmployeeJob.Employee.EmployeeJobs))]
public virtual Employee Employee { get; set; }
public int JobId { get; set; }
[ForeignKey(nameof(JobId))]
[InverseProperty(nameof(EmployeeJob.Employee.EmployeeJobs))]
public virtual Job Job { get; set; }
}
public class EmployeeDbContext : DbContext
{
public EmployeeDbContext()
{
}
public EmployeeDbContext(DbContextOptions<EmployeeDbContext> options)
: base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<EmployeeJob> EmployeeJobs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=localhost;Database=Employee;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EmployeeJob>(entity =>
{
entity.HasOne(d => d.Employee)
.WithMany(p => p.EmployeeJobs)
.HasForeignKey(d => d.EmployeeId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_EmployeeJob_Employee");
entity.HasOne(d => d.Job)
.WithMany(p => p.EmployeeJobs)
.HasForeignKey(d => d.JobId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_EmployeeJob_Job");
});
}

The required column 'CustomerId' was not present in the results of a 'FromSql' operation

The error message is clear:
'The required column 'CustomerId' was not present in the results of a
'FromSql' operation'
But somehow I didn't really expect a CustomerId?
The error happens here:
contacts = db.Contacts.FromSql("SIP_API_MONDIA_Contacts_sel").ToList();
addresses = db.Addresses.FromSql("SIP_API_MONDIA_Address_sel").ToList();
Controller:
public IList<Customer> GetAllCustomers()
{
//Initialize the objects
IList<Customer> customers = null;
IList<Contacts> contacts = null;
IList<Addresses> addresses = null;
//Fetch the data from stored procedures
customers = db.Customers.FromSql("SomeProcName").ToList();
contacts = db.Contacts.FromSql("SomeProcName").ToList();
addresses = db.Addresses.FromSql("SomeProcName").ToList();
//Loop through customers and add the contact and addresses when required
foreach(var item in customers)
{
item.Contacts = contacts.Where(x => x.Customer == item.Id).ToList();
item.Addresses = addresses.Where(x => x.Customer == item.Id).ToList();
}
return customers;
}
The model:
public class Customer
{
public Guid Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string VatCode { get; set; }
public string ChamberOfCommerceCode { get; set; }
public DateTime Modified { get; set; }
public DateTime Created { get; set; }
public string LanguageCode { get; set; }
public decimal Discount { get; set; }
public string CustomerManager { get; set; }
public Guid PriceList { get; set; }
public Guid PaymentCondition { get; set; }
// public bool VatLiable { get; set; }
public bool IsBlocked { get; set; }
public bool IsProspect { get; set; }
public bool IsSuspect { get; set; }
public string Website { get; set; }
public string DashboardUrl { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
// public ICollection<FreeFields> FreeFields { get; set; }
// public Dictionary<string, string> UknownElements { get; set; }
public ICollection<Contacts> Contacts { get; set; }
public ICollection<Addresses> Addresses { get; set; }
}
public class FreeFields
{
public string Key { get; set; }
public string Value { get; set; }
}
public class Contacts
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Initials { get; set; }
public string Function { get; set; }
public Guid Customer { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Mobile { get; set; }
public string LanguageCode { get; set; }
public bool IsMainContact { get; set; }
public string Gender { get; set; }
public string Username { get; set; }
}
public class Addresses
{
public Guid Id { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
public string Postcode { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string CountryCode { get; set; }
public string Type { get; set; }
public Guid Customer { get; set; }// This Property should be GUID instead of String..
public bool IsMainAddress { get; set; }
public string Route { get; set; }
public string State { get; set; }
}
I am not entirely sure what the error means with 'CustomerId'
The stored procedures return the 100% exact value of the models.
Edit in order to add print scrn of the sql resultset && DbContext:
public class IsahContext : DbContext
{
public IsahContext()
{
}
public IsahContext(DbContextOptions<IsahContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(Setting.ConnectionString);
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
//Entities will come here
public DbSet<Customer> Customers { get; set; }
public DbSet<Addresses> Addresses { get; set; }
public DbSet<Contacts> Contacts { get; set; }
}
CustomerId is the conventional name for the Foreign Key of the one-to-many relationships introduced by the
public ICollection<Contacts> Contacts { get; set; }
public ICollection<Addresses> Addresses { get; set; }
collection navigation properties of the Customer class.
Although the related classes Contacts and Addresses contain a property Guid Customer, due to its name it's not recognized as a Foreign Key, so it falls into No Foreign Key Property category. And EF Core assumes a shadow property (and column) named CustomerId. Shadow property convention explanation is:
Shadow properties can be created by convention when a relationship is discovered but no foreign key property is found in the dependent entity class. In this case, a shadow foreign key property will be introduced. The shadow foreign key property will be named <navigation property name><principal key property name> (the navigation on the dependent entity, which points to the principal entity, is used for the naming). If the principal key property name includes the name of the navigation property, then the name will just be <principal key property name>. If there is no navigation property on the dependent entity, then the principal type name is used in its place.
In order to map the Customer property as a FK, you should use either ForeignKey attribute:
You can use the Data Annotations to configure which property should be used as the foreign key property for a given relationship. This is typically done when the foreign key property is not discovered by convention.
Tip
The [ForeignKey] annotation can be placed on either navigation property in the relationship. It does not need to go on the navigation property in the dependent entity class.
e.g. (since you have no navigation property in the dependent entities):
[ForeignKey(nameof(Contacts.Customer))]
public ICollection<Contacts> Contacts { get; set; }
[ForeignKey(nameof(Addresses.Customer))]
public ICollection<Addresses> Addresses { get; set; }
or Fluent API:
modelBuilder.Entity<Customer>()
.HasMany(customer => customer.Contacts)
.WithOne() // no nav property
.HasForeignKey(contact => contact.Customer); // the FK property
modelBuilder.Entity<Customer>()
.HasMany(customer => customer.Addresses)
.WithOne() // no nav property
.HasForeignKey(address => address.Customer); // the FK property
Could you please also post the relevant parts of the DB context class and an example result of the stored procedure.
Without those, I can only guess:
The Identity column is not marked with the [Key] attribute, nor it follows the standard naming convention of "EntityNameId" therefore it is not able to deduct the identity column.
So, I suggest adding the [Key] attribute to the Id property:
[Key]
public Guid Id { get; set; }
If that doesn't work, please post the context and the SP result.

ASP.NET MVC customer portal User Junction Table

Im building a management portal for a chain of restaurants. I am using ASP.NET MVC with EF Code First.
I want each user to, after login, only see the rescources that are connected to them. I want to put a junction table(many-to-many) between ApplicationUser and the Restaurant-class(model), since each user can have/work at many restaurants, and each restaurant can have many owners/workers.
How do you do this in EF Code first? The same way I did Restaurant --> Menue? Do you need to build a new DBContext for Applicationuser for this to work?
public class Restaurant
{
public int Id { get; set; }
public string Name { get; set; }
public string Adress { get; set; }
public string PhoneNumber { get; set; }
public DateTime StartDate { get; set; }
//Connections
public virtual ICollection<Menue> Menues { get; set; }
}
public class Menue
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public DateTime ModifyDate { get; set; }
//FK For RestaurantConnection
public int RestaurantId { get; set; }
}
For many to many configuration do like this
Student class should have a collection navigation property for Course, and Course should have a collection navigation property for student
public class Student
{
public Student()
{
this.Courses = new HashSet<Course>();
}
public int StudentId { get; set; }
[Required]
public string StudentName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
In your DbContext add this configuration
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany<Course>(s => s.Courses)
.WithMany(c => c.Students)
.Map(cs =>
{
cs.MapLeftKey("StudentRefId");
cs.MapRightKey("CourseRefId");
cs.ToTable("StudentCourse");
});
}
For more information read this article Configure Many-to-Many relationship

Can I create multiple associations between two entities in Entity Framework 5, c#?

E.g. I have 2 entities:
public class Department
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
public Employee Manager { get; set; }
}
public class Employee
{
public Guid Id { get; set; }
public string FullName { get; set; }
[ForeignKey("DepartmentId")]
public Department Department { get; set; }
public Guid DepartmentId { get; set; }
public string Position { get; set; }
}
I know I can associate (as I already did:) Department.Employees with Employee.Id (and inverse: Employee.Department with Department.Id).
Questions:
1) Is this one or two associations?
2) Can I create the second association between Department.Manager and Employee.Id? Don' want to store Managers in another table so they are also stored in Employee table and have in Position field "Manager".
Define the relationship as follows.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>()
.HasRequired(a => a.Manager)
.WithMany()
.HasForeignKey(a => a.EmployeeId);
}
You want them virtual too if you want lazy loading and change tracking.
public class Department
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
public virtual Employee Manager { get; set; }
}

Defining many to many relation in code first entity framework

I have 3 classes in my model as you can see below.
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
public ICollection<MartialArtUserProfile> MartialArtUserProfiles { get; set; }
}
[Table("MartialArt")]
public class MartialArt
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string IconPath { get; set; }
public string ImagePath { get; set; }
public ICollection<MartialArtUserProfile> MartialArtUserProfiles { get; set; }
}
public class MartialArtUserProfile
{
public int UserProfileId { get; set; }
public UserProfile UserProfile { get; set; }
public int MartialArtId { get; set; }
public MartialArt MartialArt { get; set; }
}
And I have a configuration class for many to many relationship as below:
public class MartialArtUserProfileConfiguration : EntityTypeConfiguration<MartialArtUserProfile>
{
public MartialArtUserProfileConfiguration()
{
HasKey(a => new { a.MartialArtId, a.UserProfileId });
HasRequired(a => a.MartialArt)
.WithMany(s => s.MartialArtUserProfiles)
.HasForeignKey(a => a.MartialArtId)
.WillCascadeOnDelete(false);
HasRequired(a => a.UserProfile)
.WithMany(p => p.MartialArtUserProfiles)
.HasForeignKey(a => a.UserProfileId)
.WillCascadeOnDelete(false);
}
}
After defining my entities an relation when I try to run Update-Database in Package Manager Console, it says:
One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmEntityType: : EntityType 'MartialArtUserProfile' has no key defined. Define the key for this EntityType.
\tSystem.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'MartialArtUserProfiles' is based on type 'MartialArtUserProfile' that has no keys defined.
What am I doing wrong?
Thanks in advance,
If I understand you are simply trying to create a many to many with a transitive table. If so this is another way to approach this. Use Fluent API to map as below. You can change the UserProfileToMartialArt to whatever you want the table name to be. Instead of creating the MartialArtUserProfile model let EF create the middle ground for you. This also specifies your keys which should get you around the error.
modelBuilder.Entity<UserProfile>()
.HasMany(b => b.MartialArts)
.WithMany(a => a.UserProfiles)
.Map(m => m.MapLeftKey("MartialArtId")
.MapRightKey("UserProfileId")
.ToTable("UserProfileToMartialArt"));
In MartialArts Model put
public IList<UserProfile> UserProfiles { get; set; }
In UserProfile Model put
public IList<MartialArt> MartialArts { get; set; }
Try doing it like this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
[InverseProperty("UserProfiles")]
public IList<MartialArt> MartialArts { get; set; }
}
[Table("MartialArt")]
public class MartialArt
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string IconPath { get; set; }
public string ImagePath { get; set; }
[InverseProperty("MartialArts")]
public IList<UserProfile> UserProfiles { get; set; }
}
In EntityFramework 6.1, you don't need to do any of this - just add collections of the two types to each class and everything falls into place.
public class UserProfile {
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<MartialArt> MartialArts { get; set; }
public UserProfile() {
MartialArts = new List<MartialArt>();
}
}
public class MartialArt {
public int Id { get; set; }
public string Name { get; set; }
// *snip*
public virtual ICollection<UserProfile> UserProfiles { get; set; }
public MartialArt() {
UserProfiles = new List<UserProfile>();
}
}

Categories

Resources