Entity Framework One to Optional One relationship - entity-framework-6

I've been trying to figure out how to create a one to optional one relationship in Entity Framework but everything I try seems to result in an exception or with the navigation properties not working correctly. Could anyone explain how to annotate the below so both objects can exist in the database and a link can optionally be made from one to the other?
Many thanks
public class ObjectA
{
public int ID { get; set; }
public virtual ObjectB ObjectB { get; set; }
}
public class ObjectB
{
public int ID { get; set; }
public virtual ObjectA ObjectA { get; set; }
public int? ObjectAID { get; set; }
}
Edit:
With the code as it stands I get the following exception:
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Unable to determine the principal end of an association between the types 'Namespace.ObjectA' and 'Namespace.ObjectB'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

You need to choose which entity will be a principal and which will be a dependent in this relationship. If it's true that ObjectB is dependent, then you could solve the problem with the following options:
1) Data Annotations. Mark ObjectB's ObjectAID property with RequiredAttribute:
public class ObjectB
{
public int ID { get; set; }
public virtual ObjectA ObjectA { get; set; }
[Required]
public int? ObjectAID { get; set; }
}
2) Fluent API. Configure Model Builder:
public class AppDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ObjectB>().HasRequired(b => b.ObjectA).WithOptional(a => a.ObjectB);
}
}

Related

Entity Framework and Many-to-Many relationships, controlling the Intermediate table column names

I'm trying to wrap my head around a Many-to-Many relationship with Code-First mapping.
If I have an Album Class that can have many Genres (and vice-versa), I understand that I need to have an Intermediate table and Entity Framework will automatically do that for me. However, I would like a little more control over the Intermediate table, so I am creating one myself, the main reason is that I would like to be able to mark the row as deleted from the front-end and leave it in the database.
To do this for all my Classes I have created a BaseObject that they are Inherit from (I've removed many of the Annotations and other code to simplify this post):
public class BaseObject
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Oid { get; set;
public DateTime DateCreated { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletedDate { get; set; }
}
After that we have the Albums and Genres Classes:
public class Album : BaseObject
{
public string Name { get; set; }
public virtual List<AlbumsGenres> Albums { get; set; }
}
public class Genre : BaseObject
{
public string Name { get; set; }
public virtual List<AlbumsGenres> Genres { get; set; }
}
Finally the AlbumsGenres Intermediate Class:
public class AlbumsGenres : BaseObject
{
// Left blank because EF will create "Album_Oid" and "Genre_Oid" columns
// Tried the below code, but EF still created it's own Columns
/*
public Guid Album { get; set; }
public Guid Genre { get; set; }
*/
}
The questions that I have; Is there a way to tell EF to create Album_Oid with a different Column Name like Album?
I would accept an answer of "Just don't worry about it", if a brief explanation (or link) was provided.
You can control the intermediate table, Normally I use explicit mapping but the following works with CodeFirst:
In Album, you want a List<Genre> (not AlbumGenre)
In Genre, you want a List<Album>
In your context, add the following override for OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Album>()
.HasMany(a => a.Genres)
.WithMany(g => g.Albums)
.Map(x =>
{
x.MapLeftKey("AlbumId");
x.MapRightKey("GenreId");
x.ToTable("AlbumGenres");
});
base.OnModelCreating(modelBuilder);
}

Entity Framework Code First - map same entity twice for different purpose

I have two models, One ApplicationUser which holds all users in the system and I have a Quotation model which will hold all Quotations made. now I want to store two mappings to ApplicationUser inside Quotations. So that I can map to created User as well as cancelled User. My model looks like this
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int ApplicationUserID { get; set; }
public virtual ApplicationUser CreatedUser { get; set; }
[ForeignKey("ApplicationUserID")]
public ApplicationUser CancelledUser { get; set; }
}
But this throws an error
Quotation_CancelledUser_Target_Quotation_CancelledUser_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'ApplicationUserID' on entity 'Quotation' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'Quotation_CancelledUser'.
So I guess , The approach I am taking is wrong. Can anyone point out the correct way to achieve this?
The problem you are observing is called "Multiple Cascade Path". A Multiple Cascade Path happens when a cascade path goes from column col1 in table A to table B and also from column col2 in table A to table B.
The exception is caused by SQL Server when code first attempted to add table that has columns appearing more than once of another table.
In SQL Server, a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement. For example, the tree of cascading referential actions must only have one path to a particular table on the cascading referential actions tree.
You will need to use FluentAPI to configure the relationship. I am using EF5 currently and do not know if this can be accomplished in EF6/7.
So modifying your code sample, it would look like:
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int CreatedUserID { get; set; }
// Navigation property
public virtual ApplicationUser CreatedUser { get; set; }
public int CancelledUserID { get; set; }
// Navigation property
public virtual ApplicationUser CancelledUser { get; set; }
}
// Created a simple class for example
public class ApplicationUser
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
Now in you context class you can write:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Disable the default PluralizingTableNameConvention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// Add configuration here
modelBuilder.Entity<Quotation>()
.HasKey(e => e.QuotationID);
modelBuilder.Entity<ApplicationUser>()
.HasKey(e => e.Id);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CreatedUser)
.WithMany()
.HasForeignKey(u => u.CreatedUserID);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CancelledUser)
.WithMany()
.HasForeignKey(u => u.CancelledUserID);
}
For more information with example refer this link.

"principal end of an association" - EF

Unable to determine the principal end of an association between the
types 'XYZ.Models.Attachment' and 'XYZ.Models.Accounts.User'. The
principal end of this association must be explicitly configured using
either the relationship fluent API or data annotations.
Exception has been thrown by the target of an invocation.
That error I get, when I try to update-database with my EF Models.
Part of User.cs:
[Table("Users")]
public class User
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public int MainPhotoId { get; set; }
[ForeignKey("MainPhotoId")]
public virtual Attachment Photo { get; set; }
}
Attachment.cs
[Table("Attachments")]
public class Attachment
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AttachmentId { get; set; }
public string name { get; set; }
public int UserId { get; set; }
public DateTime AddDate { get; set; }
public bool del { get; set; }
[ForeignKey("UserId")]
public virtual User Author { get; set; }
}
Why I get this error? And how to resolve it?
Regards
Mapping conventions detect a one-to-one relationship between User.Photo and Attachment.Author and cannot infer what the principal and what the dependent end is. Hence the exception.
Actually, according to your comments, you want two relationships and not a single one-to-one relationship. You can achieve that only by overriding the convention with Fluent API and you probably need to make one of the relationships optional because otherwise you have a circular mutual dependency between User and Attachment. You can, for example, make the User.Photo property optional by choosing a nullable foreign key:
public int? MainPhotoId { get; set; }
Then the mapping would look like this:
modelBuilder.Entity<User>()
.HasOptional(u => u.Photo)
.WithMany()
.HasForeignKey(u => u.MainPhotoId);
modelBuilder.Entity<Attachment>()
.HasRequired(a => a.Author)
.WithMany()
.HasForeignKey(a => a.UserId);
With this mapping you can remove the [ForeignKey] attributes because the definition of the FK properties is part of the Fluent API mapping (HasForeignKey).

Issue with EF CF and Existing One-to-One Relationship

I am using an existing database with EF Code First and using the modelBuilder to configure. I have two tables where a SESSION can have a SUBJECT, classes are as such:
public class SessionItem {
[Key]
public int SessionId { get;set; }
// Other Values
public int Subject_ID { get;set; }
public virtual Subject Subject { get;set; }
}
public class SubjectItem {
[Key]
public int Subject_ID { get;set; }
// Other Values
public virtual SessionItem Session { get;set; }
}
And then the modelBuilder code is:
modelBuilder.Entity<SessionItem>().ToTable("tblTblSessions");
modelBuilder.Entity<Subject>().ToTable("tblTblSubjects");
modelBuilder.Entity<SessionItem>()
.HasOptional<Subject>(u => u.Subject)
.WithOptionalDependent(c => c.Session).Map(p => p.MapKey("Subject_ID"));
This failed at first until I removed Subject_ID from the SessionItem class, then I got the error: A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
Any idea where I have gone wrong?
Unfortunely one-to-one foreign key associations are not supported with Entity Framework because EF doesn't know what a unique key constraint is (that your Subject_ID column in the Session table apparently has).
You must workaround this by mapping the relationship as one-to-many. Follow the mapping in #flem's answer for the SessionItem entity and for the SubjectItem entity either remove the public virtual SessionItem Session { get;set; } altogether or replace it by
public virtual ICollection<SessionItem> Sessions { get; set; }
You don't need the mapping with Fluent API anymore for this relationship, or if you want, it should be:
modelBuilder.Entity<SessionItem>()
.HasOptional(se => se.Subject)
.WithMany() // or WithMany(su => su.Sessions)
.HasForeignKey(se => se.Subject_ID);
When you add items to that collection you must ensure in your business logic that you don't add more than one item because you can't have more than one row with the same Subject_ID in your database due to the unique key constraint. When you load a subject from the database including the sessions the session collection is either empty or has one single element, but not more.
Try this:
public class SessionItem
{
[Key]
public int SessionId { get;set; }
// Other Values
[ForeignKey("Subject")]
public int? Subject_ID { get;set; }
[ForeignKey("Subject_ID")]
public virtual SubjectItem Subject { get;set; }
}
You need to make Subject_ID foreign key nullable.
[Table("tblTblSessions")]
public class SessionItem {
[Key]
public int SessionId { get; set; }
public virtual SubjectItem Subject { get; set; }
}
[Table("tblTblSubjects")]
public class SubjectItem {
[Key, ForeignKey("Session")]
public int Subject_ID { get; set; }
public virtual SessionItem Session { get; set; }
}
One-to-one relationship between SubjectItem and SessionItem, and you can get rid of all your modelBuilder code - all the table naming and one-to-one mapping you were doing is taken care of with the attributes and properties above.
Edit: Fixed a typo and marked the Dependent side of the one-to-one.

Newbie EF4.3 & MVC4 Relationships

I have a class with a relationship to another table.
public class MyClass
{
[Key]
public Guid Id {get; set; }
public virtual OtherClass OtherClass { get; set; }
}
I hook this up to a controller and create views for CRUD - all works fine.
In the DB a OtherClass_OtherClassId column is created, but this is not in the model.
How can I put a reference in this Id column during the controller's Create method?
How can I force this relationship to be [Required] without having to create a brand new OtherClass each time?
Annotated class with some description:
public class MyClass
{
// [Key] - Don't actually need this attribute
// EF Code First has a number of conventions.
// Columns called "Id" are assumed to be the Key.
public Guid Id {get; set; }
// This reference creates an 'Independent Association'. The Database
// foreign key is created by convention and hidden away in the code.
[Required]
public virtual OtherClass OtherClass { get; set; }
// This setup explicitly declares the foreign key property.
// Again, by convention, EF assumes that "FooId" will be the key for
// a reference to object "Foo"
// This will still be required and a cascade-on-delete property
// like above - an int? would make the association optional.
public int OtherClass2Id { get; set; }
// Leave the navigation property as this - no [Required]
public virtual OtherClass2 { get; set; }
}
So which is better? Independent associations or declaring the foriegn key?
Independent associations match object programming closer. With OOP, one object doesn't really care much about the Id of a member. ORM's try to cover these relationships up, with varying degrees of success.
Declaring the foreign key puts database concerns into your model, but there are scenarios where this makes dealing with EF much easier.
Example - when updating an object with a required independent association, EF will want to have the entire object graph in place.
public class MyClass
{
public int Id {get; set; }
public string Name { get; set; }
[Required] // Note the required. An optional won't have issues below.
public virtual OtherClass OtherClass { get; set; }
}
var c = db.MyClasses.Find(1);
c.Name = "Bruce Wayne";
// Validation error on c.OtherClass.
// EF expects required associations to be loaded.
db.SaveChanges();
If all you want to do is update the name, you'll either have to pull OtherClass from the database as well since it's required for entity validation or attach a stubbed entity (assuming you know the id). If you explicitly declare foreign key, then you won't run into this scenario.
Now with foreign keys, you run into a different issue:
public class MyClass
{
public Guid Id {get; set; }
public string Name { get; set; }
public int OtherClassId { get; set }
public virtual OtherClass OtherClass { get; set; }
}
var c = db.MyClasses.Find(1);
// Stepping through dubugger, here, c.OtherClassId = old id
c.OtherClass = somethingElse;
// c.OtherClassId = old id - Object and id not synced!
db.SaveChanges();
// c.OtherClassId = new id, association persists correctly though.
In summary -
Independent associations
Good: Match OOP and POCO's better
Bad: Often requires a full object graph, even if you're only updating one or two properties. More EF headaches.
Foreign Keys
Good: Easier to work with sometimes - less EF headaches.
Bad: Can be out of sync with their object
Bad: Database concerns in your POCO's
EF generally require handholding with the model configuration. This should get you started. However doing a good tutorial on EF Code First and DB first would be greatly beneficial.
Following has:
Order with multiple OrderItems
single User
and single OrderType made by keeping the identity OrderTypeId and the actual OrderType ref object.
public class Order
{
public Order()
{
OrderItems = new OrderItemCollection();
}
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public string OrderName { get; set; }
public int UserId { get; set; }
public virtual User OrderUser { get; set; }
public virtual OrderItemCollection OrderItems { get; set; }
public int? OrderTypeId { get; set; }
public OrderType OrderType { get; set; }
public override int GetHashCode() { return OrderID.GetHashCode();}
}
public class OrderConfiguration : EntityTypeConfiguration
{
public OrderConfiguration()
{
this.ToTable("ORDERS");
this.HasKey(p => p.OrderID);
this.Property(x => x.OrderID).HasColumnName("ORDER_ID");
this.Property(x => x.OrderName).HasMaxLength(200);
this.HasMany(x => x.OrderItems).WithOptional().HasForeignKey(x => x.OrderID).WillCascadeOnDelete(true);
this.HasRequired(u => u.OrderUser).WithMany().HasForeignKey(u => u.UserId);
this.Property(x => x.OrderTypeId).HasColumnName("ORDER_TYPE_ID");
this.HasOptional(u => u.OrderType).WithMany().HasForeignKey(u => u.OrderTypeId);
}
}
public class OrderContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new OrderConfiguration());
}
}
'

Resources