I have seen many examples of implementing a one to one relationship, but I failed doing mine, because the requirements are some kind different (Guid with database generated option, foreign key property and so on).
I have 2 classes (Bundesland, Programmkonfiguration) that have a 1:1 relationship (both ends are required in business sense) but cannot be joined into one table
Requirements to Bundesland:
Guid Id as Key but without a DatabaseGenerated Attribute
Navigation Property Programmkonfiguration
Bundesland.cs:
public class Bundesland
{
[Key]
public Guid Id { get; set; }
public virtual Programmkonfiguration Programmkonfiguration { get; set; }
}
Requirements to Bundesland
Guid Id as Key generated from Database
ForeignKey Property Bundesland_Id (needed with _ for interface)
Navigation Property Bundesland
Programmkonfiguration.cs:
public class Programmkonfiguration
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid Bundesland_Id { get; set; }
public virtual Bundesland Bundesland { get; set; }
}
database schema should look like this
table Bundesland (Id)
table Programmkonfiguration (Id, Bundesland_Id)
Why I failed until now:
EF doesn’t recognize the relationship by itself
if I use either attributes (ForeignKey, Required) or fluent API and the mode builder is not failing, the foreign key property Programmkonfiguration.Bundesland_Id is never set, after context.SaveChanges()
If you want to help me, here are additional classes you may gonna need: https://gist.github.com/anonymous/9cb554cd864e3dbee1ac
I am using .NET 4.5(.1) with EF5, but I failed with EF6 too
Thanks in advance :)
You can use fluent configuration for this:
public class Bundesland
{
[Key]
[ForeignKey("Programmkonfiguration")]
public Guid Id { get; set; }
public virtual Programmkonfiguration Programmkonfiguration { get; set; }
}
public class BundesLandConfiguration: EntityTypeConfiguration<Bundesland>
{
public BundesLandConfiguration()
{
HasProperty(p=>p.Id)
HasRequired(p=>p.Programmkonfiguration).WithRequiredPrincipal(p=>p.Bundesland);
}
}
public class Programmkonfiguration
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid Bundesland_Id { get; set; }
public virtual Bundesland Bundesland { get; set; }
}
public class ProgrammkonfigurationConfiguration: EntityTypeConfiguration<Programmkonfiguration>
{
public ProgrammkonfigurationConfiguration()
{
HasKey(p=>p.Id);
HasProperty(p=>p.Id)
HasProperty(p=>p.Bundesland_Id)
}
}
Do not forget to add this configurations to EntityModelConfigurations in db context.
Update: because property naming is against convention, you should add [ForeignKey] attribute as I added to property Id of Bundesland class.
Related
I have a model which is used for managing friend relationships. It looks as follows:
public class Relationship
{
[Required]
public User User { get; set; }
[Required]
public User Friend { get; set; }
[Required]
public DateTimeOffset RelationshipInitializationDate { get; set; }
}
Users will have multiple records for their ID and there will be multiple records with the same FriendID so defining either of these as a key is a no-go. I would like the key to be a composite between User and Friend but when I define it like this:
modelBuilder.Entity<Relationship>().HasKey(r => new { r.User, r.Friend });
I get an error that states:
The property 'Relationship.User' is of type 'User' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
How should I go about this to create the primary key that will link with a user and friend object. I didn't have any issues with my other objects having typed properties and I don't have an issue if I add an arbitrary key to the Relationship model. Thanks in advance
The basic idea here is that your adding properties to the model that EF can use to make a relationship. Right you're trying to create a relationship of type User and that is creating an error. To assign a composite key each key needs to be a type compatible with a Key field, not a navigation property. So we add UserId and FriendId of type int, string or GUID etc. and create a relationship off those properties.
public class Relationship
{
public User Friend { get; set; }
public User User { get; set; }
public int FriendId { get; set; }
public int UserId { get; set; }
public DateTimeOffset RelationshipInitializationDate { get; set; }
}
public class User
{
public int UserId { get; set; }
}
You can now define a composite key across UserId and FriendId. Something like this should do:
public class NorthwindContext : DbContext
{
public NorthwindContext(DbContextOptions<NorthwindContext> options):base(options) { }
public NorthwindContext() { }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Relationship>().HasKey(table => new {
table.FriendId, table.UserId
});
}
public DbSet<Relationship> Relationships { get; set; }
public DbSet<User> Users { get; set; }
}
Source: Medium - How To: Entity Framework Core relationships, composite keys, foreign keys, data annotations, Code First and Fluent API
When using data annotations with EF4.1 RC is there an annotation to cause cascade deletes?
public class Category
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
Using this model the constraint generated is:
ALTER TABLE [Product] ADD CONSTRAINT [Product_Category]
FOREIGN KEY ([Category_Id]) REFERENCES [Categorys]([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
If not how is it achieved?
Putting required on the Product table Category relationship field solves this
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
[Required] //<======= Forces Cascade delete
public Category Category { get; set; }
}
I like to turn off cascade delete by default (by removing the OneToManyCascadeDeleteConvention)
I was then hoping to add them back in via annotations, but was surprised that EF doesn't include a CascadeDeleteAttribute.
After spending way too long working around EF's ridiculous internal accessor levels, the code in this gist adds a convention that allows attributes to be used: https://gist.github.com/tystol/20b07bd4e0043d43faff
To use, just stick the [CascadeDelete] on either end of the navigation properties for the relationship, and add the convention in your DbContext's OnModeCreating callback. eg:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Add<CascadeDeleteAttributeConvention>();
}
And in your model:
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[CascadeDelete]
public List<BlogPostComment> Comments { get; set; }
}
Not sure on Data Annotations, but you can add it in the database by modifying the actual relationship.
Looks like the answer is no for data annotations:
http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/394821ae-ab28-4b3f-b554-184a6d1ba72d/
This question appears to show how to do it with the fluent syntax, but not sure if that applies for 4.1 RC
EF 4.1 RC: Weird Cascade Delete
As an additional example to Tyson's answer, I use the [CascadeDelete] attribute like follows in an entity, which successfully adds the "Cascade" delete rule to the Parent-Child relation.
public class Child
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[SkipTracking]
public Guid Id { get; set; }
[CascadeDelete]
public virtual Parent Parent { get; set; }
[Required]
[ForeignKey("Parent")]
public Guid ParentId { get; set; }
}
Full error:
One or more validation errors were detected during model generation:
EFEmployee_Identity_Source: : Multiplicity is not valid in Role 'EFEmployee_Identity_Source' in relationship 'EFEmployee_Identity'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
I am dealing with three types of entities: EFEmployee, EFPerson, and EFOffice. It's kind of weird that I'm getting this error because the code I'm testing only creates an instance of an EFOffice entity. Anyway, here is the EFEmployee entity class:
[Table("employee_entity")]
public class EFEmployee : EFBusinessEntity
{
[ForeignKey("Office")]
public Guid OfficeID { get; set; }
[ForeignKey("Identity")]
public Guid PersonID { get; set; }
[Column("hire_date")]
public DateTime HireDate { get; set; }
[Column("job_title")]
public byte[] JobTitle { get; set; }
[Column("salary")]
public int Salary { get; set; }
[Column("certifications")]
public byte[] Certifications { get; set; }
[Column("vacation_time")]
public int VacationTime { get; set; }
[Column("sick_time")]
public int SickTime { get; set; }
public virtual EFOffice Office { get; set; }
public EFPerson Identity { get; set; }
public virtual EFEmployee ReportingTo { get; set; }
}
And this is my EFPerson entity class:
[Table("person_entity")]
public class EFPerson : EFBusinessEntity
{
[Column("first_name")]
[StringLength(50)]
public string FirstName { get; set; }
[Column("last_name")]
[StringLength(50)]
public string LastName { get; set; }
[Column("phone_num")]
public uint? PhoneNum { get; set; }
[Column("date_of_birth")]
public DateTime DateOfBirth { get; set; }
public EFEmployee Employee { get; set; }
}
You can see that they both inherit from EFBusinessEntity, which is here:
[Table("business_entity")]
public abstract class EFBusinessEntity : IBusinessEntity
{
[Column("tenant_id")]
public Guid TenantId
{
get;
set;
}
[Column("id")]
[Key]
public Guid Id
{
get;
set;
}
}
As you can see, there is a one-to-zero-or-one relationship between EFEmployee and EFPerson, with EFEmployee being the dependent side since there can be a person who is not an employee, but there can't be an employee who is not a person too. Since EFEmployee is the dependent side, I have added a PersonID in EFEmployee with the data annotation (attribute?) above denoting that it's the foreign key to Person:
[ForeignKey("Identity")]
public Guid PersonID { get; set; }
I think I've made it pretty clear for Entity Framework that this is a 1:0..1 relationship. Does anyone know how to solve this error using data annotations (or attributes, whatever those square bracket things above properties are). I can't use fluent API for reasons I'm not getting into.
Generally, with 1:0..1 relationships in Entity Framework, the dependent side needs to use its primary key as the foreign key. Fortunately, for your case, this doesn't seem like it would be a bad idea. You would need to:
Remove the EFEmployee.PersonID property
Add [ForeignKey("Id")] to EFEmployee.Identity
Edit: May not work because key and navigation property are on separate classes. See this.
Having EFEmployee inherit from EFPerson seems like it might be a viable option as well. Inheritance uses TPH by default, but if you want to use TPT (table-per-type), add the [Table] attribute to your type.
I did some more playing around with the models and found out what was wrong. So I kept the foreign key attribute with EFPerson.Identity like jjj suggested:
[ForeignKey("PersonID")]
public virtual EFPerson Identity { get; set; }
Then the other change I had to make was in the EFPerson class. In my EFPerson class I had the navigation property to EFEmployee:
public virtual EFEmployee Employee { get; set; }
However, since this is a 1:0..1 relationship with EFEmployee being the dependent side (i.e. the non-essential side), I removed that navigation property, and when I ran my test it worked.
Suppose you have
public class A
{
public string _myString;
}
And this context:
public class MyContext: DbContext
{
public DbSet<A> myASet{ get; set; }
}
Now, is there a way to tell EF to generate an identity column for myASet?
I don't want to add an ID field to class A, so I wonder if EF could do this.
Many thanks,
Juergen
You must add ID column to your class if you want to have it in the database. Also in EF each entity must have mapped primary key.
EF will only use columns which are actually in your model classes, so you have to put all the ones you want in yourself. This includes identity columns for primary keys.
If you have an entity called Product and a property called 'ProductId' , EF will automatically add the identity column as it looks for entity name + Id by convention.
You can use a column that does not comply with the convention by adding a [key] attribute above the desired property.
In the example below. An identity column will be created for ProductId.
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
In this example, the column 'MyId' will be created as an identity.
public class Product
{
[key]
public int MyId { get; set; }
public string Name { get; set; }
public string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
I'm new to asp.net, mvc3 and entity framework.
I'm trying to develop a mvc3 programm with entity framework and code-first.
So I have two classes with a many-to-many relationship.
One class called "User" the other one is "Course".
public class Course : IValidatableObject
{
[...]
public virtual ICollection<User> Users { get; set; }
[...]
}
public class User : IValidatableObject
{
[...]
public virtual ICollection<Course> Courses { get; set; }
[...]
}
So, this works. But now I need an additional field which safes the status of the registration for a course.
Is there an easy way I don't know?
I tried it this way:
public class Course : IValidatableObject
{
[...]
public virtual ICollection<CourseUser> CourseUsers { get; set; }
[...]
}
public class User : IValidatableObject
{
[...]
public virtual ICollection<CourseUser> CourseUsers { get; set; }
[...]
}
public class CourseUser
{
[Key, ForeignKey("Course"), Column(Order = 0)]
public int Course_ID { get; set; }
[Key, ForeignKey("User"), Column(Order = 1)]
public string User_ID { get; set; }
public int Status { get; set; } //{ pending, approved }
public virtual Course Course { get; set; }
public virtual User User { get; set; }
}
But this makes it much more difficult to add or edit related data.
For example I didn't managed it yet to automatically add the user who created the course to the CourseUsers table.
No there is no easier way to do that. Once you add any additional field to your junction table it must be mapped as entity to allow you access to that field. It is not pure many-to-many relation any more. It is a new entity in your model with two one-to-many relations.