EF Code First Error - entity-framework

I am trying to create some tables using Code First. Here is my code:
public class Country
{
[Key]
public int Id { get; set; }
public string CountryName { get; set; }
}
public class State
{
[Key]
public int Id { get; set; }
public string StateName { get; set; }
public int CountryId { get; set; }
public Country Country { get; set; }
}
public class Customer
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int CountryId { get; set; }
public int StateId { get; set; }
public virtual Country Country { get; set; }
public virtual State State { get; set; }
}
public class ProductContext : DbContext
{
public DbSet<Country> Country { get; set; }
public DbSet<Customer> Customer { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
When I Execute this code the following error occurs:
Introducing FOREIGN KEY constraint
'FK_dbo.State_dbo.Country_CountryId' on table 'State' may cause cycles
or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO
ACTION, or modify other FOREIGN KEY constraints. Could not create
constraint. See previous errors.
But I want the CountryId in State Table to be a foreign key. What am I missing here? Can anybody guide me the correct way of achieving this?

Entity Framework is worried about deletion here- because the User has a direct relationship to a Country and also a State and the State also relates to a Country you effectively have a potential loop of User -> State -> Country -> User which would mean that if cascade deletions were enabled the moment you deleted one user you would potentially delete everything in your database.
The answer is in the error message- by disabling cascade deletions across some of these relationships ( which is logical - deleting a user doesn't mean you want to delete their state and country ) you will avoid this risk. As you might imagine this has come up on SO before.
As an aside, having the Country on the User and also on the State looks like questionable denormalisation - there may be a good reason for it, but that happens less often than you would expect.

Related

I am not able to have one-to-many relationship in Entity Framework

I am following examples from the internet but it's not working. The database is getting created successfully, there is no error.
What I want to have is: one user can have multiple transactions, and a transaction can have references to two users. One of those is the user who did the transaction, the second is the user to whom transaction is done.
But what is happening is I am getting three foreign keys in the Users table, but none in the Transactions table.
See image below:
My classes
public class User
{
public int userId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string CardNumber { get; set; }
public string Password { get; set; }
public int Balance { get; set; }
public string UserType { get; set; }
public string ProfileUrl { get; set; }
public IList<Transaction> Transactions { get; set; }
}
public class Transaction
{
public Transaction()
{
this.TranscationDateTime = DateTime.UtcNow;
}
public int TransactionId { get; set; }
public int Amount { get; set; }
public User FromUser { get; set; }
public User ToUser { get; set; }
public DateTime TranscationDateTime { get; set; }
}
public class DB: DbContext
{
public DB() : base("name=DBConnection")
{ }
public DbSet<User> Users { get; set; }
public DbSet<Transaction> Transactions { get; set; }
}
You need to make some modification to your code.
First of all, each navigation property needs to be marked as virtual, in order to allow Entity Framework to lazy loading, unless you want always eager load all your navigations (could be a choice, is up to you).
After that, each of your user has outgoing and incoming transactions, so for the User class:
public class User
{
public int userId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string CardNumber { get; set; }
public string Password { get; set; }
public int Balance { get; set; }
public string UserType { get; set; }
public string ProfileUrl { get; set; }
public virtual IList<Transaction> IncomingTransactions { get; set; }
public virtual IList<Transaction> OutgoingTransactions { get; set; }
}
Let's make virtual navigation properties of Transaction class
public class Transaction
{
public Transaction()
{
this.TranscationDateTime = DateTime.UtcNow;
}
public int TransactionId { get; set; }
public int Amount { get; set; }
public virtual User FromUser { get; set; }
public virtual User ToUser { get; set; }
public DateTime TranscationDateTime { get; set; }
}
Last, but not least, let's inform your DbContext of how things are supposed to go:
public class MyContext : DbContext
{
public MyContext(string connectionString) : base(connectionString) { }
public DbSet<User> Users { get; set; }
public DbSet<Transaction> Transactions { get; set; }
protected override void OnModelCreating(DbModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Transaction>()
.HasRequired<User>(t => t.FromUser)
.WithMany(u => u.OutgoingTransactions).WillCascadeOnDelete(false);
builder.Entity<Transaction>()
.HasRequired<User>(t => t.ToUser)
.WithMany(u => u.IncomingTransactions).WillCascadeOnDelete(false);
}
}
This should be enough for EF autodiscovery to make the right assumptions and create right database structure, that would be two FKs in Transaction table each of them to the primary key of Users table.
And voilĂ :
This happens because EF doesn't know that one of the FromUser and ToUser fields is supposed to match the collection Transactions - since you are not following the naming conventions. You have several options on how to resolve this situation:
If you only want to match Transactions collection with either FromUser or ToUser but not both, you can use [ForeignKey] and/or [InverseProperty] attributes to setup the database relation explicitly
If you want to use BOTH of them, then you would need to specify two collections in the User class - e.g. TransactionsFromUser and TransactionsToUser. You might still need to setup the relationships explicitly through the attributes though
What i want to have is one user can have multiple transaction but a transaction can have reference to two user.
Your current database model reflects this accuratly. I will explain why in the rest of my answer.
The User table can not hold the foreign keys to the Transactions table because one User can be associated with multiple Transactions. If the FK column was on the User table, it would need to hold more than one TransactionId.
Instead, the references to the Users are stored in the Transaction table. So every Transaction only has to store a single UserId per FK column.
Transaction.User_userId tells us that this Transaction is in the IList<Transaction> Transactions of the User with the stored User_userId.
To get this list of Transactions for a certain user, we query
SELECT *
FROM Transactions t
INNER JOIN Users u on t.User_userId = u.userId
WHERE u.userId = {theUserId}
The additional FKs ToUser_userId and FromUser_userId exists because they might reference different Users.
If the semantics of the IList<Transaction> Transactions is actually "all transactions that originated from this User", you could configure the ModelBuilder to use the FromUser_userId FK for this collection instead of creating the third FK User_userId. See the answer of Sergey.

EF CodeFirst - cannot create database

I am getting error while trying to run my MVC application
Introducing FOREIGN KEY constraint 'FK_dbo.Passages_dbo.Localizations_ToID' on table 'Passages' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors'
I`ve seen many posts but I couldn't get what should I do now.
There are my models:
public class Passage
{
[Key]
public int ID { get; set; }
public int FromID { get; set; }
[ForeignKey("FromID")]
public Localization FromLocalizaton { get; set; }
public int ToID { get; set; }
[ForeignKey("ToID")]
public Localization ToLocalization { get; set; }
public DateTime DepartureTime { get; set; }
public DateTime ArrivalTime { get; set; }
public DateTime? AdditionalTime { get; set; }
public bool Weekend { get; set; }
public int Seats { get; set; }
}
public class Localization
{
[Key]
public int ID { get; set; }
public string Province { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string StreetAdres { get; set; }
}
Passage has two foreign key refers to Lozalization with one to one relationship
The issue came from this:
Passage has two foreign key refers to Lozalization with one to one relationship
Because by default those two relationships are required in Passage (look at foreign key FromID and ToID there are not Nullable<int> or int?) hence Code First create cascade delete action on those relations. However two cascade deletions will be applied on the same table which is not allowed.
To correct this issue, you have two solutions:
Make one of the foreign key property Nullable<int> which by default not create a cascade delete action on that relationship.
Or you can disable cascade delete action by using Fluent API like this :
// Assuming that you want to disable cascade deletion with ToLocalization
modelBuilder.Entity<Passage>()
.HasRequired(p => p.ToLocalization)
.WithMany()
.WillCascadeOnDelete(false);

code first one-to-one enable cascade delete

I have one to one relationship with foreign keys but the Cascade Delete is not enabled for some reason. The sample code is below.
public class AppRegistration
{
public int AppRegistrationId { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Username")]
public string UserName { get; set; }
[Required]
[StringLength(100)]
public string Password { get; set; }
[StringLength(20)]
public string StudentOrAgent { get; set; }
// navigation properties
public virtual AppStatus AppStatus { get; set; }
public virtual Agreement Agreement { get; set; }
public virtual AnotherTable AnotherTable { get; set; }
}
The dependent table with a foreign key is below.
public class Agreement
{
[Key]
[ForeignKey("AppRegistration")]
public int AppRegistrationId { get; set; }
public DateTime DateAgreed { get; set; }
public virtual AppRegistration AppRegistration { get; set; }
}
When I try to delete an entry from the generated AppRegistrations table I get a Reference constraint conflict.
I tried putting [Required] on the navigation property in the dependent table but it doesn't do anything - the Update-Database command shows the No pending code-based migrations. message. Any ideas? Thanks.
Update:
I'm getting the following error message:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.AppStatus_dbo.AppRegistrations_AppRegistrationId". The conflict occurred in database "MVCapp", table "dbo.AppStatus", column 'AppRegistrationId'.
I decided to work out the cascade delete problem in a separate sample project. I found the following blog & MSDN pages very useful.
http://blog.bennymichielsen.be/2011/06/02/entity-framework-4-1-one-to-one-mapping/
http://msdn.microsoft.com/en-us/library/gg671256%28v=VS.103%29.aspx
http://msdn.microsoft.com/en-us/library/gg671273%28v=VS.103%29.aspx
Using the Code First approach create the following Model.
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public virtual Book Book { get; set; }
}
public class Book
{
public int CategoryId { get; set; }
public string BookTitle { get; set; }
public string BookAuthor { get; set; }
public string BookISBN { get; set; }
public virtual Category Category { get; set; }
}
(I realize the entity names suggest one-to-many relationship, but I am trying to model 1-to-1 relationship, as in my original question at the top.)
So, in the above model each Category can only have one Book.
In your DbContext-derived class add the following.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Book>()
.HasKey(t => t.CategoryId);
modelBuilder.Entity<Category>()
.HasRequired(t => t.Book)
.WithRequiredPrincipal(t => t.Category)
.WillCascadeOnDelete(true);
}
(The following namespaces are required for the above code: System.Data.Entity, System.Data.Entity.ModelConfiguration.Conventions.)
This properly creates the 1-to-1 relationship. You'll have a primary key in each table and also a foreign key in Book table with ON DELETE CASCADE enabled.
In the above code, on the Category entity I used WithRequiredPrincipal() with t => t.Category argument, where the argument is the foreign key column in the dependent table.
If you use WithRequiredPrincipal() without an argument you'll get an extra column in the Book table and you'll have two foreign keys in the Book table pointing to CategoryId in Category table.
I hope this info helps.
UPDATE
Later on I found answer directly here:
http://msdn.microsoft.com/en-us/data/jj591620#RequiredToRequired
A reason why you're not getting cascading delete is because your relationship is optional.
If you want the relationship required i.e. an AppRegistration has to have one Agreement you can use (cascading delete configured automatically):
public class Agreement
{
...
[Required]
public AppRegistration AppRegistration{ get; set; }
}
If you want the relationship to be optional with cascading delete you can configure this using Fluent API:
modelBuilder.Entity<AppRegistration>()
.HasOptional(a => a.Agreement)
.WithOptionalDependent()
.WillCascadeOnDelete(true);

How can I update a Code First application to accommodate manual schema changes?

I have an app that was created using EF. The problem is that I noticed some extraneous foreign keys columns created in one of the tables. Dropping these columns causes an [SqlException (0x80131904): Invalid column name 'Material_Id' error.
Here is a simplified version of the class structure...
public class Hazard
{
public int Id { get; set; }
public string Name { get; set; }
}
public abstract class HazardAnalysis
{
public int Id { get; set; }
public int HazardId { get; set; }
public virtual Hazard Hazard { get; set; }
}
public class ProductHazard : HazardAnalysis
{
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
The table that was generated looked like this...
dbo.Hazards
Id int
Name string
Product_Id int
Since the relationship between ProductHazards and Hazards is 1:many, the Product_Id field should not be there. Dropping this column generates the Invalid column name 'Product_Id' error.
I've scoured the model for hours and can't find any valid reason for this column to exist.
Is there any way to update the model after manually dropping a column? I obviously don't want to drop and recreate the database.
I've also noticed that the productId of the current product is inserted in the dbo.Hazards Product_Id table whenever a new ProductHazard is created. Since there is a many-to-one relationship between ProductHazards and Hazards, when a new ProductHazard is created, the Product_Id field is updated with the ProductId of the new ProductHazard, which seems bizarre.
Any advice would be greatly appreciated.
Here is the DbSet code:
public DbSet<Hazard> Hazards { get; set; }
public DbSet<HazardAnalysis> HazardAnalyses { get; set; }
and also...
modelBuilder.Entity<HazardAnalysis>()
.HasRequired(e => e.Hazard)
.WithMany()
.HasForeignKey(e => e.HazardId)
.WillCascadeOnDelete(false);
You need to define the many part of the relationship. In this case, you need to add a collection property to your Hazard object, like below:
public class Hazard
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HazardAnalysis> HazardAnalyses { get; set; }
}

code first relationships with multiple foreign keys

I have a scenario I'm getting a little muddled with using EF code first. The classes I've created are below:
public class Company
{
public int Id { get; set; }
public List<Contact> Contacts { get; set; }
public List<Job> Jobs { get; set; }
}
public class Contact
{
public int Id { get; set; }
[ForeignKey("CompanyId")]
public virtual Company Company { get; set; }
public int CompanyId { get; set; }
public List<Job> Jobs { get; set; }
}
public class Job
{
public int Id { get; set; }
[ForeignKey("CompanyContactId")]
public virtual CompanyContact CompanyContact { get; set; }
public int CompanyContactId { get; set; }
[ForeignKey("CompanyId")]
public virtual Company Company { get; set; }
public int CompanyId { get; set; }
}
However, when I build the DB I get the following error:
Introducing FOREIGN KEY constraint 'FK_Contacts_Company_CompanyId' on table 'Contacts' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
So a little research indicates the answer to this is to use the Fluent API to define the mappings as required but I can't get my head around how to do this or find an example of a similar scenario.
I realise I could remove the Company class from Job and navigate through Contact but I'd prefer not to if possible.
Any help gratefully received
You want to use the EF model builder to set up these relationships.
An example of how you would do this for one of your properties would be the following:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>().HasOptional(e => e.Company).WithMany(c=>c.Contacts);
}
For more of an explanation around how to use the modelbuilder take a look at my article on EF Navigation Properties