I'm slightly confused on how to properly setup my model. Below you'll see my POCOs and I'm wondering how I can auto increment the ID's and if it's necessary to set the data annotation [Key]. Is there a naming convention of some sort which makes EF recognize the ID/primary key or do I have to use the [Key] data annotation?
Also is it necessary to set a [Key] data annotation in the child entity?
public class User
{
[Key]
public int UserId { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime Reg { get; set; }
public virtual ICollection<Stats> Stats { get; set; }
}
public class Stats
{
[Key]
public int StatId { get; set; }
public string Age { get; set; }
public string Height { get; set; }
public string Weight { get; set; }
public bool Sex { get; set; }
}
public class BodylogContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Stats> Stats { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<BodylogContext>(null);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
You should look up Entity Framework Code First tutorials for more details.
Specifically, in your case and a few basic rules (disclaimer:) I'm not trying to cover everything just a few basic ones....
You can remove [Key] -
if you use <Entity>Id - or just Id it's made into a PK by default.
Same goes for 'FK-s' and related navigation properties (except that <Property>Id is also mapped by convention),
It's case insensitive.
Identity is by default - for pk types that makes sense - int, long... - not for strings,
If you have more than one pk - then you'd need to 'decorate' it with Key - or in fluent config,
etc...
Note: you can adjust and remove conventions from the fluent configuration.
Also from EF6 you'll be able to define a new ones for your code.
My recommendation: Turn on Migrations and look up the migrations
script code (.cs file) generated file. It always has the clear
description of what are keys, indexes etc. Best way to learn how your
Db is actually created.
I'm just getting with MVC too and I found that this tutorial answered most of the questions you have asked.
By default, the Entity Framework interprets a property that's named ID or classnameID as the primary key. So in your User class, you do not need the [Key] attribute on the UserId property. In your Stats class, the property does not match the name of the class (you have pluralised the name) so here you would need the attribute.
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
Related
I'm working on a serverside blazor project (.net 6) using Entity Framework with code first. I have two tables, let's say (in order to protect private data), we have the Tables Band and Bandsman. Originally, every band could have exactly one bandsman, a bandsman could be connected to more then one band though. It's an example, so please don't question this assumptive circumstances.
I created two classes:
[Table("Band")]
public partial class Band
{
[Key]
public int Id { get; set; }
public string BandName { get; set; }
public int? BandsmanId { get; set; }
public virtual Bandsman Bandsman { get; set; }
}
[Table("Bandsman")]
public partial class Bandsman
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Band> Band { get; set; }
}
So far everything works fine. Entity Framework set the correct foreign key. But now I have to insert a second bandsman. Let's say, the first bandsman is a keyboarder, now I need a drummer as well. So I altered the existing classes:
[Table("Band")]
public partial class Band
{
[Key]
public int Id { get; set; }
public string BandName { get; set; }
public int? BandsmanId { get; set; }
public int? DrummerId { get; set; }
public virtual Bandsman Bandsman { get; set; }
public virtual Bandsman Drummer { get; set; }
}
[Table("Bandsman")]
public partial class Bandsman
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Band> Band { get; set; }
public virtual List<Band> Drummer { get; set; }
}
I know I have to tell Entity Framework now how to map the tables. So I added mapping instructions to the OnModelCreating-Method in DbContext:
builder.Entity<Band>().HasOne(a => a.Bandsman).WithMany().HasForeignKey(b => b.BandsmanId);
builder.Entity<Band>().HasOne(a => a.Drummer).WithMany().HasForeignKey(b => b.DrummerId);
This doesn't work. When I create the migrations I see that Entity Frameworks tries to create new Columns BandsmanId1 and BandsmanId2 to the Band-Table instead of using the Columns I defined.
So I tried to add the instructions the other way around, too, in addition to the previous ones:
builder.Entity<Bandsman>().HasMany<Band>(a => a.Band).WithOne().HasForeignKey(b => b.BandsmanId);
builder.Entity<Bandsman>().HasMany<Band>(a => a.Drummer).WithOne().HasForeignKey(b => b.DrummerId);
It's still the same, Entity Framework tries to add new columns and map the foreign keys to them.
I also tried to rename Band.BandsmanId to Band.KeyboarderId or rather add and map a new column with the new name (so existing data won't get lost), rename Band.Bandsman to Band.Keyboarder and Bandsman.Band to Bandsman.Keyboarder. With no effect, Entity Framework still seems incapable to use the colums I want it to use. I guess the instructions I added to OnModelCreating in DbContext are incorrect, but I'm not able to find out how to put it right. I found some examples here on stackoverflow and elsewhere, but I can't manage to convert one of this examples to my code.
So I hope someone can help me to put the classes and instructions right.
After posting my question, I found the solution in a post that was shown as possibly related:
Entity Framework Code First - two Foreign Keys from same table
I was close, my only mistake was not to name the virtual List-Property of the Bandsman-Class in the .HasMany()-Part of the instructions. So Entity Framework didn't now these properties were related to the foreign key columns in the band-table and tried to create the assumed-to-be-missing columns on its own. This way it works:
builder.Entity<Band>().HasOne(a => a.Bandsman).WithMany(b => b.Band).HasForeignKey(a => a.BandsmanId);
builder.Entity<Band>().HasOne(a => a.Drummer).WithMany(b => b.Drummer).HasForeignKey(a => a.DrummerId);
I'm struggling with composite keys and extra fields being generated by Entity Framework. I have a question about something which I think is odd.
Let's say I have a one to many relationships with these classes:
File (dossier)
[Table("Dossier")]
public class Dossier
{
[Key]
public string Dossiernummer { get; set; }
[Key]
public string Dossierversie { get; set; }
[Required]
public string Dossierreferentie { get; set; }
[Required]
public string Relatienr { get; set; }
public ICollection<Artikel> Artikels { get; set; } ();
}
And my artikel (article) class:
[Table("Artikel")]
public class Artikel
{
[Key]
public string Artnr { get; set; }
[Key]
public string ArtVersie { get; set; }
public string ArtOmschrijving { get; set; }
public Dossier Dossier { get; set; }
public string Dossiernummer { get; set; }
}
I'm using migrations and a code first approach. For some reason using migrations creates a dossiernummer1 column in the artikel table. I don't understand why and would like it gone. Does anyone know how?
Another thing which I prefer not to have is the second primary key in my artikel table. It puts both keys from the dossier table in the artikel table yet I only want to use Dossiernummer as a foreign key. Do you know how to change this?
When getting all the dossiers from the context I notice something odd as well. When I look into a dossier object the artikels list is empty, even though data exists in the database for that. Is it normal you have to initialize it yourself?
Thanks for any help and info in advance.
Kind regards,
you must use fluent API for set relations and add ColumnAttribute to order keys :
[Table("Artikel")]
public class Artikel
{
[Key]
[Column(Order = 1)]
public string Artnr { get; set; }
[Key]
[Column(Order = 2)]
public string ArtVersie { get; set; }
public string ArtOmschrijving { get; set; }
public Dossier Dossier { get; set; }
public string Dossiernummer { get; set; }
}
[Table("Dossier")]
public class Dossier
{
[Key]
[Column(Order = 1)]
public string Dossiernummer { get; set; }
[Key]
[Column(Order = 2)]
public string Dossierversie { get; set; }
[Required]
public string Dossierreferentie { get; set; }
[Required]
public string Relatienr { get; set; }
public ICollection<Artikel> Artikels { get; set; }
}
in your dbcontext override OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
builder.Entity<Dossier>()
.HasMany(x => x.Artikels)
.WithOne(a => a.Dossier)
.HasForeignKey(a => new { a.Dossiernummer, a.Artnr });
builder.Entity<Artikel>()
.HasKey(x => new {x.Artnr,x.ArtVersie});
builder.Entity<Dossier>()
.HasKey(x => new {x.Dossiernummer,x.Dossierversie});
}
Dossiernummer1:
Artikel has a Dossier. EF knows an K relationship must be set up. This requires that the PK of Dossier must be included in Artikel and wants to add it. It finds you've already put in a field with that name (what for it has no idea) and so it adds it as Dossiernummer1. You should not add Dossiernummer to Artikel - unless you you actually need one for something else - as the only reason it's there is to be an FK. EF will take care of that for you.
Adding Dossierversie to Artikel:
It thinks that the PK of Dossier is Dossiernummer + Dossierversie, and so to point to the correct Dossier it must have both of tem. I don't use code-first so I can't advise you on a) how to specify a PK and another, separate index (I assume that's what you want) versus a compound PK (which is what you appear to have).
Dossier.Artikels is empty: That's the way EF works, known as lazy loading. It gets the 'root' objects by not anything owned by them at first. Once your code accesses an Artikels collection it should load them (for that Dossier) at that point. This prevents EF pulling in what could be a large percentage of your database Imaging an ECommerce system. Getting a Customer list would pull in all Orders related to cutomers in that list; all order lines owned by those orders; all Product data related to the products on those order lines and so on. This would not be a good thing. Instead it just gets the things you've specifically mentioned and then pulls in related items as needed.
Incidentally, when looking as an unloaded collection as an attribute of the owner (e.g. looking at Artikels on a loaded Dossier), the debugger in VS tells me that examining the collection will result in it being loaded and gives me the option to continue or not.
I'm building a fairly simple MVC project and still getting my head around where to use navigation properties and foreign keys with code first.
This is the main model class:
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
public GroceryCategory Category { get; set; }
public QualityProfile Quality { get; set; }
public GroceryStore BestStore { get; set; }
public double BestPrice { get; set; }
public double LastSeenPrice { get; set; }
//Navigation Properties
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
}
and these are the relating classes:
public class GroceryStore
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public Uri Website { get; set; }
}
public class QualityProfile
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
/// <summary>
/// Rank out of 1-10, 10 being the best
/// </summary>
public byte Ranking { get; set; }
}
public class GroceryCategory
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Which brings me to my question, is the navigation property of SimilarItems I have in the GroceryItem class sufficient to represent a list of multiple grocery items or does this not work as it is referring to itself?
Additionally...do the Category, Quality and BestStore properties require ID properties to represent a foreign key inside of the GroceryItem class (e.g. CategoryID), or is the way I have this represented OK?
----EDIT----
--Refactored Code--
I've re-factored my model based on the suggestions below, which I think better accommodates the suggestions you've made (yes a 2nd time), realised my model was a little flawed and extracted out the price component into a separate purchases Model.
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
[ForeignKey("Quality")]
public int QualityID { get; set; }
//Navigation Properties
public virtual QualityProfile Quality { get; set; }
public virtual GroceryCategory Category { get; set; }
}
However the last thing I'm uncertain about which is on topic to this post, is if I have a collection as a part of the model (one that does not reference itself like in the first example), can I just represent that with a navigation property or does an extra step need to be taken?
Ie. If I was to allow multiple different categories on a GroceryItem, instead of looking like this:
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual GroceryCategory Category { get; set; }
it would look like this:
public virtual ICollection<GroceryCategory> Categories { get; set; }
The best answer to your question(s) is, "It depends". Navigation properties are one way of informing Entity Framework that there's a relationship between entities. By convention, if you have a navigation property such as:
public Category Category { get; set; }
Entity Framework will create a column on the table named in the form of [RelatedPropertyName]_[RelatedPK]. Given your classes, the property above would cause a column named Category_ID. There's nothing more you need to do make it work. The relationship will automatically be handled by EF.
However, doing it this way, you won't have access to this foreign key property. It's not exposed in the public API of your entity. Often, especially when selecting related items from a select list and similar such scenarios, this becomes problematic, as you must store the selected value some place else, usually a property on a view model, and then use this to query the related thing from the database before setting it on the entity it belongs to and finally saving the entity. Whereas, with an actual foreign key property, you can simply post directly back to this and Entity Framework will automatically wire up the related entity. As a result, I tend to always follow the following pattern with my navigation properties:
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
In most scenarios, Entity Framework will automatically connect those two, such that FooId will hold the foreign key relationship for the Foo navigation property. However, occasionally, EF will trip up and try to create the implicit foreign key behind the scenes, still, but you can correct that behavior by explicitly telling EF that this is the foreign key:
[ForeignKey("Foo")]
public int FooId { get; set; }
Roughly the same applies with collection navigation properties. EF will see this as an indication that there's a one-to-many relationship in play and add the implicit foreign key on the opposite entity. Given your collection:
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
The opposite entity is actually the same entity, which presents an interesting use case. Typically, EF would handle this by assuming there's a one-to-many relationship. You'd end up with a column named GroceryItem_ID on your dbo.GroceryItems table. Here, though, you would not only have no access to the foreign key directly, but you also have no public API for accessing the parent GroceryItem either. That may not be a problem, but it's something to be aware of. The only way you'd be able to manage the relationship is through the collection on the parent, not through a child item in that collection.
However, since this is self-referential and you have not specify a foreign key or instance navigation property, all EF will see is a collection on both sides of the relationship, so my guess is that you'll actually end up with an M2M with an intermediary table. I can't test that theory out myself at the moment, and I haven't tried this particular scenario myself previously.
To create a true one-to-many, you would need to create another navigation property similar to:
public virtual GroceryItem ParentGroceryItem { get; set; }
And, even, then, I don't think EF will get the point without a little Fluent configuration:
HasMany(m => m.SimilarItems).WithOptional(m => m.ParentGroceryItem);
You could also use WithRequired in other scenarios instead of WithOptional, which would obviously make the relationship a required one, but since this is self-referential, it's impossible to have it required, because there will have to be at least one root node with no parent.
Consider the following model classes:
public class Thing
{
public int Id { get; set; }
[Required]
public Text Subject { get; set; }
[Required]
public Text Body { get; set; }
}
public class Text
{
public int Id { get; set; }
public string Value { get; set; }
}
The model is simple - each Thing must reference to two Text entities. Each Text entry at any point in time should be referenced only by a single entity of any other type (Thing is not the only one).
Is it possible to configure EF5 to automatically delete all referenced Texts when Thing gets deleted (via context.Set<Thing>().Remove), or should it be done with a database trigger?
You just need to configure CASCADE DELETE at database level and don't have to do anything special at Entity framework level.
How do I store additional fields in the "link table" that is automagically created for me if I have two entities associated as having a many to many relationship?
I have tried going the "two 1 to many associations"-route, but I'm having a hard time with correctly configuring the cascading deletion.
Unless those extra columns are used by some functions or procedures at the database level, the extra columns in the link table will be useless since they are completely invisible at the Entity Framework level.
It sounds like you need to re-think your object model. If you absolutely need those columns, you can always add them later manually.
You will most likely need to expose the association in your domain model.
As an example, I needed to store an index (display order) against items in an many-to-many relationship (Project <> Images).
Here's the association class:
public class ProjectImage : Entity
{
public Guid ProjectId { get; set; }
public Guid ImageId { get; set; }
public virtual int DisplayIndex { get; set; }
public virtual Project Project { get; set; }
public virtual Image Image { get; set; }
}
Here's the mapping:
public class ProjectImageMap : EntityTypeConfiguration<ProjectImage>
{
public ProjectImageMap()
{
ToTable("ProjectImages");
HasKey(pi => pi.Id);
HasRequired(pi => pi.Project);
HasRequired(pi => pi.Image);
}
}
From Project Map:
HasMany(p => p.ProjectImages).WithRequired(pi => pi.Project);
Maps to the following property on project:
public virtual IList<ProjectImage> ProjectImages { get; set; }
Hope that helps
Ben
Suppose there is a many-to-many association between two types: User and Message, and the association class is defined as UserMessageLink with additional properties.
public class User {
public int Id {get;set;}
}
public class Message {
public int Id {get;set;}
}
//The many-to-many association class with additional properties
public class UserMessageLink {
[Key]
[Column("RecieverId", Order = 0)]
[ForeignKey("Reciever")]
public virtual int RecieverId { get; set; }
[Key]
[Column("MessageId", Order = 1)]
[ForeignKey("Message")]
public virtual int MessageId { get; set; }
public virtual User Reciever { get; set; }
public virtual Message Message { get; set; }
//This is an additional property
public bool IsRead { get; set; }
}