The application I'm working on generates an error while creating an instance of a model. I have Product and Color (many to many) and ProductImage (many ProductImage to a ProductColor).
public partial class ProductColor
{
public ProductColor()
{
this.ProductImages = new HashSet<ProductImage>();
}
public int Id { get; set; }
[DefaultValue(0),DisplayName("Price Offset")]
public Decimal PriceOffset { get; set; }
public int ProductId { get; set; }
public int ColorId { get; set; }
public virtual Color Color { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<ProductImage> ProductImages { get; set; }
}
public partial class ProductImage
{
public int Id { get; set; }
[DisplayName("File name"),
Required(),
StringLength(255)]
public string FileName { get; set; }
public bool Default { get; set; }
public int ProductColor_Id { get; set; }
public virtual ProductColor ProductColor { get; set; }
}
public class testContext : DbContext
{
public testContext() : base("name=testContext")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Manufacturer> Manufacturers { get; set; }
public DbSet<ProductColor> ProductColors { get; set; }
public DbSet<Color> Colors { get; set; }
public DbSet<ProductImage> ProductImages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.HasMany(c => c.ProductColors);
}
}
After scaffolding the controller and views for ProductImage and going to the index of ProductImage I get an error trying to get ProductImages from the db context.
No wonder because Entity decided the following sql should be used to get the instances:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[FileName] AS [FileName],
[Extent1].[Default] AS [Default],
[Extent1].[ProductColor_Id] AS [ProductColor_Id],
[Extent1].[ProductColor_Id1] AS [ProductColor_Id1]
FROM [dbo].[ProductImages] AS [Extent1]
ProductColor_Id1 does not exist in the database. Here is the sql that created the tables:
CREATE TABLE [dbo].[ProductColors] (
[Id] int IDENTITY(1,1) NOT NULL,
[PriceOffset] decimal(7,2) NOT NULL,
[ProductId] int NOT NULL,
[ColorId] int NOT NULL
);
CREATE TABLE [dbo].[ProductImages] (
[Id] int IDENTITY(1,1) NOT NULL,
[FileName] nvarchar(255) NOT NULL,
[Default] bit NOT NULL,
[ProductColor_Id] int NOT NULL
);
ALTER TABLE [dbo].[ProductColors]
ADD CONSTRAINT [PK_ProductColors]
PRIMARY KEY CLUSTERED ([Id] ASC);
ALTER TABLE [dbo].[ProductColors]
ADD CONSTRAINT [FK_ProductColorColor]
FOREIGN KEY ([ColorId])
REFERENCES [dbo].[Colors]
([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX [IX_FK_ProductColorColor]
ON [dbo].[ProductColors]
([ColorId]);
ALTER TABLE [dbo].[ProductColors]
ADD CONSTRAINT [FK_ProductColorProduct]
FOREIGN KEY ([ProductId])
REFERENCES [dbo].[Products]
([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX [IX_FK_ProductColorProduct]
ON [dbo].[ProductColors]
([ProductId]);
ALTER TABLE [dbo].[ProductImages]
ADD CONSTRAINT [FK_ProductColorProductImage]
FOREIGN KEY ([ProductColor_Id])
REFERENCES [dbo].[ProductColors]
([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE INDEX [IX_FK_ProductColorProductImage]
ON [dbo].[ProductImages]
([ProductColor_Id]);
The database is generated from an Entity diagram and looks fine to me. I have no idea why on a ProductImage create it added ProductColor_Id1 in the select statement.
Hope there is enough information provided and that this is a general mistake that's easily solved. Thank you for reading this and hope you can help.
I would like the scaffolded controller and views to work in listing, creating, editing and deleting the ProdcutImage objects but as it is it's not even possible to create one with the information provided to Entity.
After deleting and re creating the association in the Entity diagram I ended up with a ProductImage having ProductColorId instead of ProductColor_Id.
Not too sure what I did differently (maybe checked the "Add foreign key properties to ProductImage entity" in the diagram.
Re created database from diagram and re created model classes from database (to be used in another project). The ProductImage now looks like this:
public partial class ProductImage
{
public int Id { get; set; }
[DisplayName("File name"),
Required(),
StringLength(255)]
public string FileName { get; set; }
public bool Default { get; set; }
public int ProductColorId { get; set; }
public virtual ProductColor ProductColor { get; set; }
}
As far as I can see the only difference is ProductColerId, must be a convention in Entity to specify relations this way.
A tip when working in the diagram:
In a one to many first click the one side (in my case one ProductColor has many ProductImage so I right click on ProductColor). Then when choosing add new => association it automatically sets ProductColor (the clicked item) to one. Now setting the many relation on the right side will automatically check the "add foreign key" checkbox.
If I were to click on the many side first (right click on ProductImage). Then change the left side dropdown to many and right side select ProductColor with one for multiplicity then the "add foreign key" check box needs to be checked manually.
Related
I have folowwing scenario in ASP.net MVC - code first
[Table("api")]
public class Api
{
[Column("Id")]
public int Id { get; set; }
[Column("api_key")]
public string ApiKey { get; set; }
public virtual BookingPluginConfig BookingPluginConfig { get; set; }
}
public class BookingPluginConfig
{
[Key,ForeignKey("Api")]
public int ApiId { get; set; }
public virtual Api Api { get; set; }
}
Currently "Api" table to "BookingPluginConfig" have one to one relationship - I want to change it to one to many
'BookingPluginConfig' table has
[Key,ForeignKey("Api")]
public int ApiId { get; set; }
I want to remove this column which is the primary key, and I want to add new column as the primary key (auto increment)
Currently this 'ApiId ' is primary key as well foreign key. I want to remain ''ApiId '' is foreign key but not as primary key
I have already many data in existing table
How can I do this?
Your modified BookingPluginConfig class:
public class BookingPluginConfig
{
[Key(), Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey("Api")]
public int ApiId { get; set; }
public virtual Api Api { get; set; }
}
Navigation property, which is the only change, in the API class:
//one to many relation to BookingPluginConfig
public virtual ICollection<BookingPluginConfig> BookingPluginConfigs { get; set; }
That's all you need to add a new primary key and define relation. Take back up of database if you want, add migration and update database.
I am new to MVC and this is my function. There are three tables (Order, OrderNotes, Notes), ID is their primary key. One Order can have many Notes, the table OrderNotes has foreign key OrderID(from Booking table) and NotesID(from Notes table). I want to have a Order Edit page to display individual Order (FirstName, LastName), also display a list of its Notes. Here is my DB structure:
Booking table:
{ID,
FirstName,
LastName
}
BookingNotes table:
{ID,
BookingID,
NotesID
}
Notes table:
{ID,
NoteName,
StatusID
}
So how can I implement the list of Notes since it's from multiple tables? It will be able to Create New Note, Delete existing Note in the list row record, not Edit. Linq used in DB query. Thanks.
It would be a better idea to have only 2 tables:
public class Book
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// Navigational properties
public virtual List<Note> Notes { get; set; }
}
public class Note
{
public int ID { get; set; }
public int BookID { get; set; }
public string NoteName { get; set; }
public int StatusID { get; set; }
// Navigational properties
public virtual Book Book { get; set; }
public virtual Status Status { get; set; }
}
A third table is useful when you want to reuse the same Note for a different booking. However i think this is not the case.
So to retrieve data for your context make sure you have the DbSet<Book>
public class ApplicationDbContext : DbContext
{
public virtual DbSet<Book> Bookings { get; set; }
}
In your controller (or better in a repository class):
var BookingID = 10; // this is parameter passed to the function
var myBooking = this.dbContext.Bookings
.Include(p => p.Notes)
.ThenInclude(p => p.Status)
.FirstOrDefault(p => p.ID == BookingID);
Map the retrieved booking to a ViewModel, pass it to the View and you're good to go.
I have two models, One ApplicationUser which holds all users in the system and I have a Quotation model which will hold all Quotations made. now I want to store two mappings to ApplicationUser inside Quotations. So that I can map to created User as well as cancelled User. My model looks like this
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int ApplicationUserID { get; set; }
public virtual ApplicationUser CreatedUser { get; set; }
[ForeignKey("ApplicationUserID")]
public ApplicationUser CancelledUser { get; set; }
}
But this throws an error
Quotation_CancelledUser_Target_Quotation_CancelledUser_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'ApplicationUserID' on entity 'Quotation' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'Quotation_CancelledUser'.
So I guess , The approach I am taking is wrong. Can anyone point out the correct way to achieve this?
The problem you are observing is called "Multiple Cascade Path". A Multiple Cascade Path happens when a cascade path goes from column col1 in table A to table B and also from column col2 in table A to table B.
The exception is caused by SQL Server when code first attempted to add table that has columns appearing more than once of another table.
In SQL Server, a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement. For example, the tree of cascading referential actions must only have one path to a particular table on the cascading referential actions tree.
You will need to use FluentAPI to configure the relationship. I am using EF5 currently and do not know if this can be accomplished in EF6/7.
So modifying your code sample, it would look like:
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int CreatedUserID { get; set; }
// Navigation property
public virtual ApplicationUser CreatedUser { get; set; }
public int CancelledUserID { get; set; }
// Navigation property
public virtual ApplicationUser CancelledUser { get; set; }
}
// Created a simple class for example
public class ApplicationUser
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
Now in you context class you can write:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Disable the default PluralizingTableNameConvention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// Add configuration here
modelBuilder.Entity<Quotation>()
.HasKey(e => e.QuotationID);
modelBuilder.Entity<ApplicationUser>()
.HasKey(e => e.Id);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CreatedUser)
.WithMany()
.HasForeignKey(u => u.CreatedUserID);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CancelledUser)
.WithMany()
.HasForeignKey(u => u.CancelledUserID);
}
For more information with example refer this link.
I am trying to develop an ASP.NET MVC 4 application where players can be rated according to their Offence, Defence and Assist skills. Offence, Defence and Assist are foreign keys on Player table referencing the same lookup table - Rating.
I have the following parent entity:
public class Rating
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Player> Players { get; set; }
}
And child entity:
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
public int OffenceRatingId { get; set; }
[ForeignKey("OffenceRatingId")]
public virtual Rating OffenceRating { get; set; }
public int DefenceRatingId { get; set; }
[ForeignKey("DefenceRatingId")]
public virtual Rating DefenceRating { get; set; }
public int AssistRatingId { get; set; }
[ForeignKey("AssistRatingId")]
public virtual Rating AssistRating { get; set; }
}
Building and scaffolding went fine but when I run the app, I get the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.Players_dbo.Ratings_DefenceRatingId' on table 'Players' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I am new to MVC and have no idea what I am missing here. Any help with this will be greatly appreciated. Thanks.
By default, Entity Framework has a Cascade on Delete convention. When two entities have foreign keys to each other, it causes a circular reference and Cascade on Delete can't be applied to both entities.
The simplest solution is to remove the cascade on delete convention, and apply it on a case by case basis.
I´am just in the beginning of creating a comment system for my website. I´am using EF and I want to bind a few of my tables to the Comments table. We can say that I have a Car entity and a Bike entity in two separate tables, and I would like to bind a collection of comments of these two tables.
In my mind I have a picture that the comments table would contain:
CommentID | EntityID | CommentText
1 Bike_2 Hello world..
2 Car_2 --
3 Bike_3 --
Am I thinking right? How do a setup this with entity framework?
Best regards.
(The following is for Entity Framework 4.1 to 4.3.1 and Code-First/DbContext.)
The type of mapping which comes closest to your idea is Table-per-Type (TPT) inheritance mapping. It would look like this:
public abstract class EntityWithComments
{
public int Id { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string CommentText { get; set; }
public int EntityId { get; set; }
public EntityWithComments Entity { get; set; }
}
public class Car : EntityWithComments
{
public string Manufacturer { get; set; }
public string Color { get; set; }
}
public class Bicycle : EntityWithComments
{
public int Weight { get; set; }
public bool HasThreeWheels { get; set; }
}
EntityWithComments is a base class for Car and Bicycle and perhaps other entities. Then you have a derived DbContext class:
public class MyContext : DbContext
{
public DbSet<EntityWithComments> EntitiesWithComments { get; set; }
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.ToTable("Cars");
modelBuilder.Entity<Bicycle>()
.ToTable("Bicycles");
}
}
As a result you have four tables in the database:
A Comments table which looks like your proposal but EntityId won't refer directly to the Cars and Bicycles tables. Instead it refers to the base type table EntitiesWithComments.
A table EntitiesWithComments representing the abstract base class and which only has a single column, namely the Id column.
A table Cars with a one-to-one shared primary key constraint between the Id and the Id in table EntitiesWithComments
A table Bicycles with a one-to-one shared primary key constraint between the Id and the Id in table EntitiesWithComments
You can then - for example - load all blue cars:
using (var ctx = new MyContext())
{
var blueCars = ctx.EntitiesWithComments.OfType<Car>()
.Where(c => c.Color == "Blue")
.ToList();
}
Because the EntitiesWithComments base table does not contain any column except the Id there is no join between the tables necessary. The generated SQL looks like this and only touches the table for the derived type:
SELECT
'0X0X' AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Manufacturer] AS [Manufacturer],
[Extent1].[Color] AS [Color]
FROM [dbo].[Cars] AS [Extent1]
WHERE N'Blue' = [Extent1].[Color]
(I guess, the strange 0X0X value in this query is kind of a type descriptor EF uses to check if the returned rows are really cars, but I am not sure.)
If you want to load all bicycles with three wheels including their comments the following query works:
using (var ctx = new MyContext())
{
var bicyclesWithThreeWheelsWithComments = ctx.EntitiesWithComments
.Include(e => e.Comments)
.OfType<Bicycle>()
.Where(b => b.HasThreeWheels)
.ToList();
}