Many to many relationship identity error - c#

I'm working on a mvc4 app with ef5 codefirst and I cannot solve this error:
The member with identity 'xxxx' does not exist in the metadata collection.
Update:
I saw that I used two different contexts (the navigation object was called thorugh a repository that creates a different DbContext), probably this is a problem. I changed that, but now I get a new error:
Invalid column name 'Brewery_BreweryId'.
In the IntelliTrace I saw that ef tries to
select ..., Brewery_BreweryId from UserProfiles
This column is not present and shouldn't be present, I want a many to many, not a one-to-many.
I think that is something related to a many to many relation.
this is an example of my code
internal class BreweryConfiguration : EntityTypeConfiguration<Brewery>
{
public BreweryConfiguration()
{
// PK
HasKey(e => e.BreweryId);
// FK
HasMany(e => e.UserProfiles)
.WithMany()
.Map(m =>
{
m.MapLeftKey("BreweryId");
m.MapRightKey("UserId");
m.ToTable("BreweryUserProfiles");
});
namespace Project2.DAL.Entities
{
[Table("Breweries")]
public class Brewery : ABrewery
{
public int BreweryId { get; set; }
public ICollection<UserProfile> UserProfiles { get; set; }
}
}
namespace Project1.DAL.Entities
{
[Table("UserProfiles")]
public class UserProfile : IUserProfile
{
[Key]
public int UserId { get; set; }
...
}
}

c.MapLeftKey("ClassB_ID");
c.MapRightKey("ClassA_ID");
should be
c.MapLeftKey("ClassA_ID");
c.MapRightKey("ClassB_ID");
Edit:
You need to define the PK of the ClassB in the configuration as well. In the way you implemented, you may add another derived Configuration for ClassB.

Related

Foreign key in Owned entity: "There is no corresponding CLR property or field"

This is a tale of optional owned entities and foreign keys.
I'm working with EF 5 (code first) and I do this :
public class Parent {
public Guid Id { get; private set; }
public OwnedType1? Owned1 { get; private set; }
public OwnedType2? Owned2 { get; private set; }
public Parent(Guid id, OwnedType1? owned1, OwnedType2? owned2) {
Id = id; Owned1 = owned1; Owned2 = owned2;
}
}
public class OwnedType1 {
public Guid? OptionalExternalId { get; private set; }
public OwnedType1 (Guid? optionalExternalId) {
OptionalExternalId = optionalExternalId;
}
}
public class OwnedType2 {
public Guid? OptionalExternalId { get; private set; }
public OwnedType2 (Guid? optionalExternalId) {
OptionalExternalId = optionalExternalId;
}
}
public class Shared {
public Guid Id { get; private set; }
public Shared (Guid id) {
Id = id;
}
}
Now, the configuration :
//-------- for Parent ------------
public void Configure(EntityTypeBuilder<Parent> builder) {
builder
.ToTable("Parents")
.HasKey(p => p.Id);
builder
.OwnsOne(p => p.Owned1)
.HasOne<Shared>()
.WithMany()
.HasForeignKey(x => x.OptionalExternalId);
builder
.OwnsOne(p => p.Owned2)
.HasOne<Shared>()
.WithMany()
.HasForeignKey(x => x.OptionalExternalId);
}
//-------- for OwnedType1 ------------
// (there's no builder as they're owned and EntityTypeBuilder<Parent> is enough)
//-------- for OwnedType2 ------------
// (there's no builder as they're owned and EntityTypeBuilder<Parent> is enough)
//-------- for Shared ---------------
public void Configure(EntityTypeBuilder<Shared> builder) {
builder
.ToTable("Shareds")
.HasKey(p => p.Id);
}
Side note : If you're wondering why OwnedType1 and OwnedType2 don't each have a property called 'ParentId', it's because it's created implicitly by the "OwnsOne".
My problem is this :
When I create a new Migration, then OwnedType1 works like a charm, but for OwnedType2 (which is quasi-identical), I get his error :
The property 'OptionalExternalId' cannot be added to the type
'MyNameSpace.OwnedType2' because no property type was specified and
there is no corresponding CLR property or field. To add a shadow state
property, the property type must be specified.
I don't understand what it's complaining about. And why it's complaining only for one of them.
I know that you probably can't work it out with this simplified version of my schema, but what I'm asking is what you think it might be (follow your guts of EF guru) :
Some missing constructor?
Incorrect visibility on one of the fields?
Bad navigation definition?
A typo?
Something tricky (like : If you're going to have TWO different entity classes having a one-to-many relation with Shared, then they can't use the same name for external key. Or I need to use a composite key. Or whatnot).
It was a configuration issue that had nothing to do with Owned entities. Another case of "EF error message is obscure but issue is somewhere there in plain sight".
Unfortunately I don't remember how I fixed it. But it was along the lines of "Need an extra constructor with all the paramaters" or "one of the fields had a different name in the constructor parameters" or one of those classic EF mishaps.

Extend ApplicationUser how to set correctly relationship from shared project

I am using ASP.NET Identity 2.0. Currently I would like to extend ApplicationUser (renamed AspNetUsers to Users) by table UserDetails like FirstName etc.
What's my issue?
I have separated class library project which stores EF Model. I don't like to generate another EF Model (if its not necessary).
I have in SQL Server relationship between Users table and UserDetails table, that's all alright. Unfortunately in ASP project Users table is somewhere deep hard-coded in IdentityUser. That means I have ApplicationUser which inherits IdentityUser.
What am I trying is in AppDbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Ignore columns
builder.Entity<ApplicationUser>().Ignore(c => c.PhoneNumberConfirmed);
// Rename tables
builder.Entity<ApplicationUser>().ToTable("Users");
builder.Entity<IdentityRole>().ToTable("Roles");
builder.Entity<IdentityRoleClaim<string>>().ToTable("IdentityRoleClaims");
builder.Entity<IdentityUserClaim<string>>().ToTable("IdentityUserClaims");
builder.Entity<IdentityUserLogin<string>>().ToTable("IdentityUserLogins");
builder.Entity<IdentityUserToken<string>>().ToTable("IdentityUserTokens");
// Relationship
builder.Entity<ApplicationUser>()
.HasOne(p => p.UserDetails)
.WithOne(i => i.Users)
.HasForeignKey<Shared.Database.UserDetails>(u => u.UserID);
}
Problem is on last line in WithOne(i => i.Users), i.Users is not ApplicationUser. Funny thing is that ApplicationUser has same properties like i.Users :D
So I only need to know how to correctly convert it to make relationship.
Any ideas how to solve this? Or any advice how to do it another way?
Shared project is used by 1 additional project (WCF) so I can't put there inheriting from IdentityUser :/
Thanks guys.
The way I understand it, you want to map two different objects to one and the same database table and also map another entity FK to that table. Adn you have no problem with the first part, but having troubles with the second:
Problem is on last line in WithOne(i => i.Users), i.Users is not ApplicationUser. Funny thing is that ApplicationUser has same properties like i.Users :D
I see two solutions.
Remove the navigation property from the UserDetails class and simply use in both places WithOne().
In case you need that navigation property, you could use approach similar to the Identity class structure as follows:
Let say the UserDetails class that you have currently in the shared project looks like this:
public class UserDetails
{
public string Id { get; set; }
public string FirstName { get; set; }
// other properties ...
public string UserID { get; set; }
public User User { get; set; } // the problematic navigation property
}
You can convert it to a generic class:
public class UserDetails<TUser> where TUser: class
{
public string Id { get; set; }
public string FirstName { get; set; }
// other properties ...
public string UserId { get; set; }
public TUser User { get; set; }
}
and let the old non generic class simply inherit from it:
public class UserDetails : UserDetails<User> { }
Now everything in the shared project EF model should be as before, so the other (WCF) project using it should not be affected.
I guess you already are getting the point. In ASP project, you would just create another class:
public class ApplicationUserDetails : UserDetails<ApplicationUser> { }
and map it to the UserDetails table similar to how you mapped ApplicationUser to Users:
builder.Entity<ApplicationUserDetails>().ToTable("UserDetails");
Then change the UserDetails navigation property in the ApplicationUser class to:
public ApplicationUserDetails UserDetails { get; set; }
and finally set up the relationship w/o any problem:
builder.Entity<ApplicationUser>()
.HasOne(u => u.UserDetails)
.WithOne(d => d.User)
.HasForeignKey<ApplicationUserDetails>(d => d.UserID);

Tracking changes in Entity Framework for many-to-many relationships with behavior

I'm currently attempting to use Entity Framework's ChangeTracker for auditing purposes. I'm overriding the SaveChanges() method in my DbContext and creating logs for entities that have been added, modified, or deleted. Here is the code for that FWIW:
public override int SaveChanges()
{
var validStates = new EntityState[] { EntityState.Added, EntityState.Modified, EntityState.Deleted };
var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && validStates.Contains(x.State));
var entriesToAudit = new Dictionary<object, EntityState>();
foreach (var entity in entities)
{
entriesToAudit.Add(entity.Entity, entity.State);
}
//Save entries first so the IDs of new records will be populated
var result = base.SaveChanges();
createAuditLogs(entriesToAudit, entityRelationshipsToAudit, changeUserId);
return result;
}
This works great for "normal" entities. For simple many-to-many relationships, however, I had to extend this implementation to include "Independent Associations" as described in this fantastic SO answer which accesses changes via the ObjectContext like so:
private static IEnumerable<EntityRelationship> GetRelationships(this DbContext context, EntityState relationshipState, Func<ObjectStateEntry, int, object> getValue)
{
context.ChangeTracker.DetectChanges();
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
return objectContext
.ObjectStateManager
.GetObjectStateEntries(relationshipState)
.Where(e => e.IsRelationship)
.Select(
e => new EntityRelationship(
e.EntitySet.Name,
objectContext.GetObjectByKey((EntityKey)getValue(e, 0)),
objectContext.GetObjectByKey((EntityKey)getValue(e, 1))));
}
Once implemented, this also worked great, but only for many-to-many relationships that use a junction table. By this, I'm referring to a situation where the relationship is not represented by a class/entity, but only a database table with two columns - one for each foreign key.
There are certain many-to-many relationships in my data model, however, where the relationship has "behavior" (properties). In this example, ProgramGroup is the many-to-many relationship which has a Pin property:
public class Program
{
public int ProgramId { get; set; }
public List<ProgramGroup> ProgramGroups { get; set; }
}
public class Group
{
public int GroupId { get; set; }
public IList<ProgramGroup> ProgramGroups { get; set; }
}
public class ProgramGroup
{
public int ProgramGroupId { get; set; }
public int ProgramId { get; set; }
public int GroupId { get; set; }
public string Pin { get; set; }
}
In this situation, I'm not seeing a change to a ProgramGroup (eg. if the Pin is changed) in either the "normal" DbContext ChangeTracker, nor the ObjectContext relationship method. As I step through the code, though, I can see that the change is in the ObjectContext's StateEntries, but it's entry has IsRelationship=false which, of course, fails the .Where(e => e.IsRelationship) condition.
My question is why is a many-to-many relationship with behavior not appearing in the normal DbContext ChangeTracker since it's represented by an actual class/entity and why is it not marked as a relationship in the ObjectContext StateEntries? Also, what is the best practice for accessing these type of changes?
Thanks in advance.
EDIT:
In response to #FrancescCastells's comment that perhaps not explicitly defining a configuration for the ProgramGroup is cause of the problem, I added the following configuration:
public class ProgramGroupConfiguration : EntityTypeConfiguration<ProgramGroup>
{
public ProgramGroupConfiguration()
{
ToTable("ProgramGroups");
HasKey(p => p.ProgramGroupId);
Property(p => p.ProgramGroupId).IsRequired();
Property(p => p.ProgramId).IsRequired();
Property(p => p.GroupId).IsRequired();
Property(p => p.Pin).HasMaxLength(50).IsRequired();
}
And here are my other configurations:
public class ProgramConfiguration : EntityTypeConfiguration<Program>
{
public ProgramConfiguration()
{
ToTable("Programs");
HasKey(p => p.ProgramId);
Property(p => p.ProgramId).IsRequired();
HasMany(p => p.ProgramGroups).WithRequired(p => p.Program).HasForeignKey(p => p.ProgramId);
}
}
public class GroupConfiguration : EntityTypeConfiguration<Group>
{
public GroupConfiguration()
{
ToTable("Groups");
HasKey(p => p.GroupId);
Property(p => p.GroupId).IsRequired();
HasMany(p => p.ProgramGroups).WithRequired(p => p.Group).HasForeignKey(p => p.GroupId);
}
When these are implemented, EF still does not show the modified ProgramGroup in the ChangeTracker.
While the concept of "relationship with attributes" is mentioned in the theory of entity-relationship modelling, as far as Entity Framework is concerned, your ProgramGroup class is an entity. You're probably unwittingly filtering it out with the x.Entity is BaseEntity check in the first code snippet.
I believe the problem lies in the definition of your Program and Group class and overridden SaveChanges method. With the current definition of the classes the EF is unable to use change tracking proxies, that catch changes as they are being made. Instead of that the EF relies on the snapshot change detection, that is done as part of SaveChanges method. Since you call base.SaveChanges() at the end of the overridden method, the changes are not detected yet when you request them from ChangeTracker.
You have two options - you can either call ChangeTracker.DetectChanges(); at the beginning of the SaveChanges method or change definition of your classes to support change tracking proxies.
public class Program {
public int ProgramId { get; set; }
public virtual ICollection<ProgramGroup> ProgramGroups { get; set; }
}
public class Group {
public int GroupId { get; set; }
public virtual ICollection<ProgramGroup> ProgramGroups { get; set; }
}
The basic requirements for creating change tracking proxies are:
A class must be declared as public
A class must not be sealed
A class must not be abstract
A class must have a public or protected constructor that does not have parameters.
A navigation property that represents the "many" end of a relationship must have public virtual get and set accessors
A navigation property that represents the "many" end of a relationship must be defined as ICollection<T>
Entity Framework represents many-to-many relationships by not having entityset for the joining table in CSDL, instead it manages this through mapping.
Note: Entity framework supports many-to-many relationship only when the joining table does NOT include any columns other than PKs of both the tables
you should have to define navigation property yourself to coupe with this proplem.
this link can be of your help.

EF 6.1 Error Trying to Establish Recursive Relationship - Self-Referencing Table and FluentAPI

I'm having quite the issue trying to create a recursive relationship in Entity Framework 6.1.
I'll need some different types of Categories, but have created a "base" abstract class for all of them. I'm using Table-Per-Type hierarchical strategy. A Category may or may not have a ParentCategory. (If not, then it's a top-level category)
In the code below, I'm showing how I've created the abstract Category class and the ParentCategory and ChildCategories navigation properties. I've made the ParentCategoryId nullable, as it's not required in the case of a top-level category. I've seen a few posts that are exactly what I'm trying to achieve here, and although I've think I have all answers addressed, I'm still getting the following error:
Category_ParentCategory: : Multiplicity conflicts with the referential constraint in Role 'Category_ParentCategory_Target' in relationship 'Category_ParentCategory'. Because all of the properties in the Dependent Role are non-nullable, multiplicity of the Principal Role must be '1'.
I'm wondering if it has something to do with Category being an abstract class and the added inheritance model I'm using as I haven't seen that exact usage in any of the other posts regarding this type of recursive relationship. Any help is appreciated!
public abstract class Category : ICategory
{
protected Category()
{
ChildCategories = new HashSet<Category>();
}
public int CategoryId { get; private set; }
public int? ParentCategoryId { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual ICollection<Category> ChildCategories { get; set; }
}
public class WLCategory : Category, IWLCategory
{
public WLCategory() : base()
{
DynamicFields = new HashSet<DynamicFieldDef>();
}
public virtual ICollection<DynamicFieldDef> DynamicFields { get; set; }
}
Using FluentAPI, I've configured the database creation as such:
class CategoriesConfig : EntityTypeConfiguration<Category>
{
public CategoriesConfig()
{
HasOptional(p => p.ParentCategory).WithMany(p => p.ChildCategories)
.HasForeignKey(p => p.ParentCategoryId);
ToTable("Categories");
}
}
class WLCategoriesConfig : EntityTypeConfiguration<WLCategory>
{
public WLCategoriesConfig()
{
HasKey(p => p.CategoryId);
ToTable("WLCategories");
}
}
Ok, found the issue! In my OnModelCreating method, I had setup some global configurations, one of which was:
modelBuilder.Properties().Configure(p => p.IsRequired());
I wanted to set all properties to being non-nullable by default unless I explicitly overrode properties on a class-by-class basis. I didn't, however, think this global setting would apply to a datatype of:
int? (nullable int)
Apparently, I was wrong. (Not sure why EF would try to make a nullable int non-nullable in the DB even if the developer set IsRequired globally. The inherent meaning of int? is "nullable".) I had to explicitly add the following line of code to my CategoriesConfig class before the HasOptional statement:
Property(p => p.ParentCategoryId).IsOptional();
Now, all is well. :-)

EF6 - TPH foreign key mapping in derived classes using base class property

I am using Entity Framework 6.0.2 with an existing database in which tags are stored in a single table that looks like this:
Id: int, primary key
TagType: string, determine the type of tag, either "usertag" or "movietag"
ItemId: int, contains the Id of the item to which is referred (either a User Id or a Movie Id)
The following classes describe this situation:
public class User
{
public int Id { get; set; }
}
public class Movie
{
public int Id { get; set; }
}
public abstract class Tag
{
public int Id { get; set; }
public int ItemId { get; set; }
}
public class UserTag : Tag
{
public virtual User User { get; set; }
}
public class MovieTag : Tag
{
public virtual Movie Movie { get; set; }
}
As you can see my derived classes have navigation properties, which are backed by the value of the ItemId property in the base class. My mapping is as follows:
public class Context : DbContext
{
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>()
.Map<UserTag>(m => m.Requires("TagType").HasValue("usertag"))
.Map<MovieTag>(m => m.Requires("TagType").HasValue("movietag"));
modelBuilder.Entity<UserTag>()
.HasRequired(m => m.User).WithMany().HasForeignKey(m => m.ItemId);
modelBuilder.Entity<MovieTag>()
.HasRequired(m => m.Movie).WithMany().HasForeignKey(m => m.ItemId);
}
}
Now when I try to use this mapping using the following code, I get an exception:
using System.Data.Entity;
class Program
{
static void Main()
{
using (var db = new Context())
{
db.Database.Delete();
db.Database.Initialize(false);
}
}
}
The exception that is thrown is:
Unhandled Exception: System.InvalidOperationException: The foreign key component 'ItemId' is not a declared property on type 'UserTag'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property
Yes the ItemId property is not declared on the type UserTag, but it is inherited from the base Tag class. To me it seems that this mapping should be possible. Is this a bug or a restriction in Entity Framework 6?
It is a restriction. EF is quite tightly bound to the way how relational database works. What you are trying to do in terms of the database is to put two foreign key constraints on single ItemId column. The foreign constraint in database is not conditional so the record will always use both constraints no matter of the tag type. That is not what you want because such definition will always require both user and movie with specific Id to exist for every single tag.
Think about it in different way. If it works the way how you are trying to define it there would be no reason why to have User and Movie navigation properties in child entities - it would be enough to have single navigation property in parent. The fact that you have to define them in child entities because they are different for each of them also means you need to have two different foreign keys.
You need to have separate UserId and MovieId in their specific tags.

Categories

Resources