Self-referencing many-to-many recursive relationship code first Entity Framework - c#

I can't seem to make this work at all
class Member
{
public virtual IList<Member> Friends { get; set; }
[Key]
public int MemberId { get; set; }
public string Name{ get; set; }
}
I tried adding Mappings but in vain. Is there a way to do so with CTP5?

By convention, Code First will take uni-directional associations as one to many. Therefore you need to use fluent API to let Code First know that you want to have a many to many self referencing association:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Member>().HasMany(m => m.Friends).WithMany().Map(m =>
{
m.MapLeftKey("MemberId");
m.MapRightKey("FriendId");
m.ToTable("MembersFriends");
}
);
}

If I am correct you can influence the many to many table name with this code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Member>().HasMany(m => m.Friends).WithMany().Map(m =>
{
m.MapLeftKey("MemberId");
m.MapRightKey("FriendId");
m.ToTable("MembersFriends");
}
);
}
hope this helps.

You can get this to work in EF 4 CTP5 using Model-First, but the CTP5 Code First has too many bugs with self-referential and polymorphic query configurations to use Code First for such scenarios. Morteza Manavi (see other answer) has documented several of them on his blog.

I wanted to get this done without having to write fluent API code. So here is my take.
The following example trying to save whomever other users profiles the user visited and who visited that user profile. Sadly, the following example doesn't support extra properties other than two ids of the visiting and the visited user.
The navigation properties had been linked using the InversePropertyAttribute.
see more about it in entityframework.net and entityframeworktutorial.net
Model ↴
public class User
{
[InverseProperty(nameof(User.VisitingUsers))]
public virtual List<User> VisitedUsers { get; set; }
[NotMapped]
public long? VisitedUsersCount { get { return this.VisitedUsers == null ? 0 : this.VisitedUsers.Count(); } }
[InverseProperty(nameof(User.VisitedUsers))]
public virtual List<User> VisitingUsers { get; set; }
[NotMapped]
public long? VisitingUsersCount { get { return this.VisitingUsers == null ? 0 : this.VisitingUsers.Count(); } }
}
Generated Migration Code ↴
CreateTable(
"dbo.UserUsers",
c => new
{
User_Id = c.Long(nullable: false),
User_Id1 = c.Long(nullable: false),
})
.PrimaryKey(t => new { t.User_Id, t.User_Id1 })
.ForeignKey("dbo.Users", t => t.User_Id)
.ForeignKey("dbo.Users", t => t.User_Id1)
.Index(t => t.User_Id)
.Index(t => t.User_Id1);

Your example is not a many-to-many relationship, it is more of a recursive relationship.
I am not sure how to fix it. But the problem with your code is that your will get two fields on the same row with the same name. MemberId for the id of the row and MemberId for the id of the friend.
Edit
Try doing it like this:
class Member
{
[Key]
public int MemberId { get; set; }
public string Name { get; set; }
public virtual IList<FriendRelationship> Friends { get; set; }
}
class FriendRelationship
{
[Key]
public int RelationshipId { get; set; }
public Member Friend { get; set; }
}

Related

Getting multiple cascades paths error while updating the database using Entity Framework [duplicate]

I've been wrestling with this for a while and can't quite figure out what's happening. I have a Card entity which contains Sides (usually 2) - and both Cards and Sides have a Stage. I'm using EF Codefirst migrations and the migrations are failing with this error:
Introducing FOREIGN KEY constraint 'FK_dbo.Sides_dbo.Cards_CardId' on
table 'Sides' may cause cycles or multiple cascade paths. Specify ON
DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
Here's my Card entity:
public class Card
{
public Card()
{
Sides = new Collection<Side>();
Stage = Stage.ONE;
}
[Key]
[Required]
public virtual int CardId { get; set; }
[Required]
public virtual Stage Stage { get; set; }
[Required]
[ForeignKey("CardId")]
public virtual ICollection<Side> Sides { get; set; }
}
Here's my Side entity:
public class Side
{
public Side()
{
Stage = Stage.ONE;
}
[Key]
[Required]
public virtual int SideId { get; set; }
[Required]
public virtual Stage Stage { get; set; }
[Required]
public int CardId { get; set; }
[ForeignKey("CardId")]
public virtual Card Card { get; set; }
}
And here's my Stage entity:
public class Stage
{
// Zero
public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE");
// Ten seconds
public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO");
public static IEnumerable<Stage> Values
{
get
{
yield return ONE;
yield return TWO;
}
}
public int StageId { get; set; }
private readonly TimeSpan span;
public string Title { get; set; }
Stage(TimeSpan span, string title)
{
this.span = span;
this.Title = title;
}
public TimeSpan Span { get { return span; } }
}
What's odd is that if I add the following to my Stage class:
public int? SideId { get; set; }
[ForeignKey("SideId")]
public virtual Side Side { get; set; }
The migration runs successfully. If I open up SSMS and look at the tables, I can see that Stage_StageId has been added to Cards (as expected/desired), however Sides contains no reference to Stage (not expected).
If I then add
[Required]
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int StageId { get; set; }
To my Side class, I see StageId column added to my Side table.
This is working, but now throughout my application, any reference to Stage contains a SideId, which is in some cases totally irrelevant. I'd like to just give my Card and Side entities a Stage property based on the above Stage class without polluting the stage class with reference properties if possible... what am I doing wrong?
Because Stage is required, all one-to-many relationships where Stage is involved will have cascading delete enabled by default. It means, if you delete a Stage entity
the delete will cascade directly to Side
the delete will cascade directly to Card and because Card and Side have a required one-to-many relationship with cascading delete enabled by default again it will then cascade from Card to Side
So, you have two cascading delete paths from Stage to Side - which causes the exception.
You must either make the Stage optional in at least one of the entities (i.e. remove the [Required] attribute from the Stage properties) or disable cascading delete with Fluent API (not possible with data annotations):
modelBuilder.Entity<Card>()
.HasRequired(c => c.Stage)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Side>()
.HasRequired(s => s.Stage)
.WithMany()
.WillCascadeOnDelete(false);
I had a table that had a circular relationship with others and I was getting the same error. Turns out it is about the foreign key which was not nullable. If the key is not nullable the related object must be deleted, and circular relations don't allow that. So use nullable foreign key.
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }
Anybody wondering how to do it in EF core:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
..... rest of the code.....
I was getting this error for lots of entities when I was migrating down from an EF7 model to an EF6 version. I didn't want to have to go through each entity one at a time, so I used:
builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
You can set cascadeDelete to false or true (in your migration Up() method). Depends upon your requirement.
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
In .NET Core I changed the onDelete option to ReferencialAction.NoAction
constraints: table =>
{
table.PrimaryKey("PK_Schedule", x => x.Id);
table.ForeignKey(
name: "FK_Schedule_Teams_HomeId",
column: x => x.HomeId,
principalTable: "Teams",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
table.ForeignKey(
name: "FK_Schedule_Teams_VisitorId",
column: x => x.VisitorId,
principalTable: "Teams",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});
I had this issue also, I solved it instantly with this answer from a similar thread
In my case, I didn't want to delete the dependent record on key deletion. If this is the case in your situation just simply change the Boolean value in the migration to false:
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
Chances are, if you are creating relationships which throw this compiler error but DO want to maintain cascade delete; you have an issue with your relationships.
I fixed this. When you add the migration, in the Up() method there will be a line like this:
.ForeignKey("dbo.Members", t => t.MemberId, cascadeDelete:True)
If you just delete the cascadeDelete from the end it will work.
Just for documentation purpose, to someone that comes on the future, this thing can be solved as simple as this, and with this method, you could do a method that disabled one time, and you could access your method normally
Add this method to the context database class:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
In .NET Core I played with all upper answers - but without any success.
I made changes a lot in DB structure and every time added new migration attempting to update-database, but received the same error.
Then I started to remove-migration one by one until Package Manager Console threw me exception:
The migration '20170827183131_***' has already been applied to the database
After that, I added new migration (add-migration) and update-database successfully
So my suggestion would be: clear out all your temp migrations, until your current DB state.
public partial class recommended_books : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.RecommendedBook",
c => new
{
RecommendedBookID = c.Int(nullable: false, identity: true),
CourseID = c.Int(nullable: false),
DepartmentID = c.Int(nullable: false),
Title = c.String(),
Author = c.String(),
PublicationDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.RecommendedBookID)
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: false) // was true on migration
.ForeignKey("dbo.Department", t => t.DepartmentID, cascadeDelete: false) // was true on migration
.Index(t => t.CourseID)
.Index(t => t.DepartmentID);
}
public override void Down()
{
DropForeignKey("dbo.RecommendedBook", "DepartmentID", "dbo.Department");
DropForeignKey("dbo.RecommendedBook", "CourseID", "dbo.Course");
DropIndex("dbo.RecommendedBook", new[] { "DepartmentID" });
DropIndex("dbo.RecommendedBook", new[] { "CourseID" });
DropTable("dbo.RecommendedBook");
}
}
When your migration fails you are given a couple of options:
'Introducing FOREIGN KEY constraint 'FK_dbo.RecommendedBook_dbo.Department_DepartmentID' on table 'RecommendedBook' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.'
Here is an example of using the 'modify other FOREIGN KEY constraints' by setting 'cascadeDelete' to false in the migration file and then run 'update-database'.
Make your Foreign key attributes nullable. That will work.
This sounds weird and I don't know why, but in my case that was happening because my ConnectionString was using "." in "data source" attribute. Once I changed it to "localhost" it workded like a charm. No other change was needed.
The existing answers are great I just wanted to add that I ran into this error because of a different reason. I wanted to create an Initial EF migration on an existing DB but I didn't use the -IgnoreChanges flag and applied the Update-Database command on an empty Database (also on the existing fails).
Instead I had to run this command when the current db structure is the current one:
Add-Migration Initial -IgnoreChanges
There is likely a real problem in the db structure but save the world one step at a time...
In .NET 5 < and .NET Core 2.0 < you can use .OnDelete(DeleteBehavior.Restrict) in OnModelCreating like #Nexus23 answer but you do not need to disable cascade for every model.
Example with join entity type configuration many-to-many:
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId)
.OnDelete(DeleteBehavior.Restrict),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId)
.OnDelete(DeleteBehavior.Restrict),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Sources:
https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0
This does require you to remove the many to many relationship yourself or you will receive the following error when you remove a parent entity:
The association between entity types '' and '' has been severed, but
the relationship is either marked as required or is implicitly
required because the foreign key is not nullable. If the
dependent/child entity should be deleted when a required relationship
is severed, configure the relationship to use cascade deletes.
Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to
see the key values
You can solve this by using DeleteBehavior.ClientCascade instead which will allow EF to perform cascade deletes on loaded entities.
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId)
.OnDelete(DeleteBehavior.Cascade),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId)
.OnDelete(DeleteBehavior.ClientCascade),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0
None of the aforementioned solutions worked for me. What I had to do was use a nullable int (int?) on the foreign key that was not required (or not a not null column key) and then delete some of my migrations.
Start by deleting the migrations, then try the nullable int.
Problem was both a modification and model design. No code change was necessary.
The simple way is to, Edit your migration file (cascadeDelete: true) into (cascadeDelete: false) then after assign the Update-Database command in your Package Manager Console.if it's problem with your last migration then all right. Otherwise check your earlier migration history, copy those things, paste into your last migration file, after that do it the same thing. it perfectly works for me.
You could add this in your DataContext.cs, this works for me...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}
I ran into the same problem and stuck for a long. The following steps saved me.
Go through the constraints and change the onDelete ReferentialAction to NoAction from Cascade
constraints: table =>
{
table.PrimaryKey("PK_table1", x => x.Id);
table.ForeignKey(
name: "FK_table1_table2_table2Id",
column: x => x.table2Id,
principalTable: "table2",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});

What type of relationship is this and how to implement it in .NET Core (EFCore, FluentAPI)?

The problem I have is pretty easy, but my mind stopped working. Sorry if my questions is dumb, but I'm not really good with databases (neither I am with EFCore).
I want to have the following tables:
CVs: with ID and Name/Title (string)
Skills: with ID and Name/Title (string)
SkillsCV: with ID, CvID (foreign key to a record in CVs), SkillID (foreign key to a record in Skills)
I don't want to have a foreign key to SkillsCV in the CVs and Skills tables. Is it possible? Is it possible in .NET Core and more importantly with Fluent API?
I've made a small research for FluentAPI and there are foreign keys in the both ends in one-to-one relationships. Is this needed? BTW, it is a one-to-one relationship, right? I don't want the one side to know about the other side. Is this what's called 0 to 1, or this is a completely different thing? I'm really confused.
So what I've seen for one-to-one relationships in FluentAPI, I need the following code:
modelBuilder.Entity<Skill>()
.HasOne(skill => skill.SkillCV) // but I don't have a SkillCV object in Skill model
.WithOne(skillCV => skillCV.Skill) // I have skillCV.Skill
.HasForeignKey<SkillCV>(skillCV => skillCV.SkillID); // I have this foreign key in skillCV as well
But I don't want to have an object (or foreign key) in the Skills table (as I don't want such in the CVs table). Is this possible? I'm for sure doing something wrong. Can you help me to find my mistake (if there is one)?
If you know a better way to do this, please share it. Thanks in advance!
EDIT: A quick example to what I want to create:
CVs Table:
ID, Name
1 "CV1"
2 "CV2"
3 "CV3"
Skills Table:
ID, Name
1 "C#"
2 "Java"
3 "Python"
SkillsCVs Table:
ID, CvID, SkillID
1 1 1
2 1 3
3 2 1
Is this a good solution to solve this problem? I haven't created the SkillsCVs table yet, now I have only CVs and Skills (every skill has a CV_ID), but this way when I need to populate a select box in the frontend, I need to return DISTINCT Skills from the API (because there are 800 C# for example records for different CVs). I thought a SkillsCVs table will solve this issue, but I'm not entirely sure now :D
What you have is a many-to-many relationship. You could model things like this:
public class Skill
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CV
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SkillCV
{
public int SkillID { get; set; }
public Skill Skill { get; set; }
public int CVID { get; set; }
public CV CV { get; set; }
}
Then to set this up with Fluent API, you could do this:
modelBuilder.Entity<SkillCV>()
.HasKey(t => new { t.SkillID, t.CVID});
modelBuilder.Entity<SkillCV>()
.HasOne(pt => pt.Skill)
.WithMany()
.HasForeignKey(pt => pt.SkillID);
modelBuilder.Entity<SkillCV>()
.HasOne(pt => pt.CV)
.WithMany()
.HasForeignKey(pt => pt.CVID);
This way your joins later will be simple, and the SkillCV table will have a composite key made up of SkillID and CVID (ensuring referential integrity).
You can do it only with annotations if I understand you correctly.
public class Skill
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class Cv
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class SkillCv
{
[ForeignKey("Skill")]
public int SkillId { get; set; }
[ForeignKey("Cv")]
public int CvId { get; set; }
public virtual Skill Skill{ get; set; }
public virtual Cv Cv { get; set; }
}
public class TestContext : DbContext
{
public DbSet<Skill> Skills { get; set; }
public DbSet<Cv> Cvs { get; set; }
public DbSet<SkillCv> SkillCvs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//The entity does not have its own key, only the relationship of the two
modelBuilder.Entity<SkillCv>().HasNoKey();
}
}
So you will have a row in the table for each skill that has a cv
var allSkillFromCv = await _context.SkillCvs.Where(s => s.CvId == 1).ToListAsync()
If you want to use the navigation property
var allSkillFromCv = await _context.SkillCvs.Where(s => s.CvId == 1).Include(s => s.Skill).ToListAsync();
You need three table here: CVs Table, Skills Table and SkillsCVs Table. You have a
Many-to-Many relationship between CVs Table and Skills Table. You have to define the Many-to-Many relationship using Fluent API. you need navigation property in both CVs Table and Skills Table
public class CV
{
....
public IList<SkillCV> SkillsCVs { get; set; }
}
public class Skill
{
....
public IList<SkillCV> SkillsCVs { get; set; }
}
in DB context
public DbSet<SkillCV> SkillsCVs { get; set; }
You also need to define relationship using Fluent API
modelBuilder.Entity<SkillCV>()
.HasOne<Skill>(sc => sc.Skill)
.WithMany(s => s.SkillsCVs)
.HasForeignKey(sc => sc.SkillID);
modelBuilder.Entity<SkillCV>()
.HasOne<CV>(sc => sc.CV)
.WithMany(s => s.SkillsCVs)
.HasForeignKey(sc => sc.CvID);

Entity Framework Core not supporting generic abstract entities with many to many relationships

I have faced a strange problem witch EF Core 1.1. I m trying to build application where some entities can be tagged, thus I've created an abstract generic class for the relation table list. The problem is that, it seems like EF do not support to have a generic abstract classes which FK (Id property works).
Here are models:
public abstract class TaggedEntityBase<T> : EntityBase
{
public ICollection<T> EntityTags { get; set; }
public List<Tag> Tags { get { return EntityTags?.Select(x => x.Tag).ToList(); } }
}
public class AddressTag
{
public long TagId { get; set; }
public Tag Tag { get; set; }
public long EntityId { get; set; }
public Address Entity { get; set; }
}
public class Address : TaggedEntityBase<AddressTag>
{
public string Street { get; set; }
public string City { get; set; }
}
public class Tag : EntityBase
{
public string Name { get; set; }
public virtual ICollection<AddressTag> AddressTags { get; set; }
}
The Model Builder mappings:
public DbSet<Address> Addresses { get; set; }
public DbSet<AddressTag> AddressTag { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AddressTag>()
.ToTable("AddressTag");
modelBuilder.Entity<AddressTag>()
.HasKey(t => new { t.EntityId, t.TagId });
modelBuilder.Entity<AddressTag>()
.HasOne(pt => pt.Entity)
.WithMany(p => p.EntityTags)
.HasForeignKey(p => p.EntityId);
modelBuilder.Entity<AddressTag>()
.HasOne(pt => pt.Tag)
.WithMany(p => p.AddressTags)
.HasForeignKey(p => p.TagId);
}
There is an error when EF try to fetch Tags
An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.dll Additional information: Invalid column name 'AddressId'.
I dont even have that Id convention.
Note: when I place explicitly public ICollection<AddressTag> EntityTags { get; set; }inside Address POCO, then it works perfectly, including EntityTags.Tag too.
Thanks for any help :)
The issue has nothing to do with generic and/or abstract base entity classes.
First, to make your sample model compile, I've added the following classes
public abstract class EntityBase
{
public long Id { get; set; }
}
public abstract class EntityTagBase
{
public long TagId { get; set; }
public Tag Tag { get; set; }
}
modified the AddressTag class as follows:
public class AddressTag : EntityTagBase
{
public long EntityId { get; set; }
public Address Entity { get; set; }
}
and added where T : EntityTagBase constraint to TaggedEntityBase<T> class to allow Tag property accessor inside Select(x => x.Tag).
So far so good. The Tag related part of generated migration looks like this:
migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
AddressId = table.Column<long>(nullable: true),
Name = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Tags", x => x.Id);
table.ForeignKey(
name: "FK_Tags_Addresses_AddressId",
column: x => x.AddressId,
principalTable: "Addresses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
See the AddressId column and FK to Addresses table? Why is that? Because of your Tags property:
public List<Tag> Tags { get { return ...; } }
It's probably a current EF Core bug of mapping a read only collection property, but the net effect is that it considers one to many relationship between Address and Tag which of course is not your intention.
In general I would recommend keeping the entity model clean and not include such "helper" properties - both collection and reference type. They look like navigation properties, but they are not, and it's easy to use them by mistake inside a query, which will totally change the execution plan and lead to unexpected exceptions or wrong results (in case the underlying property is not loaded). Not speaking about the violation of a general rule to not create property returning List which is not a member of the class, but created in every property access call.
Shortly, simply remove that property and the problem will be gone. Or if you insist keeping it, then decorate it with NotMapped data annotation:
[NotMapped]
public List<Tag> Tags { get { return ...; } }

Entity framework relationship broken

I have two models. ApplicationUser:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public DateTime AccountCreationDate { get; set; }
public virtual ICollection<ProfileView> ProfilesViewed { get; set; }
}
And ProfileView:
public class ProfileView
{
public int Id { get; set; }
public DateTime ViewDate { get; set; }
public virtual ApplicationUser Viewer { get; set; }
public virtual ApplicationUser Viewee { get; set; }
}
Entity framework seems to have created my tables correctly. I can do the following and retrieve a user's ProfileViews:
db.ProfileViews.Where(p => p.Viewer.Id == currentUser.Id);
My problem is that I can't seem to do the following:
db.Users
.Where(u => u.Id == currentUser.Id)
.Include(u => u.ProfilesViewed);
The above returns null for that user, even though it is a Viewer and a Viewee on several ProfileView.
I ran a foreach on all my users, none of them seem to have any ProfilesViewed if I query them from the Users table with Include. I can only retrieve ProfileViews from the ProfileViews table...
Anyone has any idea how to fix this?
Since you did not mention how the ProfileView.Viewer is related to the ApplicationUser.ProfileViewed EF thinks that they are not related(if you check your DB you can see another FK created in the ProfileView for the ApplicationUser.ProfileViewed collection). So adding instances to ProfileView does not effect the User.ProfilesViewed.
Add this code to the Context class, to specify that each ApplicationUser is related to many ProfileView through ProfilesViewed collection.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ProfileView>().HasRequired(x => x.Viewer)
.WithMany(x => x.ProfilesViewed);
}
The relationships probably need to be explained to be created correctly. I think it's wrong because you have two relationships from ProfileView to ApplicationUser. See the section "Configuring Unconventional Foreign Key Names" in this MSDN article for details of how to configure unconventional relationships using EF Code First.

How to map a related table with no primary key with fluent-NHibernate

Looks a common situation to me: I have two tables:
documents:
dID (pk, int), dName(varchar)
and document_options:
dID (int), oType(int), oValue(varchar)
I would like to have a class Document with a property Options (a List of DocumentOption class)
Since document_options has no PK I cannot use HasMany, and rows from this table don't seem like 'real' entities anyway...
I see a way to generate an auto-number key for document options and map with HasMany, or maybe create a composite ID, but I'd like to know if there is a better option that I don't know about.
In this case, DocumentOptions is a value object, since it has no identity of its own and has no meaning outside of the document it belongs to. So, you would use Component to map the collection properties to the value object.
public class Document : Entity // don't worry about Entity; it's a base type I created that contains the Id property
{
public virtual string Name { get; set; }
public virtual IList<DocumentOptions> Options { get; protected set; }
public Document()
{
Options = new List<DocumentOptions>();
}
}
public class DocumentOptions
{
public virtual int Type { get; set; }
public virtual string Value { get; set; }
}
And the mapping:
public DocumentMap()
{
Table("documents");
Id(c => c.Id)
.Column("dId")
.GeneratedBy.HiLo("10");
Map(c => c.Name)
.Column("dName");
HasMany(c => c.Options)
.Component(c =>
{
c.Map(c2 => c2.Value).Column("oValue");
c.Map(c2 => c2.Type).Column("oType");
})
.Table("document_options")
.KeyColumn("dId")
.Cascade.AllDeleteOrphan();
}
If I understand correctly I had to map options as a list of components:
HasMany(x => x.DocumentOptions)
.Table("document_options")
.KeyColumn("dID")
.Component(c => {
c.Map(x => x.Option, "oID");
c.Map(x => x.Value, "oValue");
})
.Fetch.Subselect(); //This type of join isn't strictly needed, is used for SQL optimization
classes FYI:
public class Options {
public virtual int Option { get; set; }
public virtual int Value { get; set; }
}
public class Document {
public virtual int ID { get; set; }
public virtual String Name { get; set; }
public virtual IList<DocumentOption> DocumentOptions { get; set; }
}

Categories

Resources