Entity Framework: The Entity with schema was already defined - c#

In my database, I have two tables, namely Book and User, that have a M-to-M relationship table named BookXUser. In the relationship table, there are two fields, which are the foreign keys of the Book and User, and another field named bookShelf. Since using only Entity Framework I couldn't find a way to insert a M-to-M relationship while inserting into the bookShelf at the same time, I decided to make a model for the relationship table, which are defined like this:
modelBuilder.Entity<Book>()
.HasMany<UserData>(s => s.user)
.WithMany(c => c.book)
.Map(cs =>
{
cs.MapLeftKey("bookID");
cs.MapRightKey("userID");
cs.ToTable("BookXUser");
});
modelBuilder.Entity<BookXUser>()
.HasRequired<UserData>(s => s.user)
.WithMany(g => g.bookXuser)
.HasForeignKey<int>(s => s.userID);
modelBuilder.Entity<BookXUser>()
.HasRequired<Book>(s => s.book)
.WithMany(g => g.bookXuser)
.HasForeignKey<int>(s => s.bookID);
The problems arrive when I execute the following line:
userDataRepo.FindBy(user => user.email == entry.email).FirstOrDefault()
The EntitySet 'BookUserData' with schema 'dbo' and table 'BookXUser' was already defined. Each EntitySet must refer to a unique schema and table.
How can I solve this error while keeping the relationship table as a model which references the BookXUser? The idea is that I am using lazy loading on the virtual methods found in the model of BookXUser so I can get my books and users more easily, and I also manually insert my data in it so I can populate the bookshelf field.
public class BookXUser : IEntityBase
{
public int ID { get; set; }
public int bookID { get; set; }
public virtual Book book { get; set; }
public int userID { get; set; }
public virtual UserData user { get; set; }
public string bookShelf { get; set; }
}
The Book entity
public class Book : IEntityBase
{
public int ID { get; set; }
public string title { get; set; }
public string isbn { get; set; }
public int noPage { get; set; }
public string edition { get; set; }
public string bLanguage { get; set; }
public byte[] bookPic { get; set; }
public string publisherSite { get; set; }
public string bookFormat { get; set; }
public DateTime releaseDate { get; set; }
public DateTime initialReleaseDate { get; set; }
public string publisher { get; set; }
public string overview { get; set; }
public virtual ICollection<Author> author { get; set; }
public virtual ICollection<Genre> genre { get; set; }
public virtual ICollection<UserData> user { get; set; }
public virtual ICollection<Rating> rating { get; set; }
public virtual ICollection<Review> review { get; set; }
public virtual ICollection<BookXUser> bookXuser { get; set; }
public Book()
{
this.author = new HashSet<Author>();
this.genre = new HashSet<Genre>();
this.user = new HashSet<UserData>();
this.rating = new HashSet<Rating>();
this.review = new HashSet<Review>();
this.bookXuser = new HashSet<BookXUser>();
}
}
The UserData enity
public class UserData : IEntityBase
{
public int ID { get; set; }
public string username { get; set; }
public string userpass { get; set; }
public string email { get; set; }
public byte[] userPic { get; set; }
public string userOverview { get; set; }
public DateTime joinedDate { get; set; }
public virtual ICollection<Book> book { get; set; }
public virtual ICollection<Review> review { get; set; }
public virtual ICollection<Rating> rating { get; set; }
public virtual ICollection<UserData> user { get; set; }
public virtual ICollection<BookXUser> bookXuser { get; set; }
public UserData()
{
this.book = new HashSet<Book>();
this.review = new HashSet<Review>();
this.rating = new HashSet<Rating>();
this.user = new HashSet<UserData>();
this.bookXuser = new HashSet<BookXUser>();
}
}

You defined your Book and UserData entities in a wrong way. As you say in your question you are trying to define a many-to-many relashionship between Book and UserData so you added a join entity BookXUser because that entity hold a data BookShelf. That join entity will use by convention BookXUser as a table name.
Also, you defined two collections:
ICollection<UserData> user in your Book entity
ICollection<Book> book in your UserData entity
You also use this fluent configuration in your OnModelCreating method:
modelBuilder.Entity<Book>()
.HasMany<UserData>(s => s.user)
.WithMany(c => c.book)
.Map(cs =>
{
cs.MapLeftKey("bookID");
cs.MapRightKey("userID");
cs.ToTable("BookXUser"); // <-- this is your error.
});
Doing it like this, you're adding another many-to-many relashionship between Book and UserData and use BookXUser as the name for the join table which is already used by your entity BookXUser you've created.
To solve your issue you don't need to add the collections and to configure many-to-many relationship fluently like you did. Why? Bacause you have an join entity BookXUser.
So :
remove ICollection<UserData> user from your Book entity and replace it with ICollection<BookXUser> BookXUser
remove ICollection<Book> book from your UserData entity and replace it with ICollection<BookXUser> BookXUser
remove the fluent configuration you added for many-to-many relashionship between UserData and Book.

Related

Exception in Entity Framework SaveChanges?

I'm trying to make an insert in a SQL database using Entity Framework 6 and I'm stuck on this issue that I cannot solve.
The error that I keep getting is :
UpdateException: Entities in 'Connect.CompanyFinancialDetails' participate in the 'Company_CompanyFinancialDetails' relationship. 0 related 'Company_CompanyFinancialDetails_Source' were found. 1 'Company_CompanyFinancialDetails_Source' is expected
I have these 2 entities:
public class Company
{
public long CUI { get; set; }
public string UserName { get; set; }
public string CompanyName { get; set; }
public string Symbol { get; set; }
public int? SharesCount { get; set; }
public decimal? SharePrice { get; set; }
public virtual Account Account { get; set; }
public virtual CompanyFinancialDetails CompanyFinancialDetails { get; set; }
}
public class CompanyFinancialDetails
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
// other properties
public decimal? NumberOfEmployees { get; set; }
public virtual Company Company { get; set; }
}
This is the Fluent API configuration:
public DbSet<Account> SignUpModels { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<CompanyFinancialDetails> CompanyFinancialDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>()
.HasKey(k => k.Id)
.HasOptional(s => s.Company)
.WithRequired(d => d.Account);
modelBuilder.Entity<Company>()
.HasKey(k => k.CUI)
.HasOptional(s => s.CompanyFinancialDetails)
.WithRequired(d => d.Company);
}
The relationship that I want to have is 1-many (one Company has many CompanyFinancialDetails).
This is the code where I add the objects to the database:
Company co = Context.Find(username);
foreach (CompanyFinancialDetails s in c)
{
s.Company = co;
}
a.CompanyFinancialDetails.AddRange(c);
a.SaveChanges();
I get a list of CompanyFinancialDetails and I add them using the AddRange method. I had this issue before and what I did was to add the virtual property object to the object that I wanted to insert in the database and it worked. This is what I tried to do here: the Find() method gets the company object that is related to the CompanyFinancialDetails and for each CompanyFinancialDetails object an Company virtual property is adding the related company object.
Well, it didn't work, when the SaveChanges() method is called, I get that error. Any help would be appreciated.

Load related data without foreign key constraint in ef-core-2.1

I want to load related entities data Parent by using Eager Loading O/RM pattern. But I can't specify a foregin key constraint on ParentId because it creates a cycle which is not allowed. Currently, I'm using an inner join to load Parent data explicitly.
Here is my Domain Model that I'm using.
[Table("Category")]
public class CategoryDM
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[Display(Name="Parent")]
public int ParentId { get; set; }
[NotMapped]
public CategoryDM Parent { get; set; }
}
Is there any way to load related entities like this? or any other recommended way to achieve this.
var result = _context.Category.Include(e => e.Parent);
This should work fine, here is an exemplary working model.
Model
public class Category : ISelfRelated<Category>
{
public int Id { get; set; }
public string Name { get; set; }
public string ThumbnailUrl { get; set; }
public int? ParentId { get; set; }
public Category Parent { get; set; }
public IEnumerable<Category> Children { get; set; }
}
Model configuration
category.HasOne(c => c.Parent)
.WithMany(c => c.Children)
.HasForeignKey(c => c.ParentId)
.HasPrincipalKey(c => c.Id)
.OnDelete(DeleteBehavior.Restrict)
.IsRequired(false);

Entity Framework: map multiple similar classes to one table in similar databases

I have model with table in databases of my clients:
public class Doctor
{
public int Id { get; set; }
public int Filial { get; set; }
public string ShortName { get; set; }
public string FullName { get; set; }
public string Phone { get; set; }
public int? DepartmentId { get; set; }
public Department Department { get; set; }
}
public class DoctorConfiguration : EntityTypeConfiguration<Doctor>
{
public DoctorConfiguration()
{
ToTable("DOCTOR");
Property(d => d.Id).HasColumnName("DCODE").IsRequired();
Property(d => d.Filial).HasColumnName("FILIAL");
Property(d => d.ShortName).HasColumnName("DNAME");
Property(d => d.FullName).HasColumnName("FULLNAME");
Property(d => d.Phone).HasColumnName("DPHONE");
Property(d => d.DepartmentId).HasColumnName("DEPNUM");
HasKey(d => d.Id);
HasOptional(d => d.Department).WithMany(dep => dep.Doctors).HasForeignKey(d => d.DepartmentId);
}
}
Recently additional clients came. Their databases has mostly the same models, but some fields had changed types from int to long.
The new Doctor model looks like:
public class Doctor
{
public long Id { get; set; }
public long Filial { get; set; }
public string ShortName { get; set; }
public string FullName { get; set; }
public string Phone { get; set; }
public long? DepartmentId { get; set; }
public Department Department { get; set; }
}
How to properly map new Doctor model format to the same table "DOCTOR"?
The application uses Firebird database. Version for "old" clients doesn't support long numbers format.
In case of creating similar Doctor configuration, an error appears:
"The entity types 'DoctorInt' and 'Doctor' cannot share table 'DOCTOR' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them."
I know about Table-per-Hierarchy (TPH) Inheritance. Looks like it can't help in this situation.
The type Doctor is only the one of the many similar types with this problem. The code in an application is connected with first models format. I wouldn't like to change it all...
I would like to reuse existing functionality.
If I don't misunderstand, you need to support old and new databases with the same code and databases only differs on the size of IDs
An approach is to use generics and conditional compilation
public class Doctor<T> {
public T Id { get; set; }
public int Filial { get; set; } //Suposing Filial is not a foreing key
public string ShortName { get; set; }
public string FullName { get; set; }
public string Phone { get; set; }
public T? DepartmentId { get; set; }
public Department Department { get; set; }
}
When istantiating:
#ifdef USELONG
var d = new Doctor<long>();
#else
var d = new Doctor<int>();
#endif
Or with a factory pattern (where CreateDoctor may be a static method of Doctor class):
var d = Doctor.CreateDoctor();

The navigation on entity type has not been added to the model, or ignored, or entityType ignored

The navigation 'Tags' on entity type 'Notepad.Models.Note' has not been added to the model, or ignored, or entityType ignored.
public class Note
{
public Note()
{
CreationDate = DateTime.Now;
Tags = new HashSet<Tag>();
Parts = new HashSet<Part>();
}
public int ID { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual ICollection<Part> Parts { get; set; }
public DateTime? CreationDate { get; set; }
}
public class Tag
{
public Tag()
{
Notes = new HashSet<Note>();
}
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
It happens while adding a migration:
dnx ef migrations add DbData -c DataDbContext
Why do you think it happens?
EDIT:
DataDbContext:
public class DataDbContext : DbContext
{
public DbSet<Note> Notes { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Part> Parts { get; set; }
}
You have Many-to-many relationship there. As the documentation says: http://docs.efproject.net/en/latest/modeling/relationships.html#id21
Many-to-many relationships without an entity class to represent the join table are not yet supported. However, you can represent a many-to-many relationship by including an entity class for the join table and mapping two separate one-to-many relationships.
So you must create additional "join" class like this:
public class NoteTag
{
public int NoteId { get; set; }
public Note Note { get; set; }
public int TagId { get; set; }
public Tag Tag { get; set; }
}
then, replace
ICollection<Tag> Tags {set;get}
in your Note class to
ICollection<NoteTag> NoteTags {set;get}
and also in Tag class:
ICollection<Note> Notes {set;get;}
to
ICollection<NoteTags> NoteTags {set;get}
and then override OnModelCreating method in DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<NoteTag>()
.HasKey(t => new { t.NoteId, t.TagId });
modelBuilder.Entity<NoteTag>()
.HasOne(pt => pt.Note)
.WithMany(p => p.NoteTags)
.HasForeignKey(pt => pt.NoteId);
modelBuilder.Entity<NoteTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.NoteTags)
.HasForeignKey(pt => pt.TagId);
}
I am using EF 7, this problem took around 2 hours of my week end. :)
So, here is the simple solution -
I am having a profile class like this -
[Table("Profile")]
public class Profile
{
public Profile()
{
}
[Column(Order = 1)]
[Key]
public Guid ProfileID { get; set; }
[JsonIgnore]
public virtual ICollection<StudentLivingWith> StudentProfileMap { get; set; }
[JsonIgnore]
public virtual ICollection<StudentLivingWith> ParentProfileMap { get; set; }
}
I am using the ProfileID as a F-Key reference in my another table named "StudentLivingWith". (ya, I know the name is bit strange. :)) As you can see in below class, both the columns "StudentProfileID" and "ParentProfileID" refering to the same column "profileID" of my "Profile" table.
[Table("StudentLivingWith")]
public class StudentLivingWith
{
public StudentLivingWith()
{
}
[Column(Order = 1)]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int StudentLivingWithID { get; set; }
[Column(Order = 2)]
[ForeignKey("StudentProfileID")]
public Guid StudentProfileID { get; set; }
[Column(Order = 3)]
[ForeignKey("ParentProfileID")]
public Guid ParentProfileID { get; set; }
[JsonIgnore]
[InverseProperty("StudentProfileMap")]
public virtual ICollection<Profile> StudentProfile { get; set; }
[JsonIgnore]
[InverseProperty("ParentProfileMap")]
public virtual ICollection<Profile> ParentProfile { get; set; }
}
So the conclusion is - you just need to add [InverseProperty] tag on the reference, and this simple solution did the trick for me.
I hope this will help. Thanks.

Entity Framework - Multiple Foreign Keys Issue

I have a Project model which has a ProjectLead (one instance of the Person Foreign Key), this works fine. But now I also need to add a collection of People (Project members) referencing the same Person table and I can't get the Entity Framework to generate my database. As soon as I try to add the Fluent API code to create the link table ProjectPerson I get an error - "Schema specified is not valid. Errors: The relationship 'MyApp.WebApi.Models.Person_Projects' was not loaded because the type 'MyApp.WebApi.Models.Person' is not available." I assume this is because of the existing FK relationship already in place with ProjectLead.
Project Model:
public class Project
{
[Key]
public int ProjectId { get; set; }
public string Name { get; set; }
// Foreign Key - Project lead (Person)
public int ProjectLeadId { get; set; }
public virtual Person ProjectLead { get; set; }
// Create many to many relationship with People - Team members on this project
public ICollection<Person> People { get; set; }
public Project()
{
People = new HashSet<Person>();
}
}
Person Model:
public class Person
{
[Key]
public int PersonId { get; set; }
public String Firstname { get; set; }
public String Surname { get; set; }
// Create many to many relationship
public ICollection<Project> Projects { get; set; }
public Person()
{
Projects = new HashSet<Project>();
}
}
DB Context:
public class HerculesWebApiContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Project> Projects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// This works fine
modelBuilder.Entity<Project>()
.HasRequired(c => c.ProjectLead)
.WithMany(d => d.Projects)
.HasForeignKey(c => c.ProjectLeadId)
.WillCascadeOnDelete(false);
// Adding these lines to create the link table `PersonProjects` causes an error
//modelBuilder.Entity<Person>().HasMany(t => t.Projects).WithMany(t => t.People);
//modelBuilder.Entity<Project>().HasMany(t => t.People).WithMany(t => t.Projects);
}
}
I gather that perhaps I need to use the InverseProperty attribute, but I am not sure where this should go in this case?
Can you explicitly define your join table? So, define a ProjectPeople relationship and make the code something like this...
public class ProjectPerson{
[Key]
public int ProjectPersonId { get; set; }
[ForeignKey("Project")]
public int? ProjectId {get;set;}
public virtual Project {get;set;}
[ForeignKey("Person")]
public int? PersonId {get;set;}
public virtual Person {get;set;}
public string RelationshipType {get;set;}
}
Then your other 2 classes will look like this...
public class Project
{
[Key]
public int ProjectId { get; set; }
public string Name { get; set; }
// Foreign Key - Project lead (Person)
public int ProjectLeadId { get; set; }
public virtual Person ProjectLead { get; set; }
// Create many to many relationship with People - Team members on this project
public virtual ICollection<ProjectPerson> ProjectPeople { get; set; }
public Project()
{
ProjectPerson = new HashSet<ProjectPerson>();
}
}
And this..
Public class Person
{
[Key]
public int PersonId { get; set; }
public String Firstname { get; set; }
public String Surname { get; set; }
// Create many to many relationship
public virtual ICollection<ProjectPerson> ProjectPeople { get; set; }
public Person()
{
ProjectPerson = new HashSet<ProjectPerson>();
}
}

Categories

Resources