How to make a relationship be required in EF code first - entity-framework-4.1

I have a EF Code First One-To-Many relationship. It's essentially a Parent/Child relations as the child can't exist without the parent.
public class Parent
{
[Key]
public Guid Id { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual ICollection<OtherChild> OtherChildren { get; set; }
}
public class Child
{
[Key]
public Guid Id { get; set; }
public virtual Parent Parent { get; set; }
}
So I wasn't sure how I could have the child be required to have a Parent so I tried putting a [Required] attribute on it. That gave me the error:
- InnerException {"Introducing FOREIGN KEY constraint 'Child_Parent' on table 'Child'
may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE
NO ACTION, or modify other FOREIGN KEY constraints.\r\nCould not create constraint.
See previous errors."}
System.Exception {System.Data.SqlClient.SqlException}
Ok I'm not sure how he could have multiple cascading parts.
The parent also has other child objects and those child objects share a many-to-many relationship with the original child object but it shouldn't require a cascade delete.
I guess I'm doing this wrong but what is the proper way to do this.
PS. When I have a child require a parent should I make the foreign key a part of the primary key?

You can disable cascading delete for the relationship in Fluent API (it's not possible with data annotations):
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired(c => c.Parent)
.WillCascadeOnDelete(false);
}
}
You must delete the children then as well in your code if you delete a parent.
You don't need to make the foreign key part of the primary key and I don't see a benefit in doing so. Your Guid key is already unique. It will help query performance though if you create an index on the foreign key column in the database.

Related

Sorting navigation properties in Entity Framework

Consider the following database tables:
CREATE TABLE [dbo].[Step]
(
[StepId] INT NOT NULL PRIMARY KEY IDENTITY,
[OrderIndex] INT NOT NULL
)
CREATE TABLE [dbo].[StepInput]
(
[StepInputId] INT NOT NULL PRIMARY KEY IDENTITY,
[StepId] INT NOT NULL,
[OrderIndex] INT NOT NULL,
CONSTRAINT [FK_StepInput_Step] FOREIGN KEY ([StepId]) REFERENCES [Step]([StepId]) ON DELETE CASCADE,
)
With the following POCOs:
public class Step
{
public virtual int StepId { get; set; }
public virtual ICollection<StepInput> StepInputs { get; set; }
}
public class StepInput
{
public int StepInputId { get; set; }
public int StepId { get; set; }
public virtual int OrderIndex { get; set; }
}
I would like for StepInputs to be ordered by OrderIndex. Is there any way to setup the navigation property so that it is sorted automatically by EF, so that the user of Step does not have to call StepInputs.OrderBy every time?
I am using the database-first model.
Edit: I did realize that I could just add an OrderedInputs property to Step, which returns StepInputs.OrderBy(...), which solved the immediate problem, though I'm not sure about the performance implications. I am still curious as to whether there is a way to set this up in EF without having to use a custom property.
No way...! I think you have two tricks:
1) Create a static dbcontext, and for the first time, load the data you need into memory using .load() and perform your ordering, and then just use in-memory data in the Local property of your dbctx.<DbSet>s - an ugly way and most probably doesn't satisfy you...
2) Create a stored procedure to retrieve your data and perform ordering in database, and map that sp to an entity in your model.

Entity Framework Auto Migrations Primary Key

I have inherited a sql server database and an ASP.Net MVC 4 web application which is using Entity Framework 5.0 Code First with Auto Migrations . However, it appears the previous developer forgot to add a Primary Key to one of the tables. I am now trying to do this using Auto Migrations, however, it is not working, no errors either, just seems to be ignoring the command.
The table is like this
public int CourseDateHistoryID { get; set; }
public int CourseDateID { get; set; }
public int Event { get; set; }
//public string testProp{ get; set; }
And my mapping is like this to try and create the primary key on CourseDateHistoryID
this.HasKey(t => t.CourseDateHistoryID);
this.Property(t => t.CourseDateHistoryID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
I thought maybe the connection string was wrong or something, so I tried to add a dumby string property called testProp using auto migrations, but this worked fine.
Would anyone have any ideas as to why I cannot set CourseDateHistoryID as the PK using auto migrations?
Thanks for any help.
You can try manually updating the database using Update-Database -verbose command. It should show you the migration it's applying as well as the errors it encounters.
Or why not add another migration using the Add-Migration command and manually add primary key there, for example:
public partial class AddPrimaryKey : DbMigration
{
public override void Up()
{
AddPrimaryKey(table: "dto.table", column: "CourseDateHistoryID", name: "PK_CourseDateHistoryID");
}
public override void Down()
{
DropPrimaryKey(table: "dto.table", name: "PK_CourseDateHistoryID");
}
}
Hope this helps.

SQL Server CE identifies a cyclical reference with Entity Framework Code First but SQL Server 2008 does not

I am working on an Entity Framework Code First project that has a fairly complex Data Model which deploys absolutely fine on SQL Server 2008.
However when creating an SQL Server CE database for some local End-To-End testing I get the following error message when EF creates the database:
System.Data.SqlServerCe.SqlCeException: The referential relationship will result in a cyclical reference that is not allowed. [ Constraint name = FK_Sites_Persons_PersonId ].
I have disabled the ManyToManyCascadeDeleteConvention in my DataContext model creation method, so that isn't the cause of the problem. The trouble I have is that the relationship in question looks fine in the SQL Server 2008 database- it appears to be a normal foreign key from what I can tell and I can't see anything flowing back in the other direction, although it is not impossible that there is a longer-path circular reference. I don't know why CE would fail and 2008 would succeed.
It turns out the problem was very simply solved- although I had disabled ManyToManyCascadeDeleteConvention I also needed to disable the OneToManyCascadeDeleteConvention to avoid the circular reference problem.
You might also consider explicitly defining the cascading updates and deletes rather than disabling them globally. Assume a model:
namespace Models
{
public class Parent
{
public Parent() { this.Children = new HashSet<Child>(); }
public int id { get; set; }
public string description { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public int id { get; set; }
public string description { get; set; }
public Parent Parent { get; set; }
}
}
Override the OnModelCreating in your context and use the fluent api to specify the cascade options for a given relationship.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>().HasMany<Child>(p => p.Children).WithRequired(c => c.Parent).WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
Of course this is a simple example, but you can apply the same principle to your down-level entities and specifically exclude the cascaded delete that causes the circular reference.

Is it possible to add WillCascadeOnDelete to one side of a many to many relationship

I have the following model:
public class List
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<ListRules> ListRule { get; set; }
}
public class ListRule
{
public virtual int Id { get; set; }
public virtual List List { get; set; }
public virtual ICollection<List> Lists { get; set; }
}
A List can have many ListRules. A ListRule has to belong to one list. A ListRule can also have 0, 1 or many Lists associated with it.
I tried the following bindings:
modelBuilder.Entity<ListRule>()
.HasRequired(x => x.List)
.WithMany(x => x.ListRule)
.Map(x => x.MapKey("ListId"));
modelBuilder.Entity<ListRule>()
.HasMany(x => x.Lists)
.WithMany()
.Map(x => {
x.MapLeftKey("ListRuleId");
x.MapRightKey("ListId");
x.ToTable("ListRuleLists");
});
Before we go further, I woudl like to clarify the result I am looking for:
When someone deletes a List I want the cascade delete constraints to automatically delete the relationships associated with the List in the ListRule table as well as the many to many relationships between ListRule and ListRuleLists tables.
When I try to Update-Database, I get a error saying "FOREIGN KEY constraint 'FK_ListRuleLists_Lists_ListId' on table 'ListRuleLists' may cause cycles or multiple cascade paths." I understand why this is and currently one way to solve it is by adding a WillCascadeOnDelete(false) to the first model binding above.
But this creates an undesired side effect. Let's assume that there is one List with one ListRule with no many-to-many relationships in the ListRuleList table. If a user deletes a List, the ListRule will now be orphaned because of the WillCascadeOnDelete(false).
How can I go about adding the WillCascadeOnDelete(false) to the RightKey side of the second binding instead? In other words, I want to remove the cascade delete from the foreign key constraint "FK_ListRuleLists_Lists_ListId".
Is it possible? If not, are there any other ways to solve this issue?

cannot add an entity with a key that is already in use

I have got this weird error Cannot add an entity with a key that is already in use
But what is quite irritable about that error is that user gets no detais - Who? What? What table? What record is the culprit of this error?
It would be desperately complicated to determine it, in case you do many operations on LINQ objects before .Submit()
Is there any way to determine what certainly caused this error?
This error typically happens when you are creating a new record in a MetaTable with a foreign key relationship and the foreign key record already exists.
For example, let's say you have an Contact table and an Address table, and each Contact can hold multiple Addresses. The error occurs when you create a new Contact record and try to manually associate an existing Address record to that new Contact.
Assuming that the passed Address ID represents an existing Address record, this doesn't work:
public class Contact
{
public int Contact_ID { get; set; }
public string Name { get; set; }
public Address ContactAddress { get; set; }
public string Phone { get; set; }
}
public class Address
{
public int Address_ID { get; set; }
public string Street { get; set; }
public string CityState { get; set; }
public string ZIP { get; set; }
}
public void CreateNewContact(int addressID)
{
Contact contact = new Contact();
contact.Name = "Joe Blough";
contact.ContactAddress.Address_ID = addressID;
contact.Phone = "(555) 123-4567";
DataContact.SubmitChanges();
}
Historically, SQL developers are trained to just pass the ID value in order for the magic to happen. With LINQ-to-SQL, because the database activity is abstracted, we have to pass the whole object so that the LINQ engine can properly reflect the necessary changes in the ChangeSet. In the above example, the LINQ engine assumes that you are asking to create a new Address record, because it didn't have one to work with when the SubmitChanges was made and it has to respect the contract established by the foreign key relationship. It creates a blank Address record with the passed ID value. The error occurs because that ID value already exists in the data table and the ChangeSet has not flagged the Address delta as an Update.
The fix is to pass in the entire record, not just the ID value:
contact.ContactAddress = DataContext.Addresses.Where(a => a.Address_ID == addressID).Single();
Now, the LINQ engine can properly flag the incoming Address record as an existing one and not try to recreate it.
May be the column you trying to Attach(), Remove(), Add() or DeleteOnSubmit(), is a primary key and you are trying to add or attach the same value again.
Also you might be accessing a primary key or foreign key value column in a different method and it's not closed yet when you trying to call the above methods.
Above to these methods Attach(), Remove(), Add() or DeleteOnSubmit(), try to create a new instance of your datacontext again and run.
It sounds like you are doing an Table.Attach() and the entity you are attaching has a key value that L2S is already tracking. This has got nothing to do with a duplicate key in your physical database.
As explained on one of the answers above, this error is more likely due to trying to insert a record into the table with a repeated value on primary ID key field. You could solve the problem by selecting/creating a different primary key.