Prevent Entity Framework from inferring FK relationships - ef-code-first

Simplified example: I have the following POCOs:
public class Blog
{
public int Id {get;set;}
}
public class Post
{
public int Id {get;set;}
public int BlogId {get;set;}
}
How can I stop Entity Framework (Code First) from inferring the foreign key relationship on Post.BlogId to Blog.Id?
This is a very simplified example but there is a good reason I don't want to infer the FK in my real world example.

In fact FK relationship for Post.BlogId to Blog.Id will not be inferred in your example! ;)
To get FK for your code it should look either as:
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; set; } // This forces FK
}
public class Post
{
public int Id { get; set; }
public int BlogId { get; set; } // Optional (changes default column name Blog_Id to BlogId)
}
or
public class Blog
{
public int Id { get; set; }
}
public class Post
{
public int Id { get; set; }
public Blog Blog { get; set; } // This forces FK
public int BlogId { get; set; } // Optional (changes default column name Blog_Id to BlogId)
}
or
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; set; } // This forces FK
}
public class Post
{
public int Id { get; set; }
public Blog Blog { get; set; } // This forces FK
public int BlogId { get; set; } // Optional (changes default column name Blog_Id to BlogId)
}

Related

Add a column to a many to many relation table code first entity framework

I have 2 classes which have a many to many relation.
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public bool AvailableOffline { get; set; }
public string URL { get; set; }
public virtual ICollection<Profile> Profiles { get; set; }
}
public class Profile
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Document> Documents { get; set; }
}
On each profile I wish to have a SortOrder field for each document. So I made the joined table explicit in another class
public class ProfileDocuments
{
[Key, Column(Order = 0)]
public int DocumentId { get; set; }
[Key, Column(Order = 1)]
public int ProfileId { get; set; }
public int SortOrder { get; set; }
[ForeignKey("DocumentId")]
public virtual Document Document { get; set; }
[ForeignKey("ProfileId")]
public virtual Profile Profile { get; set; }
}
But when I update the database the table for this last class will not have a column for SortOrder. It only holds the 2 foreign keys. How can I tell EF to generate this table with my column?
When a junction table in a many-to-many association should contain more information than just the two foreign keys, it's no longer possible to map the association as a 'pure' many-to-many (with hidden junction class).
You need an explicit class in the class model to address the extra information (as you already found out), but this also changes the association into 1-n-1:
class Document
{
...
public virtual ICollection<ProfileDocument> ProfileDocuments { get; set; }
}
class Profile
{
...
public virtual ICollection<ProfileDocument> ProfileDocuments { get; set; }
}

Entity Framework Code First Foreign Key Columnname inheritence

Entity Framework code first (v6) creates a columnname in the database that I don't like. In tablename SharepointMappings it adds columnname: 'SharepointDestination_DestinationId' (foreign key).
It also generates a columnname SharepointDestinationId.
I would like to have 1 column, a foreign key, with the name 'SharepointDestinationId'.
My model looks like this:
public class Destination
{
public int DestinationId { get; set; }
}
public class SharepointDestination : Destination
{
public string UserName { get; set; }
public string Password { get; set; }
public string Domain { get; set; }
public string SiteUrl { get; set; }
public string DocumentLibraryName { get; set; }
public List<SharepointMapping> Mappings { get; set; }
}
public class SharepointMapping
{
public int SharepointMappingId { get; set; }
public string SourceFieldName { get; set; }
public string DestinationFieldName { get; set; }
//[ForeignKey("SharepointDestination")]
public int SharepointDestinationId { get; set; }
//[ForeignKey("SharepointDestinationId")]
public virtual SharepointDestination SharepointDestination { get; set; }
}
//.....
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// To use TPT inheritence
modelBuilder.Entity<SharepointDestination>().ToTable("SharepointDestinations");
//modelBuilder.Entity<SharepointMapping>()
// .HasRequired(m => m.SharepointDestination)
// .WithMany(d => d.Mappings)
// .HasForeignKey(m => m.SharepointDestinationId)
// .WillCascadeOnDelete(true);
}
It doesn't matter if i leave or add the attribute ForeignKey and it also doesn't matter if i make properties virtual or not. Completely deleting both properties on SharepointMapping or giving them a complete other name has no consequences.
I think this has something to do with the inheritence structure. Because it's 'only' a 1-n mapping.
How should I configure EF to have only 1 column with the name 'SharepointDestinationId' which should be a foreign key? (and also have the navigation property and DestinationId property on the SharepointMapping class)
Since the key of SharepointDestination is DestinationId, EF can't automatically figure it out. You could go with the annotation:
[ForeignKey("DestinationId")]
public virtual SharepointDestination SharepointDestination { get; set; }
and remove this:
[ForeignKey("SharepointDestination")]
public int SharepointDestinationId { get; set; }
The fluent should work as well if you comment out the annotation:
modelBuilder.Entity<SharepointMapping>()
.HasRequired(m => m.SharepointDestination)
.WithMany(d => d.Mappings)
.HasForeignKey(m => m.DestinationId)
.WillCascadeOnDelete(true);
The ForeignKey attribute is expecting a property name, not a table column name.
Really, you should be able to do this without any attributes.
The following should work:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Parent Parent { get; set; }
public int ParentId { get; set; }
}

ASP.NET MVC Deep Model Relationship? MVC5

Academic Example: Simple Tagging Mechanism - asp.net / MVC5
Models:
// post
public class Post
{
public Int64 Id { get; set; }
public string Title { get; set; }
public virtual IList<TagMap> TagMaps { get; set; }
}
// tag
public class Tag
{
public Int64 Id { get; set; }
public string Name { get; set; }
public virtual Post Post { get; set; }
}
// tagmap
public class TagMap
{
public Int64 Id { get; set; }
public virtual Post Post { get; set; }
public virtual Tag Tag { get; set; }
}
The TagTable contains single tagwords (no duplicates). TagMap represents the relationship between the Post and (many) Tags.
I think this simple design should work or not? But I don't know how to setup the views for editing and creating the tags (not the tagmaps).
Any help would be nice.
It seems that you are achieving a one-to-many relationship. (one post, many tags)..
You can have it with this..
public class Post
{
[Key]
public Int64 PostId { get; set; }
public string Title { get; set; }
}
public class Tag
{
[Key]
public Int64 TagId { get; set; }
public string Name { get; set; }
}
public class TagMap
{
public Int64 PostId { get; set; }
public Int64 TagId { get; set; }
[ForiegnKey("PostId")]
public virtual Post Post { get; set; }
[ForiegnKey("TagId")]
public virtual Tag Tag { get; set; }
}
Take note that you do not need lazy loading on two tables since that you actually referenced it to tagmap table.
Ok sounds nice.
I changed the model to:
public class Post
{
[Key]
public Int64 Id { get; set; }
public string Title { get; set; }
public IList<TagMap> TagMaps { get; set; }
}
In the index method of the postscontroller:
var posts = db.Posts.Include(m => m.TagMaps.Select(n => n.Tag));
And the view:
#for (int i = 0; i < item.TagMaps.Count; i++)
{
#Html.Raw(item.TagMaps[i].Tag.Name);
}
This is working like a charme.

EF codefirst relationships

Could someone show me how to create a relationship in my EF codefirst example - I want a relationship on the Products class that has a many relationship to the Product_Spec class so when I compile the code it will have relationships when the database is generated, and also a relationship for the Specification class related to the Product_Spec
Data Context class
classes:
namespace MvcApplication1.Models
{
public class Department
{
[Key]
public long Id { get; set; }
[Required(ErrorMessage="Please enter a name for the departments.")]
[DataType(DataType.Text)]
public string Name { get; set; }
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please enter a valid url for the department.")]
public string Url { get; set; }
public virtual List<Product> Products { get; set; }
}
public class Product
{
[Key]
public long Id { get; set; }
[ForeignKey("FK_Department_Id")]
public long DepartmentId { get; set; }
[DataType(DataType.Text)]
public string Title { get; set; }
[DataType(DataType.Currency)]
public decimal UnitPrice { get; set; }
[DataType(DataType.Currency)]
public decimal SellPrice { get; set; }
}
public class Product_Spec
{
[ForeignKey("FK_Spec_ProductId")]
public long ProductId { get; set; }
[ForeignKey("FK_Spec_SpecId")]
public long SpecId { get; set; }
}
public class Specification
{
[Key]
public long SpecId { get; set; }
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please enter a product specification type.")]
public string Type { get; set; }
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please enter a product specification value.")]
public string Value { get; set; }
}
}
namespace MvcApplication1
{
public class DataContext : DbContext
{
public DbSet<Department> Department { get; set; }
public DbSet<Product> Product { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>().HasRequired(x => x.Products)
.WithMany().HasForeignKey(x => x.Id).WillCascadeOnDelete(true);
modelBuilder.Entity<Product>().HasOptional(x => x.Product_Specs)
.WithMany().HasForeignKey(x =>x.ProductId) // this lines doesn't work
base.OnModelCreating(modelBuilder);
}
}
}
I think you should set column names in ForeignKey attribute, not constraint names:
public class Product
{
[Key]
public long Id { get; set; }
public long DepartmentId { get; set; }
[DataType(DataType.Text)]
public string Title { get; set; }
[DataType(DataType.Currency)]
public decimal UnitPrice { get; set; }
[DataType(DataType.Currency)]
public decimal SellPrice { get; set; }
[ForeignKey("DepartmentId")]
public virtual Department Department { get; set; }
public ICollection<Product_Spec> ProductSpecs { get; set; }
}
public class Product_Spec
{
public long ProductId { get; set; }
public long SpecId { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product {get; set;}
}
It looks like you're trying to create a Many-Many relationship between Products and Specifications. If that's the case, you don't need to define Product_Spec, using the default conventions, Entity Framework will create your required junction table for you provided you make some alterations to your entities (to define the relationship).
In your case, you could make the following alterations:
public class Product
{
// Your other code
// [ForeignKey("FK_Department_Id")] - Not required, EF will configure the key using conventions
public long DepartmentId { get; set; }
public virtual ICollection<Specification> Specifications { get; set; } // Navigation property for one end for your Product *..* Specification relationship.
}
public class Specification
{
// Your other code
public virtual ICollection<Product> Products { get; set; }
}
When your tables are created, you should see a table with a name like SpecificationProducts, which is the junction table used to hold your many..many Product/Specification relationship.
If you needed to explicitly define this mapping (for example if you had an existing tables), you should be able to do something like this:
modelBuilder.Entity<Product>().
HasMany(s => s.Specifications).
WithMany(p => p.Products).
Map(
m =>
{
m.MapLeftKey("ProductId");
m.MapRightKey("SpecId");
m.ToTable("SpecificationProducts");
});

Why foreign key is added to my table

I have a model:
public class QuestionRevision
{
[Key]
public int Id { get; set; }
public int IdEditor { get; set; }
public List<Tag> Tags { get; set; }
}
public class Tag
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
The problem is, I have additional column in Tags table, called QuestionRevision_Id.
Of course one tag could be assigned to many questions, so it's not what I need.
What annotation I need to add to get the desired result?
I've added at Tag class:
public virtual List<QuestionRevision> QuestionRevisions { get; set; }
and at QuestionRevision:
public virtual List<Tag> Tags { get; set; }
and now I have table TagQuestionRevisions, so now it's work how I want.

Resources