I am new to ASP.Net MVC and I am trying to get a better understanding of ASP.Net MVC. I did a couple tutorials and made a few models in those tutorials. One question that kept popping up in my head was: When would I use public int Id { get; set; } and when would I be using public int MyClassNameId { get; set; } instead as identifier for my model class? Would it matter if I would use a custom property name instead of the default Id name for my identifier for a model class?
For example, why would I use public int ArtistId { get; set; } over public int Id { get; set; }?:
public class Artist
{
public int ArtistId { get; set; }
public string Name { get; set; }
}
Is it so that it matches a property name in another class in which it will be used as Foreign Key?
Entity Framework CodeFirst recognize the key, by default, by name. Valid names are Id or <YourClassName>Id.
Your property should be named Id or AccountTypesId
Another way is to use the ModelBuilder to specify the key.
Sample
public class MyDbContext : DbContext
{
public DbSet<Artists> Artists{ get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Artists>.HasKey(x => x.ArtistId);
base.OnModelCreating(modelBuilder);
}
}
More about it you can find here
If you use custom property names then compiler will not understand it's meaning that it is an id and must be used as primary key in database table.
when you name it id compiler understands it's meaning .
This depends on whether or not using Entity Framework to set up your databases. If you are Entity Framework looks for specific property names to identity as Primary Keys.
For example, let's say you have a model called Book.
public class Book
{
public string Title {get; set;}
//all other properties here
}
When Entity Framework tries to set up your database, it looks for a property that it can identify as a primary key corresponding to the specific model. In this case EF would look for "BookID" as a primary key. So if you wished to have an accessible primary key property you would set it up like this.
public class Book
{
public int BookID {get;set;}
public string Title {get; set;}
//all other properties here
}
If you wished to set up a primary key that was not called "BookID", you could use a data annotation:
public class Book
{
[Key]
public int BookIdentifier{get;set;}
public string Title {get; set;}
//all other properties here
}
Related
Referring to this previous post: How to add description to columns in Entity Framework 4.3 code first using migrations?
I have successfully implemented the modified solution proposed by user Abdullah but I have encountered the exception below:
System.Data.SqlClient.SqlException HResult=0x80131904 Message=Object
is invalid. Extended properties are not permitted on
'dbo.School.Students', or the object does not exist.
Sample code as below:
public class School
{
public School()
{
Students = new HashSet<Student>();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
[Description("Some Text")]
public string Description { get; set; }
[Description("Some text")]
public ICollection<Student> Students{ get; set; }
}
I understand from the exception message, there is no column generated for Students. Checking the DB shows that the Student table have the column SchoolId as FK.
So the question here is: how do I go about adding/updating the FK description when EF generates the FK column in another table?
As you already understand the foreign key is defined in Student entity and the field is SchoolId so you have to add descriptor there. The Students property in the School entity is the navigation property; a feature from EF to easily get the list of all the students of a particular school.
Although he did not answer my question, I have to thank Raihan for giving me the idea to check again on how Foreign Keys can be declared. :)
Based on Entity Framework Tutorial, a foreign key can be declared 3 ways:
[ForeignKey(NavigationPropertyName)] on the foreign key scalar property in the dependent entity.
[ForeignKey(ForeignKeyPropertyName)] on the related reference navigation property in the dependent entity.
[ForeignKey(ForeignKeyPropertyName)] on the navigation property in the principal entity.
Also referencing to how a one-many relationship can be declared section in Entity Framework Tutorial website, we can see that the method which I had described in my question is Convention 2.
In order for User Raihan's suggestion to work, I will need to change the one-many declaration to Convention 3, which is to declare a navigational properties at both the School and Student classes. Like below:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public School School { get; set; }
}
public class School
{
public int GradeID { get; set; }
public string GradeName { get; set; }
public string Section { get; set; }
public ICollection<Student> Student { get; set; }
}
Referencing the foreign key declaration sample in the same website, the Student class needs to be further modified as shown below in order for the (Description) attribute to work:
public class Student
{
[Description("Id of Student")]
[Key]
public int Id { get; set; }
[Description("Name of Student")]
public string Name { get; set; }
[Description("Some Description")]
public int SchoolId {get; set;}
public School School { get; set; }
}
I am still looking for a shorter way to get the column description in so please do reply if you think your solution is more elegant than mine.. :)
Considering the documentation here, you can define foreign key relationships in your pocos like the given example:
public class Customer
{
[References(typeof(CustomerAddress))]
public int PrimaryAddressId { get; set; }
[Reference]
public CustomerAddress PrimaryAddress { get; set; }
}
However, let's say that my CustomerAddress poco class actually has to be defined like this because someone decided to design the table like this a long, long time ago.
public class CustomerAddress
{
[PrimaryKey]
public int Id_1 { get; set; }
[PrimaryKey]
public string Id_2 { get; set; }
}
How can I properly define my [Reference] for the PrimaryAddress property in the Customer class with the composite key defined in CustomerAddress?
You can't using APIs that rely on it (but you can still use SELECT)
Please see OrmLite limitations.
I had same problem with a legacy database I can't modify (because another project cohabit with mine).
So I deleted primary key then created a new Id field (autoincrement) on the database (PK) and finally created a unique constraint (NOT NULLABLE) on both fields.
So now, I can use OrmLite to select properly without breaking compatibility with the other project.
I have the following (abbreviated) models:
public class Item
{
public int id { get; set; }
public string itemId { get; set; }
... more properties ...
public virtual List<Detail> Details { get; set;}
}
public class Detail
{
[Key]
public int id { get; set; }
public string itemId { get; set; }
... more properties ...
// Navigation property
[ForeignKey("itemId")]
public virtual Item Item { get; set; }
}
If I use itemId as an int, it'll create the FK, however it'll link Item.ID to Detail.itemId - I'd like it to link Item.itemId to Detail.itemId
I'm sure it's something I'm missing in the decorations however it seems that EF want to always use the default ID.
The reason I'm looking to do it this way is because the source data is linked via a string ID, which I can convert to a int but the limitation remains, that each table I'd prefer to have it's own PK until I can make sure the source data is robust enough.
The foreign key in the dependent needs to link (edit - usually links) to the primary key of the principal. If you want these to be strings then all you should need to do is follow the naming conventions for keys and foreign keys::
public class Item
{
//Code First infers that a property is a primary key if a property
//on a class is named “ID” (not case sensitive),
//or the class name followed by "ID"
//so you could use "Id" for the name of the primary key
public string ItemId { get; set; }
//... more properties ...
public virtual List<Detail> Details { get; set;}
}
public class Detail
{
//Let's use DetailId as the key here because that is the convention
//we've used in the "Item" class
public int DetailId { get; set; }
/*Any property with the same data type as the principal primary key
property and with a name that follows one of the following formats
represents a foreign key for the relationship:
<navigation property name><principal primary key property name> (i.e.ItemItemId),
<principal class name><primary key property name>(i.e. ItemItemId),
or <principal primary key property name>(i.e. ItemId).
If multiple matches are found then precedence is given in the order listed above.*/
public string ItemId { get; set; }
//... more properties ...
public virtual Item Item { get; set; }
}
No need for attributes because all the names follow the naming convention for keys and foreign keys.
Now, if you want to add a field to the Item class named Id that is not the primary key (?!!) then you will need to tell Entity Framework that ItemId is the primary key - you can do that with the Key attribute:
public class Item
{
[Key]
public string ItemId { get; set; }
/*Because it is not the primary key, if you want it to be an Identity
field, you may need to add the attribute*/
[DatabaseGenerated(DatabaseGeneratedoption.Identity)]
public int Id {get; set; }
}
EDIT
Made after your comment, it's probably as unconventional as it comes to have foreign keys that don't refer to the primary key, but you are not tied to convention. You override conventions using data attributes or the fluent api.
In this case, you can probably force EF to do it by using the InverseProperty on the navigation property of the dependent (I say "probably" because I haven't tried this so don't actually know if EF will protest):
public class Item
{
public int Id {get; set; }
public string ItemId { get; set; }
public virtual List<Detail> Details { get; set;}
}
public class Detail
{
public int DetailId { get; set; }
public string ItemId { get; set; }
[InverseProperty("ItemId")] //NB EF will look in the principal for this
//i.e. the Item class
public virtual Item Item { get; set; }
}
Reference:
Code first conventions
Relationships with Data Attributes
Relationships with the FluentAPI
I'm trying to configure relations between tables using Data Annotations of Entity Framework. I've found the following example:
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public int StdandardRefId { get; set; }
[ForeignKey("StandardRefId")]
public virtual Standard Standard { get; set; }
}
public class Standard
{
public Standard()
{
StudentsList = new List<Student>();
}
public int StandardId { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Now, the ForeignKey attribute informs, that StandardRefId is a foregin key. I guess, that EF deduces the target table from type of property (Standard). However, I fail to see, how to define, which column the foreign key refers to. I tried:
[Column("CompanyId")]
public int CompanyId { get; set; }
[ForeignKey("CompanyId")]
[InverseProperty("Id")]
public CompanyDAL Company { get; set; }
However, all I got was the following exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll
Additional information: The property 'Id' cannot be configured as a navigation property. The property must be a valid entity type and the property should have a non-abstract getter and setter. For collection properties the type must implement ICollection where T is a valid entity type.
How can I explicitly say, that CompanyId points to Id property of Company table?
A Foreign Key will always refer to a Primary Key. When you create a navigation property to Company, that FK will refer to the PK of Company.
Entity Framework relies on every entity having a key value that it uses for tracking entities. One of the conventions that code first depends on is how it implies which property is the key in each of the code first classes. That convention is to look for a property named “Id” or one that combines the class name and “Id”, such as “BlogId”. The property will map to a primary key column in the database.
Source
What this means is that unless you have specified a different PK for Company, the "Id" property (and column) will be the PK for that entity.
Note: For the example you found, Standard uses the second convention for PK ie Id
In my AspNet MVC 3 project when I try to scaffold an entity which has a One to Zero or One relationship with another entity I get "An item with the same index has already been added" error.
Essentially this happens when the Primary Key of the related table is also a Foreign Key.
At the moment my workaround is
Add an Id column to the related table and make it the primary key
Add Unique Key to the Foreign Key Column.
The problem with this is that EF will generate an ICollection navigation property for the related entity instead of just a property of the related entity type (which I can set to null in case of zero related entities)
Is this a know bug?
Am I doing something wrong?
Is there a better work around to get rid of the ICollection navigation property?
See my answer on this question:
How do I code an optional one-to-one relationship in EF 4.1 code first with lazy loading and the same primary key on both tables?
That's the example code with the correct configuration.
public class ZoneMedia
{
public int ZoneMediaID { get; set; }
public string MediaName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public virtual ZoneMediaText MediaText { get; set; }
}
public class ZoneMediaText
{
public int ZoneMediaID { get; set; }
public string Text { get; set; }
public int Color { get; set; }
public virtual ZoneMedia ZoneMedia { get; set; }
}
public class TestEFDbContext : DbContext
{
public DbSet<ZoneMedia> ZoneMedia { get; set; }
public DbSet<ZoneMediaText> ZoneMediaText { get; set; }
protected override void OnModelCreating (DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ZoneMedia>()
.HasOptional(zm => zm.MediaText);
modelBuilder.Entity<ZoneMediaText>()
.HasKey(zmt => zmt.ZoneMediaID);
modelBuilder.Entity<ZoneMediaText>()
.HasRequired(zmt => zmt.ZoneMedia)
.WithRequiredDependent(zm => zm.MediaText);
base.OnModelCreating(modelBuilder);
}
}
class Program
{
static void Main (string[] args)
{
var dbcontext = new TestEFDbContext();
var medias = dbcontext.ZoneMedia.ToList();
}
}
You can also achieve this with DataAnnotations, but I generally prefer to keep my entity models as POCOs.
Try to use the [Key] attribute to the intended primary key. You may need to import the namespace System.ComponentModel.DataAnnotations
Also check the documentation about the full implementation of this namespace.
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx