I am still on my quest to port from a Model First to Code First implementation of EntityFramework. I have made significant progress, with the help of Eranga. I have run into another snag, and I just cant explain what is hapening. I have two Entity objects Topic and Course
A Topic can have one Course that is required
A Course can have 0 or more topics
when i execute the following linq it generates wierd SQL
var topics = from o in db.Topics where o.ParentTopic == null &&
o.Course.Id == c.Id select o;
The SQL generated is
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[ShortDescription] AS [ShortDescription],
[Extent1].[LongDescription] AS [LongDescription],
[Extent1].[Property] AS [Property],
[Extent1].[Difficulty] AS [Difficulty],
[Extent1].[Weight] AS [Weight],
[Extent1].[Course_Id] AS [Course_Id],
[Extent1].[ParentTopic_Id] AS [ParentTopic_Id],
[Extent1].[Course_Id1] AS [Course_Id1]
FROM [dbo].[Topics] AS [Extent1]
WHERE ([Extent1].[ParentTopic_Id] IS NULL) AND ([Extent1].[Course_Id] = #p__linq__0)
Notice that there is an added field called Course_Id1 that is not in my object and not declared as a foreign key. I thought that in OnModelCreating() I had specified the parent child relationship correctly from both sides (I would have thought you only needed to do it from either side), but i cant get EntityFramework not to generate the extra field that obviously does not exist in the database. Remember my database was originally created using a ModelFirst approach.
Can anyone explain where the extra field is comming from????
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Topic
modelBuilder.Entity<Topic>()
.HasRequired(m => m.Course)
.WithMany(m=>m.Topics)
.HasForeignKey(m => m.Course_Id);
modelBuilder.Entity<Topic>()
.HasOptional(m => m.ParentTopic)
.WithMany(m => m.ChildTopics)
.HasForeignKey(m => m.ParentTopic_Id);
//////// lots of code removed for brevity. //////
modelBuilder.Entity<Course>()
.HasMany(m=>m.Topics)
.WithRequired(m => m.Course)
.HasForeignKey(m => m.Course_Id);
}
public partial class Topic
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public string Property { get; set; }
public double? Difficulty { get; set; }
public double? Weight { get; set; }
[JsonIgnore]
public virtual Course Course { get; set; }
public int Course_Id { get; set; }
[JsonIgnore]
public virtual ICollection<Question> Questions { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> ChildTopics { get; set; }
[JsonIgnore]
public virtual Topic ParentTopic { get; set; }
public int? ParentTopic_Id { get; set; }
[JsonIgnore]
public virtual ICollection<RTIQueueEntryData> RTIQueueEntryData { get; set; }
[JsonIgnore]
public virtual ICollection<Intervention> Interventions { get; set; }
[JsonIgnore]
public virtual ICollection<RtiStudentGroup> RtiStudentGroups { get; set; }
}
public partial class Course
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public string Version { get; set; }
public string Year { get; set; }
public string ImportedId { get; set; }
[Required]
public string LocalCourseNumber { get; set; }
[Required]
public string NCESCourseNumber { get; set; }
[Required]
public string StateCourseNumber { get; set; }
public int? Grade { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> PerformanceIndicators { get; set; }
[JsonIgnore]
public virtual Department Department { get; set; }
public int DepartmentId { get; set; }
[JsonIgnore]
public virtual ICollection<StudentGroup> StudentGroups { get; set; }
[JsonIgnore]
public virtual ICollection<CutPointTemplate> CutPointTemplates { get; set; }
[JsonIgnore]
public virtual School School { get; set; }
public int School_Id { get; set; }
[JsonIgnore]
public virtual ICollection<Staff> RTIStaff { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> Topics { get; set; }
}
You have another relationship between Course and Topic created by convention due to this navigation property:
public virtual ICollection<Topic> PerformanceIndicators { get; set; }
EF will put an (invisible, not exposed) end of the relationship into the Topic class. By default the relationship is one-to-many. Hence you get an additional foreign key property in the Topics table (= Course_Id1).
Related
I am getting this error in code first MVC CORE while inserting this and
I am doing Code first for very first Time
command:
Update-Database -Context ProjectDbContext
"Introducing FOREIGN KEY constraint 'FK_Product_SubCategory_SubCategoryId'
on table 'Product' may cause cycles or multiple cascade paths. Specify
ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints".
My Classes are are given below and also explain me why we use association in mvc model classes like this :
public virtual SubCategory SubCategory { get; set; }
Or
public virtual ICollection<Category> Category { get; set; }
public virtual Icollection<SubCategory> SubCategory { get; set; }
public class Product
{
public int ProductId { get; set; }
[Required]
public string ProductName { get; set; }
[Required]
public string ProductModel { get; set; }
[Required]
public int Quatity { get; set; }
[Required]
public int Price { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string ProductColor { get; set; }
public byte Status { get; set; }
public DateTime TodayDate { get; set; }
[Required]
public int CategoryId { get; set; }
[Required]
public int SubCategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual SubCategory SubCategory { get; set; }
}
public class SubCategory
{
public int SubCategoryId { get; set; }
[Required]
public string SubCategoryName { set; get; }
[Required]
public int CategoryId { get; set; }
public ICollection< Category> Category { set; get; }
public ICollection<Product> Product { set; get; }
}
public class Category
{
public int CategoryId { set; get; }
[Required]
public string Name { get; set; }
}
add Following in identity model file under ApplicationDbContext class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
hope this will help but your cascade delete will be off.
I'm sorry for the title but I didn't know a better way to express this in a short description.
Basically what I'm thinking, which I'm not ever sure if its possible or if there is a better way to to it, is the following:
I have a Project model, which belongs to a Company model, and a User model that should be linked to the Project in different ways.
So I was thinking of doing this:
public class Project
{
[Key]
[Required]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime StartDate { get; set; }
[DataType(DataType.DateTime)]
public DateTime? EndDate { get; set; }
public virtual List<User> Clients { get; set; }
public virtual List<User> Employees { get; set; }
public virtual User Manager { get; set; }
public virtual List<User> ProjectLeaders { get; set; }
public virtual Company Company { get; set; }
}
So the project has to have employees, a manager, project leaders and clients, all of those vary by project and they are all users in the system.
I would also like to point out that I am using asp.net's identity membership system
The part that I'm not sure of is if this would even work out.
If it will work, how is it interpreted in the database side?
If not, could you guys point out why is this a bad idea and how it could be done properly?
Thank you very much!
As fer I understand you need a relation between Employee/user and project/Company.If I am not wrong.You can make one to many relation between them.
In a project or in a company can have multiple employees and at a time a employee can work for a company only.
So you can design your entities following way
So your project Entity means that It can have multiple employee
public class Project
{
[Key]
[Required]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime StartDate { get; set; }
[DataType(DataType.DateTime)]
public DateTime? EndDate { get; set; }
public virtual List<User> Employees { get; set; }
}
And here your User Entity means that a user can work for a company at a time.
public class User
{
[Key]
[Required]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
.................................
.................................
//ProjectId will be your foreign key
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
}
Now Entity framework will make a one to many relation between them.
Note : and you can make many to many relation also if you want.It depends on you and your desire functionalities.
The best option to fix the problem was to use FluentAPI to properly connect the models.
Here is how the models should look after fix:
public class Project
{
[Key]
[Required]
public string Id{ get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime StartDate { get; set; }
[DataType(DataType.DateTime)]
public DateTime? EndDate { get; set; }
public virtual ICollection<User> Clients { get; set; }
public virtual ICollection<User> Employees { get; set; }
public virtual ICollection<User> ProjectLeaders { get; set; }
public virtual User ProjectManager { get; set; }
}
public class User : IdentityUser
{
public string Name { get; set; }
public virtual List<Project> ProjectsAsClient { get; set; }
public virtual List<Project> ProjectsAsEmployee { get; set; }
public virtual List<Project> ProjectsAsProjectLeader { get; set; }
public virtual Project ProjectAsManager { get; set; }
}
and on Context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasMany(u => u.ProjectsAsClient)
.WithMany(t => t.Clients)
.Map(x =>
{
x.MapLeftKey("UserId");
x.MapRightKey("ProjectId");
x.ToTable("ProjectClients");
});
modelBuilder.Entity<User>()
.HasMany(u => u.ProjectsAsEmployee)
.WithMany(t => t.Employees)
.Map(x =>
{
x.MapLeftKey("UserId");
x.MapRightKey("ProjectId");
x.ToTable("ProjectEmployees");
});
modelBuilder.Entity<User>()
.HasMany(u => u.ProjectsAsProjectLeader)
.WithMany(t => t.ProjectLeaders)
.Map(x =>
{
x.MapLeftKey("UserId");
x.MapRightKey("ProjectId");
x.ToTable("ProjectLeaders");
});
}
I'm trying to delete a record from database table but I get this error
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.TicketDetail_dbo.Person_PersonID". The conflict occurred in database "AbcDB", table "dbo.TicketDetail", column 'PersonID'
I'm sharing my code please guide me.
TicketDetail:
public class TicketDetail
{
[Key]
public int TicketDetailId { get; set; }
public int? GenericOrderId { get; set; }
public int? PartId { get; set; }
public int? JunkPartId { get; set; }
public int PersonID { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public virtual Part Part { get; set; }
public virtual Ticket Ticket { get; set; }
public virtual Customer Customer { get; set; }
public virtual JunkPart JunkPart { get; set; }
public virtual ICollection<MailSystem> MailSystems { get; set; }
public virtual ICollection<MailLog> MailLogs { get; set; }
}
Person:
public abstract class Person
{
[Key]
public int PersonID { get; set; }
public string City { get; set; }
public DateTimeOffset DateAdded { get; set; }
}
Customer:
public class Customer : Person
{
public int? CountryId { get; set; }
public string Company { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public virtual ICollection<TicketDetail> TicketDetails { get; set; }
public virtual ICollection<MailSystem> MailSystems { get; set; }
public virtual ICollection<MailLog> MailLogs { get; set; }
public virtual Country Country { get; set; }
}
Migration code:
modelBuilder.Entity<Customer>()
.HasMany<TicketDetail>(c => c.TicketDetails)
.WithRequired(x => x.Customer)
.WillCascadeOnDelete(true);
The error happens when I try to delete any customerId.
I have searched internet but couldn't find any appropriate solution, please help me with this error.
i have a little issue.
I am trying to make a Address class were i save all my application's addresses.
The thing is that i want to be able to link several addresses to both customer and company.
Can someone please show me how i should design it?
I use MVC 4 with entityFramework code first.
public class Address
{
[Key]
public int AddressId { get; set; }
public string Street { get; set; }
public string Number { get; set; }
public string ZipCode { get; set; }
public int CountyId { get; set; }
public virtual County County { get; set; }
public int StateId { get; set; }
public virtual State State { get; set; }
public int CountryId { get; set; }
public virtual Country Country { get; set; }
}
public class Customer
{
[Key]
public int CustomerId { get; set; }
public int CompanyId { get; set; }
[Display(Name = "Kund")]
public string Name { get; set; }
public virtual Company Company { get; set; }
// wan't to display a ICollection of addresses.
//public virtual ICollection<Address> Addresses { get; set; }
}
public class Company
{
[Key]
public int CompanyId { get; set; }
[Display(Name = "Organisationsnummer")]
public string OrganisationNumber { get; set; }
[Display(Name = "Företag")]
public string Name { get; set; }
[Display(Name = "Företag skapat")]
public DateTime CreationDate { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
// wan't to display a ICollection of addresses.
//public virtual ICollection<Address> Addresses { get; set; }
}
At the following properties and annotations to your classes, it should help Entity Framework understand your relationships:
public class Address
{
[Key]
public int AddressId { get; set; }
public string Street { get; set; }
public string Number { get; set; }
public string ZipCode { get; set; }
public int CustomerId {get;set;}
[ForeignKey("CustomerId")]
public virtual Customer Customer {get;set;}
public int CompanyId {get;set;}
[ForeignKey("CompanyId")]
public virtual Company Company {get;set;}
public int CountyId { get; set; }
[ForeignKey("CountyId")]
public virtual County County { get; set; }
public int StateId { get; set; }
[ForeignKey("StateId")]
public virtual State State { get; set; }
public int CountryId { get; set; }
[ForeignKey("CountryId")]
public virtual Country Country { get; set; }
}
public class Customer
{
[Key]
public int CustomerId { get; set; }
public int CompanyId { get; set; }
[Display(Name = "Kund")]
public string Name { get; set; }
[ForeignKey("CompanyId")]
public virtual Company Company { get; set; }
[InverseProperty("Customer")]
public virtual ICollection<Address> Addresses { get; set; }
}
public class Company
{
[Key]
public int CompanyId { get; set; }
[Display(Name = "Organisationsnummer")]
public string OrganisationNumber { get; set; }
[Display(Name = "Företag")]
public string Name { get; set; }
[Display(Name = "Företag skapat")]
public DateTime CreationDate { get; set; }
[InverseProperty("Company")]
public virtual ICollection<Customer> Customers { get; set; }
[InverseProperty("Company")]
public virtual ICollection<Address> Addresses { get; set; }
[InverseProperty("Company")]
public virtual ICollection<Employee> Employees { get; set; }
}
public class Employee
{
[Key]
public int EmployeeId {get;set;}
public int CompanyId {get;set;}
[ForeignKey("CompanyId")]
public virtual Company Company {get;set;}
}
EDIT: You now have issue of another type. Your DELETE/UPDATE rules are causing the error you're seeing right now. You've most likely set CASCADE delete on multiple paths that lead to same primary key table. For start set all your relationships that look like this:
Then assume this scenario:
You have entities A, B and C.
Entity B has FK to entity A.
Entity C has FK to entity A and entity B.
You're allowed to set cascade delete/update only on one dependency path. This means that you can only do:
Cascade delete between A and B and cascade delete between B and C.
If you however add cascade delete between A and C as well along with above, you'll get an error you have now.
Today I've been working with MVC for the first time. Also normally I use the EF with model first, but I wanted to try POCO.
So I've made my 3 entities and when I try to make a controller I get an error:
Unable to retrieve metadata for "BookExchange.Models.Exchange". Unable to determine the principal end of an association between the types "BookExchange.Models.Exchange" and "BookExchange.Models.Book". The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
My 3 classes:
public class Book
{
public int BookID { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string ISBN10 { get; set; }
public int PersonID { get; set; }
public virtual Person Person { get; set; }
public virtual Exchange Exchange { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
public virtual ICollection<Exchange> Exchanges { get; set; }
}
public class Exchange
{
[Key]
public int BookID { get; set; }
public int PersonID { get; set; }
public DateTime ReturnDate { get; set; }
public virtual Person Person { get; set; }
public virtual Book Book { get; set; }
}
Does anyone know what I'm doing wrong? I don't want to lose the association properties.
Thanks in advance!
Try adding foreign key properties for your references. E.g.
public class Book
{
public int BookID { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string ISBN10 { get; set; }
public virtual Person Person { get; set; }
public int PersonID { get; set; }
public virtual Exchange Exchange { get; set; }
public int ExchangeID { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
public virtual ICollection<Exchange> Exchanges { get; set; }
}
public class Exchange
{
public int ExchangeID { get; set; }
public int BookID { get; set; }
public virtual Book Book { get; set; }
public int PersonID { get; set; }
public virtual Person Person { get; set; }
public DateTime ReturnDate { get; set; }
}
Also, take a look at ScottGu's post on code first and this EF post on conventions.
Try this: (Remove database, so EF will create new)
public class Book
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string ISBN { get; set; }
public virtual Person Person { get; set; }
public virtual Exchange Exchange { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
public virtual ICollection<Exchange> Exchanges { get; set; }
}
public class Exchange
{
public int Id { get; set; }
public DateTime ReturnDate { get; set; }
public virtual Person Person { get; set; }
public virtual Book Book { get; set; }
}
It's your one on one associations.
Remove one reference between exchange or book, so Code-first can decide which one is more important in your one on one relation (Book <--> Exchange)
If you want to know why, you should read this: