I've got this question that's been bugging me and my vast and unfathomable intellect just can't grasp it. The case: I want to make multiple one-to-many relationships to the same entity using the fluent nhibernate automapper and export schema.
I have:
Base Class:
public abstract class Post<T> : Entity, IVotable, IHierarchy<T>
{
public virtual string Name
{
get; set;
}
public virtual string BodyText
{
get; set;
}
public virtual Member Author
{
get; set;
}
}
and Inheriting Class:
[Serializable]
public class WallPost : Post<WallPost>
{
public virtual Member Receiver
{
get; set;
}
}
The 'Member' properties of WallPost is a foreign key relationship to this class:
public class Member : Entity
{
public Member()
{
WallPosts = new IList<WallPost>();
}
public virtual IList<WallPost> WallPosts
{
get; set;
}
}
I hope you're with me until now. When I run the exportschema of nhibernate I expect to get a table wallpost with 'author_id' and 'receiver_id' BUT I get author_id, receiver_id,member_id. Why did the Nhibernate framework add member_id, if it's for the collection of posts (IList) then how do you specify that the foregin key relationship it should use to populate is receiver, i.e. member.WallPosts will return all the wallposts of the receiver.
I hope i made sense, if you need anything else to answer the question let me know and I'll try to provide.
Thanks in advance
P.s. If I change the property name from 'Receiver' to 'Member' i.e. public virtual Member Member, only two associations are made instead of 3, author_id and member_id.
E
THE SOLUTION TO THIS QUESTION FOR ANYONE ELSE WONDERING IS TO ADD AN OVERRIDE:
mapping.HasMany(x => x.WallPosts).KeyColumn("Receiver_id");
Most likely (I don't use auto mapping, I perfer to write my own classmaps) it's because the auto mapping assuming that your Member's "WallPosts" is controled by the wall posts. So it creates a member_id for that relationship.
Edit: Try adding an override in your fluent config, after AutoMap add:
.Override<Member>(map =>
{
map.HasMany(x => x.WallPosts, "receiver_id")
.Inverse;
});
Related
I'm getting started with ASP.NET Core and EF Core 6. I'm trying to build a simple web api whith two models and an 1-n relationship between the models.
Model A:
public class ModelA
{
public Guid Id { get; set; }
public string StringProperty { get; set; }
public ICollection<ModelB> ModelBs { get; set; }
}
Model B:
public class ModelB
{
public Guid Id { get; set; }
public string StringProperty { get; set; }
public Guid ModelAId { get; set; }
public ModelA ModelA { get; set; }
}
When I try to create a ModelB by using the POST-endpoint of the ModelB-Controller, it expects me to pass the ModelA as well. If I do provide it, I get a duplicate key error because EF tries to create a new ModelA in the database, which causes a duplicate key error.
I must only use the ModelB-model as parameter for the post method and explicitly must not use any kind of intermediate model.
I would like to only use the ModelA id, not the entire ModelA object:
//Desired post-request
{
"stringProperty": "value",
"modelAId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
Making the ModelA reference nullable allows for post-requests as described above:
public class ModelB
{
public Guid Id { get; set; }
public string StringProperty { get; set; }
public Guid ModelAId { get; set; }
public ModelA? ModelA { get; set; }
}
But that feels wrong since an instance of ModelB must not exist without a reference to a ModelA instance.
Is there any way to achieve this without using DTOs or making the reference nullable?
I'm probably missing something trivial here.
I think you should star to use DTOs on your project! For example, model B would have only the data necessary to create the item, i.e StringProperty and ModelAId, and inside of your controller, you would associate with the existing ModelA.
You can have a look on the entity framework on the link below.
https://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx
Even if you create a DTO(highly recommended, passing a whole another object just to show relationship is a waste of bandwidth and bad practice ) or not you will accept ModelAId from the user, not the whole object anyway so.
Edit:
if you want to trick the product owner. Just create a base class without ModelA prop and all the rest props in ModelB now make ModelB inherit this and add ModelA explicitly. Now create ModelBPost also inheriting from this and use this as a parameter for POST data this way the product owner knows the fields are exactly the same and you pass the verification error.
Old Answer
How about you get the model A fresh from DB and assign that to ModelB
like
public IAction PostModelB(ModelB modelB)
{
modelB.ModelA = context.ModelAs.First(x => x.Id == modelB.ModelAId);
//now since efcore is tracking this it know this object already exists
context.ModelBs.Add(modelB);
}
However, sometimes EFCore screws up and you get an already tracked error(which is quite unfortunate after so much time it still can't do it properly). If this happens(which it might since you are persistent on not using a DTO and accept a whole object just for relationship) you will have to set the navigation property null and only use the ModelAId property to insert the new record or you can get the instance which efcore holds:
var modelA = context.ModelAs.First(x => x.Id == modelB.ModelAId);
var trackedinstance = context.ChangeTracker.Entry(modelA)?.Entity ?? modelA;
modelB.ModelA = trackedinstance;
modelB.ModelAId = modelA.Id;
I'm trying to share common properties with multiple entities by using multiple levels of inheritance, but I'm running into an error.
Cannot create a relationship between 'User.SupersCreated' and 'Super.CreatedBy' because a relationship already exists between 'User.BasicsCreated' and 'Basic.CreatedBy'. Navigation properties can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'Super.CreatedBy' first in 'OnModelCreating'.
The structure of my models is as follows.
public class EntityBase
{
public Guid Id { get; set; }
public Guid CreatedById { get; set; }
public User CreatedBy { get; set; }
}
public class Basic: EntityBase
{
public string BasicProperty { get; set; }
}
public class Super : Basic
{
public string SuperProperty { get; set; }
}
public class User : IdentityUser<Guid>
{
public ICollection<Basic> BasicsCreated { get; set; }
public ICollection<Super> SupersCreated { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasMany(x => x.BasicsCreated)
.WithOne(x => x.CreatedBy);
modelBuilder.Entity<User>()
.HasMany(x => x.SupersCreated)
.WithOne(x => x.CreatedBy);
}
The problem seems to be a result of Super Inheriting from Basic, or at least, the problem goes away when I remove this level of inheritance and make Super inherit from EntityBase (however than I'll lose the properties that exist in Basic).
Can anyone please help me understand why I'm getting this error and what should be done to fix it?
Edit
After considering this some more, I think I'm trying to abuse inheritance to do what it's not intended to do.
The database structure I was hoping to end up with, is:
Even though my Basic and Super tables share the same properties, with Super having it's own additional properties, there's no relationship between Basic data and Super data.
From having a look at Microsoft's tutorial on implementing inheritance, there's two options:
Table per type
Table per hierarchy
Neither of these are what I'm trying to achieve.
Perhaps I should be using interfaces to define the common properties that exist between unrelated entities. It seems like I need to back and re-evaluate my design anyway.
If some of the base classes of the entity is identified as entity (as with your Super and Basic), by default EF Core will try to use one of the database inheritance strategies.
If you don't want that (want to treat is just like non entity base class), then you have to configure that explicitly at the very beginning of the OnModelCreating, e.g. for your sample
modelBuilder.Entity<Super>().HasBaseType((Type)null);
or more generally using a loop similar to this
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
entityType.BaseType = null;
and then define explicitly the entity hierarchy if and where needed.
I have a basic table with a few FK references. So when I retrieve an entity for an update operation; that entity contains ICollections of related entites. My main viewModel contains Lists which correspond to these ICollections. However, since some other models represent 1-1 mapping, I have object instead of List. But inside the Entity they continue to be represented as ICollections.
This is giving me some problems when trying to map between viewModel and Entity. I am using Automapper for the mapping. I have
mapper.Map(viewModel, entity);
Currently I am leaving out the problematic models from this mapping and adding them separately. Is there a way to handle everything in one mapping? Is there a way to deal with the ICollections which ideally should be a single object?
EDIT
public class MainViewModel
{
public EntityVM1 vm1 { get; set; }
public List<EntityVM2> vm2 { get; set; }
public List<EntityVM3> vm3 { get; set; }
}
public class MainEntity
{
... some scalar props...
public virtual ICollection<Entity1> e1 { get; set; }
public virtual ICollection<Entity2> e2 { get; set; }
public virtual ICollection<Entity3> e3 { get; set; }
}
Entity1 and EntityVM1 are causing the problem.
Thanks
You can always override the default mapping system in the mapping config of AutoMapper, you should have a peek at the runtime polymorphism in the mapping inheritance section of the documentation.
If what you want on the entity is a straight object, why not take Automapper out of the equation and just force EF to map it using a one to one system... i.e
modelBuilder.Entity<MainEntity>()
.HasOne(p => p.Entity1)
.WithOne(i => i.MainEntity)
.HasForeignKey<Entity1>(b => b.MainEntityForignKey);
HAve a peek at the EF docs, under section one-to-one for more info
I've created a custom user inheriting from IdentityUser called Contacts, and my applications dbcontext inherits from IdentityDbContext like so:
public class Contact : IdentityUser<int, ContactLogin, ContactRole, ContactClaim>
{
public Contact()
{
}
}
public class dbcontext : IdentityDbContext<Contact, Role, int, ContactLogin, ContactRole, ContactClaim>
{
public dbcontext()
: base("dbcontext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// IdentityDbContext base - must be called prior to changing identity configuration
base.OnModelCreating(modelBuilder);
// custom identity table names and primary key column Id names
modelBuilder.Entity<Contact>().ToTable("Contacts").Property(p => p.Id).HasColumnName("ContactId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ContactRole>().ToTable("ContactRoles");
modelBuilder.Entity<ContactLogin>().ToTable("ContactLogins");
modelBuilder.Entity<ContactClaim>().ToTable("ContactClaims").Property(p => p.Id).HasColumnName("ContactClaimId");
modelBuilder.Entity<Role>().ToTable("Roles").Property(p => p.Id).HasColumnName("RoleId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
By default IdentityDbContext contains a Users DbSet. Is it possible to change the name of this DbSet to match the type that it's implementing, e.g Contacts?
It's not a big deal, but it would just be nice to refer to the DbSet using dbcontext.Contacts instead of dbcontext.Users.
Thanks.
The base IdentityDbContext uses: public virtual IDbSet<TUser> Users { get; set; } to expose the Users DbSet.
You'll need a similar property for your own implementation, e.g: public IDbSet<Contacts> Contacts { get; set; }
Update
Question was regarding renaming the existing DbSet of Contacts from Users to Contacts.
No, you can't do this out of the box. You could attempt to wrap it and expose it again, but this isn't really the right thing to do. See this question for an in depth discussion.
Just a note that if you decide to overwrite anything or add your own, the default EF implementation of UserStore will use the DbSet named Users. Just something to keep an eye on if you get unexpected behavior.
Generally what I tend to do is have a big separation of concerns right.
So I have:
public IDbSet<User> Users { get; set; }
This represents anyone who wants to log into my system. So now I want to model actual concepts into my database, concepts that relate to real world things. So I have a system administrator for example, I will create an entity for this.
public class SystemAdministrator
{
public string Name { get; set; }
public int LocationId { get; set; } // a complex representation of where this administrator works from
public int UserId { get; set; } // this is now a reference to their log in
}
Now my context will look like this:
public IDbSet<User> Users { get; set; }
public DbSet<SystemAdministrator> SystemAdministrators { get; set; } // I use DbSet because it exposes more methods to use like AddRange.
This means now my database has proper representations of real world concepts which is easy for everyone to develop against. I do the same for Clients or Employees.
This also means that I can move away from primitive obsession
This question already has an answer here:
How to map inherited entities in EF code-first
(1 answer)
Closed 2 years ago.
I searched the threads on here and found multiple similar posts but no solutions
Assume I have a User table in my db that I've mapped to a simple User entity
public class User{
public int UserId {get;set;}
public string Username {get;set;}
}
I want to create a new class that will encapsulate an ExternalUser which has all the same fields as User but adds a few more fields. The fields for my ExternalUser will be populated from a view in the db that pulls in both the fields from User and the additional fields required for ExternalUser
public class ExternalUser : User{
public int SomeExternalId{get;set;};
public string SomeExternalProp{get;set;};
}
but no matter how I seem to define my mappings for this new object I get the following error:
The property 'UserId' is not a declared property on type 'ExternalUser'. Verify that the property has not been explicitly excluded from the model by using the Ignore method or NotMappedAttribute data annotation. Make sure that it is a valid primitive property.
Can someone share the correct way to map this. Its stuff like this that makes me hate EF, simply inheriting a POCO shouldn't cause it to blow up, especially not when all the fields exist in the underlying view that I'm pointing to. Much thanks!
I am not sure about your use case but if you have the option to make User an abstract base class, then you can use the Table per Concrete Type approach.
You would need to make User an abstract class and call MapInheritedProperties() when creating the model mappings for ExternalUser:
public abstract class User
{
public int UserId { get; set; }
public string Username { get; set; }
}
[Table("ExternalUser")]
public class ExternalUser : User
{
public int SomeExternalId { get; set; }
public string SomeExternalProp { get; set; }
}
Note that I am using [Table] attribute to map the entity name to table name (you can also do this in OnModelCreating method but I find it cleaner to use the [Table] attribute):
And this is OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
Database.SetInitializer<ApplicationDbContext>(new CreateDatabaseIfNotExists<ApplicationDbContext>());
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
// I believe the inherited properties are mapped by default
modelBuilder.Entity<ExternalUser>().Map(m =>
{
m.MapInheritedProperties();
});
}
Note that I am removing PluralizingTableNameConvention as I don't want plural table names.