Entity Framework Code First double relation to same class - c#

I have a Page class that is to be associated with similar pages. Each association has additional information about the association. This is the class that has been defined as the representation of a Page.
public class Page {
[Key]
public virtual int Id { get; protected set; }
[Required]
[StringLength(32)]
public virtual string Name { get; set; }
[InverseProperty("Page")]
public virtual ICollection<Association> Associations { get; set; }
}
Each page has can be associated with any number of other pages. This is to be defined as a source and a target page. I do not mind if the association is unidirectional or bidirectional, either will be justified in my particular scenario (I prefer bidirectional associations, tough). This is the association class..
public class Association {
[Key, Column(Order = 0)]
public virtual int PageId { get; protected set; } // SOURCE
[Required]
public virtual Page Page { get; set; } // SOURCE
[Key, Column(Order = 1)]
public virtual int TargetId { get; protected set; } // TARGET
[Required]
public virtual Page Target { get; set; } // TARGET
[Required]
[StringLength(32)]
public virtual string InformationAboutTheAssociation { get; set; }
}
Now I have the following context ...
public class DbCtx : DbContext {
public DbSet<Association> Associations { get; set; }
public DbSet<Page> Pages { get; set; }
}
And the issue is that SQL Express is complaining about possible cyclic references (which is not the case, but it is over-protective). How do I solve the scheme that is generated to accept the described scenario?
Introducing FOREIGN KEY constraint 'FK_dbo.Associations_dbo.Pages_TargetId' on table 'Associations' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Thanks!

RESPONSE
Do what the error tells you.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION. Since you have two foreign keys to the same entity, and model first (probably) inserts by default on delete cascade, you must disable this, because when a page is deleted, the association will try to be deleted by the first foreign key but will fail because of the second foreign key.
Modify 3
public class DbCtx : DbContext {
public DbSet<Association> Associations { get; set; }
public DbSet<Page> Pages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Association>()
.HasRequired(x => x.Page)
.WithMany(x => x.Associations)
.WillCascadeOnDelete(false);
}
}
I think this should work.
Deleting a Page entity
Because you don't have on delete cascade on the Association if you try and delete a Page it will fail if you have Association entities that depend on the Page entity you want to delete.
So when you want to delete a Page you must first delete all the associations referencing the Page. You have a foreign constraint (two of them actually) from the Association to the Page. Use only one of them and be consistent (either Page or Target).
Since you have asked this question I can only conclude you don't really know how SQL works so it is better if you also read a book about designing a database and using SQL.
I haven't used code first in EF but considering an Entity Relation diagram, Page has "1 to many" with Association and Association has two "1 to 1" with Page.
Isn't this cyclic because it seems like and it's a bad design.
Normalize the database.
If you would design the database (database first), you wouldn't need the relation "1 to many" between the Page and Association because you already have the relation "1 to 1" (two of them) from Association to Page.
So if you want to look for a specific association knowing the pagId you can have a select * from association where pageId or targetId = the id you are looking for.
If you would use EF with database first, you would have a back reference from page to association (what you are trying to do here) but not in the actual Entity Relation model of the database itself. They are virtual references.
EDIT:
I reread the question and the problem is the second reference from the Association to the Page. It is strange since the database first works fine with this case.
I will look into it.

Related

Entity Framework - Explicitly mapped ID columns

Consider the relationship between the following entities:
class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
class Author
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
Okay, nobody in their right minds would have a one-to-one relationship in this context; that's not the issue at play here...
You'll notice that for each navigation property (Author and Post) there are explicit Id columns defined (AuthorId and PostId) respectively.
Personally I don't like this approach (though I can see some potential benefit). I'd prefer EF to manage the Id columns internally for me, and just let me expose a relationship between Post and Author.
What I want to know is, is there any official recommendation for or against explicit Id columns?
NOTE: I do know of one place where explicit Id mapping is valuable, and that is when you're implementing a many-to-many join table. You can use the Ids to create a unique constraint which prevents record duplication for the same many-to-many relationship.
What I want to know is, is there any official recommendation for or against explicit Id columns?
Yes:
It is recommended to include properties in the model that map to
foreign keys in the database. With foreign key properties included,
you can create or change a relationship by modifying the foreign key
value on a dependent object. This kind of association is called a
foreign key association. Using foreign keys is even more essential
when working with N-Tier applications.
Entity Framework Relationships and Navigation Properties
This is not an "official recommendation". But here is how I see it.
You want the navigational properties to be virtual for lazy loading, and you will need the two Id columns for the mapping into the database.
But your code never uses those foreign keys. Here is an example that links a Post with an Author.
var p = new Post {Title="Foo"};
p.Author = _db.Authors.First(a => a.Id == 5);
_db.Posts.Add(p);
_db.SaveChanges();
You also need to map those fields up into your domain layer to keep track of relations.

Entity Framework multiple mapping to same table DB first

I have an issue with an Entity Framework from DB model.
My issue is down to the fact that one of my models has a multiple references to one table.
public partial class Customer
{
public int Id { get; set; }
public Nullable<int> PrimaryEngId { get; set; }
public Nullable<int> AssignedDevloperId { get; set; }
public virtual Engineer Engineer { get; set; }
public virtual Engineer Engineer1 { get; set; }
}
In my model the columns are mapped respectively, however when a colleague builds the model from the same database the two are reversed.
I believe the issue is that the first mapping to in was the primaryEngId
and the Db constraint is called FK_Customer_Engineer.
And the assigned developer id was added subsequently and the DB constraint is called FK_Customer_Devloper
So alphabetically Developer come before Engineer and Entity Framework now maps them the other way round.
My code references the Engineer in quite a lot of places which now won't work
Is there any way out of this?
Many thanks
Ian
You have to add missing ForeignKey attributes on foreign keys for those two navigation properties:
[ForeignKey("Primary")]
public int? PrimaryEngId { get; set; }
[ForeignKey("Assigned")]
public int? AssignedDevloperId { get; set; }
public virtual Engineer Primary { get; set; }
public virtual Engineer Assigned { get; set; }
NOTE: Also don't use generic names for navigation properties with EF. In the nutshell one of the best things EF gives you is that you can say:
#myCustomer.Assigned.Name
etc in the view, and you are totally screwing it up with names like Engineer and Engineer1.
NOTE2: Keep Nullable<int> to code generation. int? is a lot more readable.
NOTE3: Use VS refactoring to rename properties Engineer and Engineer1 to what they should be ( PrimaryEngineer and AssignedEningeer etc). After that add ForeignKey attributes to your model. That should be enough. However, any future changes that you are doing has to be done in the Code and not in db.
IF on the other hand you are constantly regenerating entities and context code from database, make sure that all your foreign keys has meaningful names, as EF will use them to generate name.(ie it is not named Engineer1 out of blue) Rename those foreign keys to reflect what logical relationship is. Ie you most likely have the following foreign keys in db:
FK_Customer_Engineer
FK_Customer_Engineer1
You need to rename them to
FK_Customer_PrimaryEngineer
FK_Customer_AssignedEngineer
Update: you can have different column name and property name like so:
[Column("PrimaryEngId")]
[ForeignKey("Primary")]
public int? PrimaryID { get; set; }

Why fluent nhibernate automapping creates many foreign keys instead of one?

I use Fluent Nhibernate and have 2 entities:
public class Document
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual User Author { get; set; }
public virtual DateTime Date { get; set; }
}
and
public class User
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual IList<Document> Docs { get; set; }
public User()
{
Docs = new List<Document>();
}
}
and I don't understand why fnh creates that wrong schema on this simpliest entities. That's what fnh creates in my db:
I can't understand why fnh creates 2 references (Author_id and User_id) to User table instead of a single reference (only Author_id).
I found an workaround here Fluent Nhibernate AutoMapping -- 2 foreign keys to same table? and here Fluent NHibernate Automappings generating 2 foreign keys for 1 relationship but I don't want to use it because I don't understand why I should set up every thing by my hands if I use automappings that should do all work for me (at-least that simpliest and obvious mappings as in my entities).
You have a Document entity referring a User entity (0-1 relationship) through a property named Author, but in the same time, in User entity you refer Document in a one-to-many relationship.
Fluent NHibernate automapping works with conventions, and the specific HasManyConvention maps the relationship creating a foreign key name based on the NAME (and not the type) of the referring entity (in this case USER)
So NHibernate, when creating the relationship between User and Document, creates a User_Id key in the Document table. This is a correct convention behavior.

Code first model not auto incrementing key on insert

I think I have an error in my model, but I'm not sure what it is.
First the error
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_dbo.ProjectDocAccess_dbo.ProjectDoc_ProjectDocAccessID". The conflict
occurred in database "dbname",
table "dbo.ProjectDoc", column 'ProjectDocID'.
The ProjectDocAccess model (trimmed down)
public class ProjectDocAccess
{
public int ProjectDocAccessID { get; set; }
public int ProjectDocID { get; set; }
public virtual ProjectDoc ProjectDoc { get; set; }
}
The ProjectDoc model (trimmed down)
public class ProjectDoc
{
public int ProjectDocID { get; set; }
public int ProjectID { get; set; }
public ProjectDocAccess ProjectDocAccess { get; set; }
public Project Project { get; set; }
}
The fluent API mapping
modelBuilder.Entity<ProjectDocAccess>()
.HasRequired(p => p.ProjectDoc).WithOptional()
.WillCascadeOnDelete(true);
When I attempt to insert a new record in the ProjectDocAccess table, it forces me to insert a value in the ProjectDocAccessID field. In all of my other models, this auto increments. I'm not sure what I am doing wrong. Help?
UPDATE
Based on the answer I selected. This is what I did to fix it.
Removed the fluent API mapping altogether.
Updated the ProjectDocAccess model as follows
public class ProjectDocAccess
{
[Key, ForeignKey("ProjectDoc")]
public int ProjectDocAccessID { get; set; }
public virtual ProjectDoc ProjectDoc { get; set; }
}
First, when issues like this you should take a look at your migration files generated (or if you don't have them just enable it) - it shows in a 'higher level' form of tables/mappings generated for you.
Problem is that you have 'one to one' relationship and your ProjectDocAccess to ProjectDoc is mapped with pk -> pk. Code first automatically does that for you in these cases as that's the only supported way of making the one to one.
So your ProjectDocAccessID is at all times mapped == the same as your ProjectDocID.
The ProjectDocID is auto-generated - but you need to put the access-id to match it yourself. (I'm guessing that could be automated but strictly in db terms it's not).
So, it can't auto-generate things. It has to be copied from the principal
table, where the 'optional' is.
With your current entities it is what it is - but you could rearrange things maybe (though one-to-one are tough to make and could lead to issues if you don't get it right).
There is an option to create one to one using FK-s only (which would then allow you to place an 'independent' primary key on your 'access' table) - but requires manually injecting SQL CONSTRAINT - e.g. see this page - http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx
If you want the ProjectDocAccessID to be generated by the database (an Identity column) then you should decorate that property with the DatabaseGenerated attribute, like this:
[DatabaseGenerated(DatabaseGenerationOption.Identity)]
public int ProjectDocAccessID { get; set; }
As Andrei pointed out, you may also need to decorate it with the [Key] attribute as well.
Source: http://msdn.microsoft.com/en-US/data/jj591583

Entity has two properties which both reference the same entity type in one-to-many relationship

This seems like the most common relationship but for some reason I cannot get code-first EF working. When I run the code below I get the following error:
*{"Introducing FOREIGN KEY constraint 'Recording_RecordingLocation' on table 'Recordings' 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."}*
I have researched SO and other places but have not been able to figure this out. I must be having a minor stroke so if this is duplicative I apologize. I don't think it is because all other reference questions I found were for many-to-many relationships... many-to-one.
My scenario is quite simple...
I have an entity (Recording) that has two required properties RecordingLocation and EditingLocation which are both of the same type WorkLocation. Each Recording has exactly one RecordingLocation and one EditingLocation (not many-to-many). I also have the requisite navigation properties.
Each WorkLocation is stand-alone and is not intrinsically linked to the Recording -- it's just a physical place where some work on that Recording took place. So when I delete a recording I do not want to delete the associated WorkLocations.
public class Recording
{
[Key]
public virtual int Id { get; set; }
//... other properties not shown here
public virtual int RecordingLocationId { get; set; }
public virtual WorkLocation RecordingLocation { get; set; }
public virtual int EditingLocationId { get; set; }
public virtual WorkLocation EditingLocation { get; set; }
{
public class WorkLocation
{
[Key]
public virtual int Id { get; set; }
public virtual WorkLocationType Type { get; set; }
public virtual string Description { get; set; }
public virtual LogicalStatus Status { get; set; }
}
// I'll use this on the front-end to filter a selection list
// but don't necessarily assume a Work Location is bound to only items of this type
public enum WorkLocationType
{
RecordingLocation,
EditingLocation,
MasteringLocation
}
What am I missing to get this working?
Your navigation properties RecordingLocation and EditingLocation are required because the corresponding foreign key properties are not nullable. By convention EF assumes that cascading delete is active for a required one-to-many relationship which causes a problem if you have more than one such relationship refering to the same table - hence the exception.
You must disable cascading delete (also your business logic seems to require it) which is only possible in Fluent API:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Recording>()
.HasRequired(r => r.RecordingLocation)
.WithMany()
.HasForeignKey(f => f.RecordingLocationId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Recording>()
.HasRequired(r => r.EditingLocation)
.WithMany()
.HasForeignKey(f => f.EditingLocationId)
.WillCascadeOnDelete(false);
}
}

Categories