I would like to configure two entities to have independent unidirectional 1:1 mappings using Code First Fluent API. But I am struggling to get the correct configuration (even though my schema looks right).
Models;
class User {
Guid Id;
virtual Subscription ActiveSubscription;
}
class Subscription {
Guid Id;
virtual User Owner;
}
In this relationship, Subscription.Owner should be NOT NULL but User.ActiveSubscription should be NULLABLE.
There can be many Subscription for the same User, with 0..1 of those referenced as the User.ActiveSubscription.
I'm using the following EntityTypeConfiguration implementations, but I am unable to INSERT both objects.
class UserMap : EntityTypeConfiguration<User> {
HasOptional(x => x.ActiveSubscription)
.WithOptionalDependent()
.Map(m => m.MapKey("ActiveSubscription_Id"))
.WillCascadeOnDelete(false);
}
class SubscriptionMap : {
HasRequired(x => x.Owner)
.WithOptional()
.Map(m => m.MapKey("Owner_Id"))
.WillCascadeOnDelete(true);
}
Deleting a User should CASCADE DELETE to Subscription table, though this should not be the case for the inverse relationship.
After generating a schema from this mapping, it looks correct, but I get errors on INSERT ("Unable to determine a valid ordering for dependent operations.")
Related
The join table of a many-to-many relationship in my Xamarin.Forms application seems to not be cleared correctly when deleting one of the two entities.
I have these classes:
public class Input
{
// One-to-many
public ObservableCollection<InputResult> InputResults { get; set; }
//...
// Here are many more entities which shouldn't be relevant for this example
//...
}
public class InputResult
{
// One-to-many
public string ParentInputId { get; set;}
// Many-to-many
public ObservableCollection<MyDropdown> MyDropdowns { get; set; }
}
public class MyDropdown
{
// Many-to-many
public ObservableCollection<InputResult> InputResults { get; set; }
}
I configured the relationships in my DbContext class like this:
modelBuilder.Entity<Input>()
.HasMany(b => b.InputResults)
.WithOne()
.HasForeignKey(b => b.ParentInputId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<InputResult>()
.HasMany(b => b.MyDropdowns)
.WithMany(b => b.InputResults);
Let's say I have previously loaded an Input with its InputResults. Then I delete the InputResults in a helper class like that:
Context.RemoveRange(Input.InputResults);
The InputResults get deleted correctly. When I look into the SQLite database directly I still see all the entries in the join table of InputResults and MyDropdown. Why are there still entries? Yesterday one of our users got a unique constraint error after deleting some data and trying to insert the same data again.
I appreciate any help.
Edit:
To expand my comment on CSharp's answer:
I can't use OnDelete(DeleteBehavior.Cascade) when configuring the DbContext. It seems as EF Core did this correctly by itself though. The part of the join table in the DatabaseContextModelSnapshot.cs looks like this:
modelBuilder.Entity("InputResultMyDropdown", b =>
{
b.HasOne("Inputs.MyDropdown", null)
.WithMany()
.HasForeignKey("MyDropdownId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Inputs.InputResult", null)
.WithMany()
.HasForeignKey("InputResultId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
There should be a cascade delete behavior in DbContext:
modelBuilder.Entity<InputResult>()
.HasMany(b => b.MyDropdowns)
.WithMany(b => b.InputResults)
.OnDelete(DeleteBehavior.Cascade);
I have tables with foreign keys from main tables. I want that when I'm deleting an entry also want to be able to first remove all related entities from other tables.
I tried this
public void Delete<T>(T entity) where T : EntityBase
{
var relationManager = ((IObjectContextAdapter)m_context).ObjectContext.ObjectStateManager.GetRelationshipManager(entity);
var related = relationManager.GetAllRelatedEnds();
foreach (var relate in related)
{
// what to do here - ??
}
}
EntryBase is a base DBSet entity for all tables in DB contains UId as GUID type - the relationship is by this GUID
you can do it automatically using small configuration using FluentAPI in onModelCreating Method by add OnDelete(DeleteBehavior.Cascade) example as below
modelBuilder.Entity<YourEntity>(entity =>
{
entity.HasOne(d => d.Entity1)
.WithMany(p => p.YourEntity)
.HasForeignKey(d => d.Id)
.OnDelete(DeleteBehavior.Cascade) //This is the key to solve your problem
.HasConstraintName("FK_YourEntity_Entity1");
}
I use the following modelbuilder to setup my relationships on my database.
In my datacontext I have;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema(schema: DbGlobals.SchemaName);
modelBuilder.AddConfiguration<Address>(new AddressConfiguration());
/*reduced for brevity*/
base.OnModelCreating(modelBuilder);
}
where in the AddressConfiguration() i have the following;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Data.EF.Configuration
{
internal class AddressConfiguration : DbEntityConfiguration<Address>
{
public override void Configure(EntityTypeBuilder<Address> entity)
{
entity.HasKey(x => x.Id);
entity.Property(x => x.Latitude).HasColumnType($"decimal(9,6)").IsRequired();
entity.Property(x => x.Longitude).HasColumnType($"decimal(9,6)").IsRequired();
//I have tried the following but it says doesnt exist
//entity.OnDelete(DeleteBehavior.Cascade);
}
}
}
Now my address model has a List<Contact> Contacts { get; set; }. How can I configure the model. to delete cascade on when the Address is deleted?
I found the following link;
Delete Cascade in EF Core
Which is detailing an OnDelete method, however this doesnt seem to be present on the EntityTypeBuilder<T>?
Can anyone tell me what I am doing wrong here please?
OnDelete specifies how to configure delete operation when applied to delete the dependent entities in the relationship when the principal is deleted or the relationship is severed.
It is available on ReferenceCollectionBuilder|ReferenceReferenceBuilder objects. Read about here.
Solution
You should define the relationship as
entity
.HasMany(a => a.Contacts)
.WithOne(c => c.Address)
.OnDelete(DeleteBehavior.Cascade);
I am using entity framework code first for creating database in my project. I have defined many to many relationship between following two table.:
Student
Course
Fluent Api
modelBuilder.Entity<Student>().HasMany(e => e.Courses)
.WithMany(e => e.Students).Map(m =>
{
m.MapLeftKey("StudentId");
m.MapRightKey("CourseId");
m.ToTable("StudentCourse");
});
This will define a many to many relationship between Student and Course and will create a new table StudentCourse in the database.
Now i want to define a new relationship (that may be 1 to 1 OR 1 to many) between
StudentCource
Any other table
How can i do this with entity framework code first ??
What do you want is no possible without creating a own class (poco) for the StudentCourse
public class StudentCourse
{
public int Id {get;set;}
public Student Student {get; set;}
public Course Course {get; set;}
}
And then use fluent api to make the relationship between this three variables
modelBuilder.Entity<StudentCourse>()
.HasRequired(i => i.Student)
.WithMany(u => u.StudentCourses)
.HasForeignKey(i => i.StudentId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<StudentCourse>()
.HasRequired(i =>i.Course)
.WithMany(d =>d.StudentCourses)
.HasForeignKey(i => i.CourseId)
.WillCascadeOnDelete(false);
Where StudentCourses are the navigation properties in the student class and Course class
You can use the Id defined as primary key or use the foreign key of both tables as primary:
modelBuilder.Entity<StudentCourse>()
.HasKey(i => new {i.StudentId,i.CourseId });
In my opinion use the Id as primary key make you more simple the things for making relationships between StudentCourse and another table.
The title pretty much explains it all, I have a Member object that references 'Friends' who are also type Member.
public class Member : Entity
{
public Member()
{
Friends = new List<Member>();
}
public virtual IList<Member> Friends
{
get; set;
}
}
The schema generation tool makes it a 1:n relationship while it should be a n:n relationship i.e. a column is added to the member table called member_id and no connecting table is created.
Is there any way to make a Self referencing many to many relationships in Fluent NHibernate?
I tried using an override that I got as an answer before:
public class MemberOverride : IAutoMappingOverride<Member>
{
public void Override(AutoMapping<Member> mapping)
{
mapping.HasManyToMany(m => m.Friends)
.Table("MemberFriendsLinkTable");
}
}
but I get the error message:
"NHibernate.MappingException: Repeated column in mapping for collection: Proj.BO.Member.Friends column: Member_id"
Thanks
EDIT: I found the answer, it's to put:
mapping.HasManyToMany(m => m.Friends).ParentKeyColumn("Member_Id").ChildKeyColumn("Friend_Id")
.Table("MemberFriendsLinkTable").Inverse().Cascade.SaveUpdate();
So that I don't have to see this question at the top of the "Unanswered NHibernate Questions" list anymore...
Eitan, the asker, discovered the solution to his own problem. He needed to specify the ParentKeyColumn and ChildKeyColumn like so:
EDIT: I found the answer, it's to put:
mapping.HasManyToMany(m => m.Friends)
.ParentKeyColumn("Member_Id")
.ChildKeyColumn("Friend_Id")
.Table("MemberFriendsLinkTable")
.Inverse()
.Cascade.SaveUpdate();
FluentNHibernate by default names foreign key columns like so: {className}_Id. Since both ends of the many-to-many were of the same type, it wanted to use the same column name, Member_Id for both columns. Explicitly naming the columns circumvents this problem.
References(x => x.Parent)
.Class<Parent>()
.Access.Property()
.Cascade.None()
.LazyLoad()
.Not.Insert()
.Not.Update()
.Columns("PARENT_ID");
HasMany(x => x.Children)
.Access.Property()
.AsBag()
.Cascade.SaveUpdate()
.LazyLoad()
.Inverse()
.Generic()
.KeyColumns.Add("PARENT_ID", mapping => mapping.Name("PARENT_ID")
.SqlType("NUMBER")
.Not.Nullable());
Well I understand that, I have similar kind of issue with little bit of change. Can you please try to answer the question to the link
Fluent nhibernate m-to-m mapping with external table