I'm trying to apply mapping to navigation property but I got an error. I have two classes that contain a navigation property One-To-Many.
public class Contractor : Entity<string>
{
public virtual Collection<DocsVat> DocsVats { get; set; }
}
public class DocsVat : Entity<Guid>
{
public Contractor Contractor { get; set; }
}
The configuration is :
public class DocsVatEntityConfiguration : EntityTypeConfigurationBase<DocsVat>
{
protected override void ConfigureEntity()
{
Builder.ToTable("DocsVat", "VAT");
Builder.HasKey(p => p.Id);
Builder.HasOne(p => p.Contractor).WithMany(p=>p.DocsVats).HasForeignKey(p =>
p.ContractorNo).IsRequired();
Builder.Property(p => p.Id).HasColumnName("Id").IsRequired();
}
}
and Mapping profile:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Entities.DocsVat, DocsVatDto>();
CreateMap<DocsVatDto, Entities.DocsVat>();
CreateMap<FnPurchaseDetail, DocsVatDto>();
CreateMap<Entities.DocsVat, DocsVatDto>().ForMember(dto => dto.Contractor, conf =>
conf.MapFrom(ent => ent.Contractor.Id));
}
}
The error I've got:
Error mapping types.
Mapping types:
DocsVatDto -> DocsVat
###.ApplicationServices.Doc.DocsVAT.Dto.DocsVatDto -> ###.Entities.DocsVat
Type Map configuration:
DocsVatDto -> DocsVat
###.ApplicationServices.Doc.DocsVAT.Dto.DocsVatDto -> ###.Entities.DocsVat
Destination Member:
Contractor
How can I fix it?
The error seems to be with Automapper configuration, not entity framework. You would need to post a more detailed error message, but apparently Automapper is trying to map the property Contractor of DocsVat but it can't find a mapping configuration for it.
Either ignore that property when configuring the mapping of DocsVatDto => DocsVat or create a map for converting a ContractorDto => Contractor
Related
I am trying to use Entity Framework Core / .NET 5 to interact with my databases.
When I try to query DbContent.UserClaims I get the following error:
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'UserId1'.
I am not sure where UserId1 us coming from. I have a property called UserId which is the foreign key. Here are the relation mapping
Here is what I tried to do in the DbContext class
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<User>(user =>
{
user.HasKey(r => r.Id);
user.HasMany(x => x.UserRoles).WithOne().HasForeignKey(x => x.UserId);
user.HasMany(x => x.UserClaims).WithOne().HasForeignKey(x => x.UserId);
user.HasMany(x => x.UserTokens).WithOne().HasForeignKey(x => x.UserId);
});
builder.Entity<UserClaim>(userClaim =>
{
userClaim.HasKey(r => r.Id);
userClaim.HasOne(r => r.User).WithOne().HasForeignKey<UserClaim>(x => x.UserId);
});
}
Here is the UserClaim class which is derived from IdentityUserClaim
public class UserClaim : IdentityUserClaim<string>
{
public virtual User User { get; set; }
}
Here is the User class which is derived from IdentityUser
public class User : IdentityUser<string>
{
public virtual ICollection<UserToken> UserTokens { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
public virtual ICollection<UserClaim> UserClaims { get; set; }
}
Here is the query that EF5 is generating
SELECT [u].[Id], [u].[ClaimType], [u].[ClaimValue],[u].[UserId], [u].[UserId1]
FROM [UserClaims] AS [u]
How can I fix this issue in Entity Framework Core?
You're using shadow properties here, and on top of that, trying to add UserId foreign key to the User itself. Since UserId is an already defined property in that class, it's adding a suffix to the property name every time you're trying to add a foreign key in the user table by the same name.
It should be something like this:
modelBuilder.Entity<UserClaim>()
.Property<int>("UserForeignKey");
modelBuilder.Entity<UserClaim>()
.HasOne(a => a.User)
.WithMany(b => b.UserClaims)
.HasForeignKey("UserForeignKey")
Read the documentation on how to configure Fluent API for shadow properties, and some other ways to use the Fluent API.
I am trying to get a single User, with a list of Items, mapped with a many-to-many entity UserItems. However, I am unable to retrieve the mapped Items due to to an error that I'm unable to solve (error at bottom of question). Here is my code:
public class User
{
public int Id { get; set; }
public ICollection<UserItem> UserItems { get; set; }
}
public class Item
{
public int Id { get; set; }
public ICollection<UserItem> UserItems { get; set; }
}
public class UserItem
{
public int Id { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public int ItemId { get; set; }
public Item Item { get; set; }
public int Quantity { get; set; }
}
The UserItem class configuration has the following relationships defined:
builder.HasOne(x => x.User)
.WithMany(x => x.UserItems)
.HasForeignKey(x => x.UserId)
.OnDelete(DeleteBehavior.ClientCascade);
builder.HasOne(x => x.Item)
.WithMany(x => x.UserItems)
.HasForeignKey(x => x.ItemId)
.OnDelete(DeleteBehavior.ClientCascade);
I have the following generic repo with this method:
public class GenericRepository<T> : where T : class
{
private readonly DbContext _context;
public GenericRepository(DbContext context) => _context = context;
public T Get(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] navigationProperties)
{
IQueryable<T> query = _context.Set<T>();
query = navigationProperties.Aggregate(query, (current, property) => current.Include(property));
var entity = query.FirstOrDefault(where);
return entity;
}
}
However, when I try to run the code, I get an error on the Select(x => x.Item):
var user = repo.Get(x => x.Id == 1, x => x.UserItems.Select(y => y.Item));
Error:
System.InvalidOperationException: 'The expression 'x.UserItems.AsQueryable().Select(y => y.Item)' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.'
What am I doing wrong, this seems to work for my other projects?
This error Occurs because you are not passing in a navigation property (x.UserItems would be a navigation property) but rather something you want to do with the navigation property. UserItems.Select(y => y.Item) is not a property of x because Select() is a function and therefore it cannot be included.
What you are trying to do (I assume it is including UserItems and also the corresponding Items) is not going to work with your current implementation of the repository. To include navigation properties of navigation properties .ThenInclude() must be used instead of .Include() which works only for navigation properties directly defined on the Entity the DbSet is created for.
But apart from your question I would suggest not to use such an generic implementation of Repository. The main benefit from using reposiories is to separarte code related to loading and storing of entities from the rest of your code. In your case if the consumer of repository knows that navigation properties must be included and that he has to provide them - then what is the point of having a repository at all? Then the consumer again cares about database specific code which makes having a repository unneccessary. I would recommend just making a conrete "UserRepository" which can only be used to retrieve users and explicitly includes the needed properties.
I am extending IdentityUser, IdentityUserRole, and IdentityRole like this:
public class ApplicationUser : IdentityUser
{
public string FullName { get; set; }
public virtual ICollection<ApplicationIdentityUserRole> Roles { get; } = new List<ApplicationIdentityUserRole>();
}
public class ApplicationIdentityUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
public class ApplicationRole : IdentityRole
{
public virtual ICollection<ApplicationIdentityUserRole> Roles { get; } = new List<ApplicationIdentityUserRole>();
}
and configured like:
public class SmartAccountingSetUpContext : IdentityDbContext<ApplicationUser>
{
public SmartAccountingSetUpContext(DbContextOptions<SmartAccountingSetUpContext> options)
: base(options)
{
}
public DbSet<ApplicationUser> Users { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Ignore<RegistrationViewModel>();
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<ApplicationUser>().ToTable("AspNetUsers");
builder.Entity<ApplicationIdentityUserRole>().ToTable("AspNetUserRoles");
builder.Entity<ApplicationRole>().ToTable("AspNetRoles");
builder.Entity<ApplicationIdentityUserRole>()
.HasOne(p => p.User)
.WithMany(b => b.Roles)
.HasForeignKey(p => p.UserId);
builder.Entity<ApplicationIdentityUserRole>()
.HasOne(x => x.Role)
.WithMany(x => x.Roles)
.HasForeignKey(p => p.RoleId);
}
}
I keep getting this:
"Invalid column name 'Discriminator'.\r\nInvalid column name 'Discriminator'.\r\nInvalid column name 'Discriminator'.\r\nInvalid column name 'Discriminator'."
I understand if you have derived class, then you have to specify the HasDiscriminitor in OnModelCreating method. However IdentityUser, IdentityUserRole, and IdentityRole are no abstract classes.
How can I get past this?
Your context is inheriting IdentityDbContext<TUser> which in turn inherits IdentityDbContext<TUser, IdentityRole, string>. TUser in this case is your ApplicationUser, but the role type is IdentityRole.
Thus the base class fluent configuration registers IdentityRole as entity. When you register the derived ApplicationRole as entity, EF Core treats that as TPH (Table Per Hierarchy) Inheritance Strategy which is implemented with single table having Discriminator column.
To fix the issue, simply use the proper base generic IdentityDbContext. Since you also have a custom IdentityUserRole derived type, you should use the one with all generic type arguments - IdentityDbContext<TUser,TRole,TKey,TUserClaim,TUserRole,TUserLogin,TRoleClaim,TUserToken>:
public class SmartAccountingSetUpContext : IdentityDbContext
<
ApplicationUser, // TUser
ApplicationRole, // TRole
string, // TKey
IdentityUserClaim<string>, // TUserClaim
ApplicationIdentityUserRole, // TUserRole,
IdentityUserLogin<string>, // TUserLogin
IdentityRoleClaim<string>, // TRoleClaim
IdentityUserToken<string> // TUserToken
>
{
// ...
}
I are using TPT inheritance and the relationships of my entities is below:
DepartmentCatalog derived form base class catalog and contain another entity Department
My propurse is getting the name of DepartmentCatalog from Deparment not base class catalog and the name should be readonly. So I override the name like this:
using System;
using Rednoble.CMS.Domain.CatalogsEntity;
using Rednoble.CMS.Domain.ExpertsEntity;
using System.ComponentModel.DataAnnotations.Schema;
using Rednoble.CMS.Domain.DepartmentEntity;
namespace Rednoble.CMS.Domain.CatalogsEntity
{
[Table("DepartmentCatalog")]
public class DepartmentCatalog : Catalog
{
public DepartmentCatalog()
: base()
{
}
public Guid DepartmentId { get; set; }
public Department Department { get; set; }
public override string Name
{
get
{
return Department.Name;
}
}
}
}
But when I getting deparmentcatalog from context I got a not null exception because EF getting the name of DepartmentCatalog before setting the complex property - "Department".
I'm not sure it's the illegal design or it's the illegal using of TPT inheritance. How to get my work be done, can anyone help me?
BTW, I'm using VS2012 and EF 6.0.
** Update ***
I have tried Eagerly Loading and Explicitly Loading for hours:
https://msdn.microsoft.com/en-us/data/jj574232.aspx
var deptCatas = Context.Set<DepartmentCatalog>().Include(dc => dc.Department).Where(predicate);
deptCatas.Load();//throw not null exception
var departmentCatalogs = _departmentCatalogReposiory.FindBy(r => r.DepartmentId == id);
departmentCatalogs.FirstOrDefault();//throw not null exception
Here is my mapping:
public class CategoryMapping : EntityTypeConfiguration<Catalog>
{
public CategoryMapping()
{
Property(r => r.Name).IsRequired().HasMaxLength(150);
Property(r => r.PageUrl).HasMaxLength(500);
Property(r => r.EnglishName).HasMaxLength(100);
Property(r => r.CoverPictrue).HasMaxLength(500);
HasMany(r => r.Rala_Catalog_Articles).WithRequired(r => r.Catalog).HasForeignKey(r => r.CatalogId);
HasOptional(c => c.ViewTemplate).WithMany().HasForeignKey(r=>r.TemplateId).WillCascadeOnDelete(false);
}
}
public class DepartmentCategoryMapping
{
public DepartmentCategoryMapping()
{
HasRequired(dc => dc.Department).WithMany(d => d.DepartmentCatalogs).HasForeignKey(dc => dc.DepartmentId).WillCascadeOnDelete(true);
}
}
I've found out a way to solve this problem, just check the if Department is null when first time Call "FirstOrDefault" or "Load" for the property of name, if Department is null, return the name of base catalog. But I thought this is not good enough. one of the defect is when the name of department changed, DepartmentCatalog might get old value. Here is my code:
[Table("DepartmentCatalog")]
public class DepartmentCatalog : Catalog
{
public DepartmentCatalog()
: base()
{
}
public Guid DepartmentId { get; set; }
public Department Department { get; set; }
public override string Name
{
get
{
if (Department == null)
{
return base.Name;
}
else
{
return Department.Name;
}
}
}
}
because you didn't load Department.
You should Include Department in your query or use Lazy Loading.
** Update ***
for eager Loaing you can use same this code:
MyContext.DepartmentCatalogs.Include(x=> x.Department).ToList()
and for Lazy Loading you have to use virtual keyword for Department property.
BTW, for lazy loading, lazy loading and proxy must be true in your configuration.
**** EDIT ****
[ForeignKey("DepartmentId")]
public Department Department {get;set}
**** Update ****
context.DepartmentCatalogs.AsNoTracking().Include(x=>x.Department).FirstOrDefault();
the magic key to solving this problem is AsNoTracking.
this may seem very easy to solve, but the complication I'm having is that I have a table per hierarchy to store all of the entities and I'm not able to create the relationships over the same table. Here is what I have in the DB and classes:
I have just one table named BaseObject with an ID, name and type. I will create two classes for those entities stored in there. Master and Component. The type column is the discriminator. I have another table to store the relationships between both: A master can have many components and a component also can have many other components.
This is the code I have for the classes:
public partial class BaseObject
{
public BaseObject()
{
}
public System.Guid ID { get; set; }
public string Name { get; set; }
public Nullable<int> Type { get; set; }
}
public class MasterObject : BaseObject
{
public virtual ICollection<ComponentObject> Components { get; set; }
public MasterObject()
{
this.Components = new List<ComponentObject>();
}
}
public class ComponentObject : BaseObject
{
public virtual ICollection<MasterObject> MasterObjects { get; set; }
public ComponentObject()
{
this.MasterObjects = new List<MasterObject>();
}
}
And these are the mappings:
public class BaseObjectMap : EntityTypeConfiguration<BaseObject>
{
public BaseObjectMap()
{
// Primary Key
this.HasKey(t => t.ID);
// Table & Column Mappings
this.ToTable("BaseObject");
this.Property(t => t.ID).HasColumnName("ID");
this.Property(t => t.Name).HasColumnName("Name");
//configure the inheritance in here
this.Map<MasterObject>(m => m.Requires("Type").HasValue(1));
this.Map<ComponentObject>(m => m.Requires("Type").HasValue(2));
}
}
public class MasterObjectMap : EntityTypeConfiguration<MasterObject>
{
public MasterObjectMap()
{
this.HasMany<ComponentObject>(t => t.Components)
.WithMany(t => t.MasterObjects)
.Map(c =>
{
c.ToTable("ObjectComponents");
c.MapLeftKey("ComponentObjectID");
c.MapRightKey("BaseObjectID");
});
}
}
public class ComponentObjectMap : EntityTypeConfiguration<ComponentObject>
{
public ComponentObjectMap()
{
this.HasMany<MasterObject>(t => t.MasterObjects)
.WithMany(t => t.Components)
.Map(m =>
{
m.ToTable("ObjectComponents");
m.MapLeftKey("BaseObjectID");
m.MapRightKey("ComponentObjectID");
});
}
}
The thing is that when quering the DB, I can get a Master by accessing the DBSet Masters, but the Component collection always gives a non-sense exception saying that "Problem in mapping fragments starting at line 6:Condition member 'BaseObject.Type' with a condition other than 'IsNull=False' is mapped. Either remove the condition on BaseObject.Type or remove it from the mapping."
I don't understand what's happening. Of course if the classes where pointing each to a table, this would be very easy, but I suspect that is the root of my problem.
Also, I'm just starting with EF. I wanted to create the classes based on my existing DB that I wouldn't like to modify at all. Unless it's really needed. Please guide me if what I'm trying to do is right or wrong, or what should I do first to fully implement EF on this project that is currently using NHibernate.
Any help here? Thanks
Finally I found the issue thanks to this answer: EF4.1 Exception creating Database with table-per-hierarchy inheritance
The problem is in the mapping of the Base class. When using a base class and a discriminator to get the children classes, the discriminator MUST NOT BE a property in neither instance. Just removed the discriminator as property and it worked fine. In my example, column 'Type' is the discriminator.
Hope this helps to someone else.