I already have many Tags and I already have many Posts. I just want to post 2 Id's and insert those 2 Id's in my many to many table so the 2 records are linked.
I'm using Entity Framework and Fluent Api.
Table 1 called Tags,
Table 2 called Posts,
Table 3 called TagsPosts
My TagsPosts table has the following:
Tag_Id
Post_Id
I just want to add a new record with these 2 Ids like so:
var entry = new TagPost()
{
Tag_Id = tagId,
Post_Id = postId
};
ApplicationDbContext.TagsPosts.Add(entry);
In my context I have:
public class ApplicationDbContext
{
public DbSet<Tag> Tags{ get; set; }
public DbSet<Post> Posts{ get; set; }
public DbSet<TagPost> TagsPosts { get; set; }
}
My fluent API relationship:
ToTable("Tags");
HasKey(t => t.Id);
HasMany(t => t.Posts).WithMany(p => p.Tags);
The issue is i'm getting the error when I try and add the migration using code first:
EntityType 'TagPost' has no key defined. Define the key for this EntityType.
TagsPosts: EntityType: EntitySet 'TagsPosts' is based on type 'TagPost' that has no keys defined.
This is what TagPost looks like:
public class TagPost
{
public int Tag_Id { get; set; }
public int Post_Id { get; set; }
}
What am I doing wrong?
You don't have to include your TagPost class if you map this M2M relationship by convention.
modelBuilder.Entity<Tag>()
.ToTable("Tags")
.HasKey(t => t.Id)
.HasMany(t => t.Posts)
.WithMany(p => p.Tags)
.Map(cs =>
{
cs.MapLeftKey("Tag_Id");
cs.MapRightKey("Post_Id");
cs.ToTable("TagPost");
});
Read this for further info: http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
EDIT:
//get post object from context
var post = context.Posts.Find(postId);
//get tag object from context
var tag = context.Tags.Find(tagId);
//associate objects
post.Tags.Add(tag);
//commit to db
context.SaveChanges();
Related
I've always defined my many-to-many relationships like this:
modelBuilder.Entity<PriceList>()
.HasMany(i => i.Brands)
.WithMany(p => p.PriceLists)
.Map(m =>
{
m.ToTable("PriceListsBrands");
m.MapLeftKey("PriceListId");
m.MapRightKey("BrandId");
});
And it has always worked well.
But now that I'm using "Entity Framework Extensions Bulk Insert" (https://entityframework-extensions.net/bulk-insert) I want to bulk insert into PriceListsBrands table.
So I had to create the relationship object per sé...(which I usually don't need because I already have the navigation properties Brands and PriceLists)
public class PriceListBrand
{
public long PriceListId { get; set; }
public virtual PriceList PriceList { get; set; }
public long BrandId { get; set; }
public virtual Brand Brand { get; set; }
public PriceListBrand()
{
}
}
To finally call BulkInsert:
var priceListsBrands = new List<PriceListBrand>();
// Populate
// ...
dbContext.BulkInsert(priceListsBrands);
But then I'm getting this exception:
EntityType 'PriceListBrand' has no key defined. Define the key for this EntityType.
I tried adding the following FluentAPI definition above my previous FluentAPI code:
modelBuilder.Entity<CustomerPriceListBrand>()
.HasKey(e => new { e.PriceListId, e.BrandId })
.ToTable("PriceListsBrands");
But then I'm getting this exception:
The EntitySet 'PriceListBrand1' with schema 'dbo' and table
'PriceListsBrands' was already defined.
(Notice the "1": it is trying to define twice the relationship)
So the question is: how can I both define a "many to many" relationship and a relationship object too?
I am having difficulty creating a join table relationship between my Identity Framework IdentityContext(the IdentityUser) and one of my other tables Let's call it Entry. The problem is, Entry is in an entirely separate context doing it's own thing as well.
What is the proper way to associate these two? Where do I define the Join Table in fluent api?
Right now, I am getting the following error.
The key {'ApplicationUserId'} contains properties in shadow state and is referenced by a relationship from 'ApplicationUser.ApplicationUserEntries' to 'ApplicationUserEntry.ApplicationUser'. Configure a non-shadow principal key for this relationship.
These are how my tables are defined.
public class ApplicationUser : IdentityUser
{
...
public virtual List<ApplicationUserEntry> ApplicationUserEntries { get; set; }
}
public class Entry
{
public int Id { get; set; }
...
public virtual List<ApplicationUserEntry> ApplicationUserEntries { get; set; }
}
And the join table as follows.
public class ApplicationUserEntry
{
public int ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int EntryId { get; set; }
public Entry Entry { get; set; }
}
For the IdentityContext I have just some generic setup for other properties
var users = modelBuilder.Entity<ApplicationUser>();
users.Property(u => u.Name).IsRequired().HasMaxLength(65);
users.Property(u => u.FirstName).HasMaxLength(32);
users.Property(u => u.LastName).HasMaxLength(32);
And in my GoalsContext I have some general setup for other unrelated stuff, and the join table defined for ApplicationUserEntry
// Entry Configuration
var entries = modelBuilder.Entity<Entry>();
entries.HasKey(e => e.Id);
entries.HasAlternateKey(e => new { e.MilestoneId, e.CategoryId, e.MetricId });
entries.Property(e => e.Value).IsRequired();
entries.Property(e => e.Locked).IsRequired().HasDefaultValue(false);
entries.ToTable("GoalsEntries");
// ApplicationUserEntry Join Table
modelBuilder.Entity<ApplicationUserEntry>()
.ToTable("GoalsApplicationUserEntry")
.HasKey(se => new { se.ApplicationUserId, se.EntryId });
modelBuilder.Entity<ApplicationUserEntry>()
.HasOne(se => se.ApplicationUser)
.WithMany(s => s.ApplicationUserEntries)
.HasForeignKey(se => se.ApplicationUserId);
modelBuilder.Entity<ApplicationUserEntry>()
.HasOne(se => se.Entry)
.WithMany(e => e.ApplicationUserEntries)
.HasForeignKey(se => se.EntryId);
Now I'm sure I'm obviously missing something but I can't figure out what. I've never attempted to create a many to many relationship between two tables that are defined in two different contexts... and not even sure if that's wise or not to do.
My ultimate goal is to be able to associate owners with Entry records, so they can only be modified by the owners, which I verify with Identity Framework.
Ideally I would just prefer a unidirectional relationship, so I can find the owner from the Entry, but I'm not intending to get a list of Entry by looking at the IdentityUser
I have a project where I'm using EF5, I made a custom Guid Generator and I have an override of the SaveChanges method to assign the ids of my entities.
Everything is working fine except in one case: when the ID of one entity is a FK to another ID of another entity.
A little bit of code to explain the problem:
I have two entities I cannot change:
public class FixedEntityA
{
public Guid Id { get; set;}
public string SomeText { get; set; }
}
public class FixedEntityB
{
public Guid Id { get; set;}
public int OneInt { get; set; }
}
In my project I have an entity defined like this:
public class ComposedEntity
{
public Guid Id { get; set;}
public FixedEntityA FixedA { get; set; }
public FixedEntityB FixedB { get; set; }
public double OneDouble { get; set; }
}
The relationships are:
ComposedEntity may have 0 or 1 FixedEntityA
ComposedEntity may have 0 or 1 FixedEntityB
The constraints on the id are:
The Id of FixedEntityA is a FK pointing to the Id of ComposedEntity
The Id of FixedEntityB is a FK pointing to the Id of ComposedEntity
The mapping class are:
public ComposedEntity(): EntityTypeConfiguration<ComposedEntity>
{
HasOptional(fea => fea.FixedA).WithRequired();
HasOptional(feb => feb.FixedB).WithRequired();
}
Here is my SaveChanges override:
foreach (var entry in ChangeTracker.Entries<IEntity>().Where(e => e.State == EntityState.Added))
{
Type t = entry.Entity.GetType();
List<DatabaseGeneratedAttribute> info = t.GetProperty("Id")
.GetCustomAttributes(typeof (DatabaseGeneratedAttribute), true)
.Cast<DatabaseGeneratedAttribute>().ToList();
if (!info.Any() || info.Single().DatabaseGeneratedOption != DatabaseGeneratedOption.Identity)
{
if (entry.Entity.Id == Guid.Empty)
entry.Entity.Id = (Guid) _idGenerator.Generate();
}
}
return base.SaveChanges();
This code works fine everywhere for all kind of relationships except in this case, I am missing a test to make sure I'am not setting an id on id that are foreign keys, and I have no clue on how to check if an Id is a FK...
Here is a sample object where this code fails:
var fea = new FixedEntityA();
var feb = new FixedEntityB();
var composedEntity = new ComposedEntity();
composedEntity.FixedA = fea;
composedEntity.FixedB = feb;
If you insert the whole graph, all three objects are marked as Added and all Ids are default.
The problem is, with the current SaveChanges method, I will go through all object with the Added state in the change tracker and I will assign an Id to all entity with a default Guid and break my FK constraints.
Thanks in advance guys!
Here is some code that will get the FK properties for a given type (it's horrible I know). Should be simple enough to plug this into your code.
var typeName = "Category";
var fkProperties = ((IObjectContextAdapter)db)
.ObjectContext
.MetadataWorkspace
.GetItems<AssociationType>(DataSpace.CSpace)
.Where(a => a.IsForeignKey)
.Select(a => a.ReferentialConstraints.Single())
.Where(c => c.FromRole.GetEntityType().Name == typeName)
.SelectMany(c => c.FromProperties)
.Select(p => p.Name);
I want to be able to access followers and following as collections from my user entity:
public class User
{
public int Id { get; set; }
public ICollection<User> Followers { get; set; }
public ICollection<User> Following { get; set; }
}
This would then map to a table:
UserFollowers(UserId, FollowerId)
I can get the table generated correctly and working for followers with some fluent config:
modelBuilder.Entity<User>().HasMany(m => m.Followers).WithMany().Map(x => x.MapLeftKey("UserId").MapRightKey("FollowerId").ToTable("UserFollowers"));
The tricky part is letting EF know that the Following collection should map to the same table but with FollowerId mapping to the User.
I have tried simply adding:
modelBuilder.Entity<User>().HasMany(m => m.Following).WithMany().Map(x => x.MapLeftKey("FollowerId").MapRightKey("UserId").ToTable("UserFollowers"));
but I get an error:
The EntitySet 'UserUser1' with schema 'dbo' and table 'UserFollowers'
was already defined. Each EntitySet must refer to a unique schema and
table.
How do I resolve this?
you can do this using one mapping like this.
modelBuilder.Entity<User>().HasMany(m => m.Followers).WithMany(m=>m.Following ).Map(x => x.MapLeftKey("UserId").MapRightKey("FollowerId").ToTable("UserFollowers"));
I'm pretty sure it's something regarding hidden conventions, but I always get an error when trying to map a many-to-many relation to an existing database.
Here is the simplest example:
[Table("ALRole", SchemaName = "AL")]
public class Role
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
[Table("ALUser", SchemaName = "AL")]
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
I got the usual three tables in the db: the first two are obvious, and the third is created with this script:
CREATE TABLE AL.ALUsersRoles
(
RoleID int NOT NULL,
UserID int NOT NULL,
CONSTRAINT PK_ALUserRole PRIMARY KEY(RoleID, UserID),
CONSTRAINT FK_ALUserRole_RoleID FOREIGN KEY(RoleID) REFERENCES AL.ALRole(ID),
CONSTRAINT FK_ALUserRole_UserID FOREIGN KEY(UserID) REFERENCES AL.ALUser(ID)
)
Now I try to map the many-to-many relation, with code like this:
// ...I'm in the EntityTypeConfiguration-derived class (User)
HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(m =>
{
m.MapLeftKey(u => u.ID, "UserID");
m.MapRightKey(r => r.ID, "RoleID");
ToTable("ALUsersRoles", "AL");
});
I tried all the possibile combinations and variations in this code, but I always get the error:
{"Invalid column name 'Name'.\r\nInvalid ...and so on...
So I think it must be the table that is not created correctly.
Any ideas?
Thanks in advance
Andrea
P.S.: I stripped down some of my code, so maybe there can be some small typo...
well, this works for me same as OP.
//many-to-many between *Users -> Web_User_Rol <- Web_Rol*
modelBuilder.Entity<Users>()
.HasMany(u => u.Web_Rols).WithMany(r => r.Users)
.Map(t=>t.MapLeftKey("user_id")
.MapRightKey("roleID")
.ToTable("Web_User_Rol"));
There is nothing wrong with your object model or fluent API code. I've used them and they perfectly created the desired schema without any exception. I think your problem comes from another entity (perhaps one with a "Name" property) unrelated to what you've shown here. To find that, drop (or rename) your existing database and let Code First create one for you and then compare the 2 databases and see what is different.