Mapping not working? - c#

I have a weird situtation, i have 1 master class called User, this is an abstract class. It cointains 3 child classes (Student, Promotor and BPC) who always inherit from User.
I am using the TPH (Table per Hierarchy) structure to map this to the database, the Discriminator field is just the name of the class.
a Student has a relationship with Promotor. It can contain 0 or 1 promotor. With the initializer i make sure that a Student has the corresponding promotor. (it shows correctly in my database after launch)
When i try to use the modelbinder to get the Student from the logged in user it returns the correct student but it says it doesnt contain a promotor.
Class User:
public abstract class User
{
//TPH is default
public int Id { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String Username { get; set; }
public String Email { get; set; }
public String Salt { get; set;}
public String Password { get; set; }
public DateTime LastLogin { get; set; }
public String LastIp { get; set; }
public DateTime LastPasswordChangedDate { get; set; }
public DateTime CreationDate { get; set; }
[NotMapped]
public String PlainPassword { get; set; }
}
Class Student
public class Student : User
{
public virtual Promotor Promotor { get; set; }
public virtual CoPromotor CoPromotor { get; set; }
public virtual ICollection<Suggestion> Suggestions { get; set; }
public Student()
{
Suggestions = new Collection<Suggestion>();
}
}
Class Promotor
public class Promotor : User
{
public virtual ICollection<Student> Students { get; set; }
public List<User> users;
private String message;
public Promotor()
{
Students = new Collection<Student>();
users = new List<User>();
}
}
User model binder
public class UserModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext.HttpContext.User.Identity.IsAuthenticated)
{
IUserRepository repos =
(IUserRepository) DependencyResolver.Current.GetService(typeof (IUserRepository));
return repos.FindByUsername(controllerContext.HttpContext.User.Identity.Name);
}
return null;
}
}
Action in the controller
[HttpPost]
public ActionResult Create(CreateViewModel model, User user, string btnSaveSend)
{
var student = (Student) user; //here the attribute Promotor is NULL
}
User mapper:
public class UserMapper : EntityTypeConfiguration<User>
{
public UserMapper()
{
ToTable("user");
// Primary key
HasKey(u => u.Id);
// Properties
Property(u => u.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(u => u.FirstName).IsRequired();
Property(u => u.LastName).IsRequired();
Property(u => u.Email).IsRequired();
Property(u => u.Salt).IsRequired();
Property(u => u.Username).IsRequired();
Property(u => u.Password).IsRequired();
Property(u => u.LastLogin);
Property(u => u.LastIp);
Property(u => u.CreationDate).IsRequired();
Property(u => u.LastPasswordChangedDate).IsRequired();
}
}
Student Mapper
public class StudentMapper : EntityTypeConfiguration<Student>
{
public StudentMapper()
{
HasMany(s => s.Suggestions).WithRequired(s => s.Student);
HasRequired(s => s.Promotor).WithMany(s => s.Students);
}
}
UserRepository:
public User FindByUsername(string name)
{
return users.FirstOrDefault(u => u.Username.ToLower() == name.ToLower());
}

Related

The corresponding CLR type for entity type cannot be instantiated Table-per-type

Using EFCore 5
I've been trying to follow Microsoft tutorials to create a field in a model class that may reference two other classes.
In my case, I have an abstract Credentials class and two child classes: ClientCredentials and AccessKeyCredentials. There's an Organization class that holds a reference to Credentials and can use any of the child classes to instantiate the object.
I want so that there's a choice of what type of credentials to use.
However, I've came across an error:
System.InvalidOperationException: The corresponding CLR type for entity type 'Credentials' cannot be instantiated, and there is no derived entity type in the model that corresponds to a concrete CLR type.
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateClrInheritance(IModel model, IEntityType entityType, HashSet`1 validEntityTypes)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateClrInheritance(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.SqlServer.Internal.SqlServerModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.ValidatingConvention.ProcessModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IModel model)
at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
...
It's not really clear for me what is the issue here. I've been trying to follow a few documentation pages but didn't succeed.
Just in case, the links are:
https://learn.microsoft.com/en-us/ef/core/modeling/inheritance
https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt
Here is my code sample:
Credentials.cs
public abstract class Credentials
{
public Credentials()
{
this.CreatedOn = DateTime.Now;
}
[Key]
public Guid Id { get; set; }
public int TypeId { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
[ForeignKey("TypeId")]
public virtual CredentialsType Type { get; set; }
}
ClientCredentials.cs
public class ClientCredentials : Credentials
{
[Required]
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
AccessKeyCredentials.cs
public class AccessKeyCredentials : Credentials
{
[Required]
public string Username { get; set; }
public string AccessKey { get; set; }
}
Organization.cs
public class Organization
{
public Organization()
{
this.CreatedOn = DateTime.Now;
}
[Key]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public string Country { get; set; }
public string City { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
public string AdditionalInformation { get; set; }
public string BusinessCentralInstanceUrl { get; set; }
public Guid BusinessCentralCredentialsId { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
public virtual IList<UserIdentity> Users { get; set; }
[ForeignKey("BusinessCentralCredentialsId")]
public virtual Credentials BusinessCentralCredentials { get; set; }
}
DbContext.cs
public class OrganizationDbContext : DbContext, IOrganizationDbContext
{
public DbSet<Organization> Organizations { get; set; }
public DbSet<Credentials> Credentials { get; set; }
public DbSet<CredentialsType> CredentialTypes { get; set; }
public OrganizationDbContext(DbContextOptions<OrganizationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Organization>(entity =>
{
entity.ToTable(TableConsts.Organizations);
entity.HasKey(x => x.Id);
entity.HasMany(x => x.Users)
.WithOne(x => x.Organization)
.OnDelete(DeleteBehavior.ClientSetNull);
//entity.HasOne(x => x.BusinessCentralCredentials)
// .WithOne()
// .OnDelete(DeleteBehavior.ClientSetNull);
});
//modelBuilder.Entity<Credentials>(entity =>
//{
// entity.ToTable(TableConsts.BusinessCentralCredentials);
// entity.HasKey(x => x.Id);
// entity.HasOne(x => x.Type)
// .WithOne()
// .OnDelete(DeleteBehavior.ClientSetNull);
//});
//modelBuilder.Entity<Credentials>().ToTable(TableConsts.BusinessCentralCredentials);
modelBuilder.Entity<ClientCredentials>().ToTable(TableConsts.BusinessCentralClientCredentials);
modelBuilder.Entity<AccessKeyCredentials>().ToTable(TableConsts.BusinessCentralAccessKeyCredentials);
modelBuilder.Entity<CredentialsType>(entity =>
{
entity.ToTable(TableConsts.BusinessCentralCredentialTypes);
entity.HasKey(x => x.Id);
});
modelBuilder.Entity<UserIdentity>()
.ToTable(TableConsts.AspNetUsers, t => t.ExcludeFromMigrations());
}
}
Hope you may help to find out some fix for this.
Thank you in advance.
UPD: I can create migration and apply it without any error. Error happens when I launch my app.
Remove "abstract" from class definition. I don't know why it is like that.

EF: Automatically load related data

Currently when I want to select accounts with related properties, I have to include them manually everytime I select data:
public async Task<List<AccountDataModel>> GetAccountsAsync()
{
return await _dbContext.Accounts.Include(a => a.Settings).ToListAsync();
}
public Task<AccountDataModel> GetAccountByLoginAsync(string login)
{
return Task.FromResult(_dbContext.Accounts.Include(a => a.Settings).Where(a => a.Login.Equals(login)).FirstOrDefault());
}
Data models:
public class AccountDataModel
{
public string Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public DateTime Created { get; set; }
public SettingsDataModel Settings { get; set; } = new SettingsDataModel();
}
public class SettingsDataModel
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public AccountDataModel Account { get; set; }
}
Relation setting:
modelBuilder.Entity<AccountDataModel>(e =>
{
// Set primary key
e.HasKey(a => a.Id);
// Settings one-to-one relationship
e.HasOne(a => a.Settings)
.WithOne(s => s.Account)
.OnDelete(DeleteBehavior.Cascade)
.HasForeignKey<SettingsDataModel>(s => s.Id)
.IsRequired();
});
I was wondering, if this is possible to automatically load related Settings property for each Account selected?
I'm using the lastest version of EF Core.

AutoMapper not returning objects from related entites

I'm having issues with returning objects from related domain models. The objects that are from other models are returning null.
What i am basically trying to accomplish is return an DTO that have the fields that i want from the related domain models instead of passing every field straight from the domain models to json.
Please see below code, can someone please advise.
## CourseDomainModels.cs ##
public class CourseDomainModel : IObjectWithState
{
public int Id { get; set; }
public string Name { get; set; }
public Double Duration { get; set; }
public string Description { get; set; }
public virtual TutorDomainModel CourseTutor { get; set; }
public virtual SubjectDomainModel CourseSubject { get; set; }
public ICollection<EnrollmentDomainModel> Enrollments { get; set; }
[NotMapped]
public Common.State state { get; set; }
[NotMapped]
public bool InDb => this.Id != default(int);
public object PersistenceEntityId => this.Id;
}
## TutorDomainModel.cs ##
public class TutorDomainModel : IObjectWithState
{
public int Id { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Enums.Gender Gender { get; set; }
public ICollection<CourseDomainModel> Courses;
[NotMapped]
public Common.State state { get; set; }
[NotMapped]
public bool InDb => this.Id != default(int);
public object PersistenceEntityId => this.Id;
}
## CourseDTO.cs ##
public class CourseDTO
{
public string Name { get; set; }
public Double Duration { get; set; }
public string Description { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
## AutoMapperConfig.cs ##
public class AutoMapperConfig
{
public static void RegisterMapping()
{
Mapper.CreateMap<CourseDomainModel, CourseDTO>();
}
}
## Startup.cs ##
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
AutoMapperConfig.RegisterMapping();
}
}
## CourseService.cs ##
public CourseDTO GetCourse(int id)
{
var course = _courseRepo.Get(id);
CourseDTO courseView = Mapper.Map<CourseDomainModel,CourseDTO(course);
return courseView;
}
AutoMapper maps the properties of TSource to properties of TDestination, but it does not try to find properties of TDestination from child properties of TSource by default.
You can instruct AutoMapper to do so:
Mapper.CreateMap<CourseDomainModel, CourseDTO>()
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.CourseTutor == null ? string.Empty : src.CourseTutor.Email))
.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.CourseTutor == null ? string.Empty : src.CourseTutor.FirstName))
.ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.CourseTutor == null ? string.Empty : src.CourseTutor.LastName));
CourseDTO courseView = Mapper.Map<CourseDTO>(course);
AutoMapper is not AI yet, so you should explicitly specify custom member mappings:
Mapper.CreateMap<CourseDomainModel, CourseDTO>()
.ForMember(dest => dest.Email, opt => opt.MapFrom(source => source.TutorDomainModel.Email));

move identity in one DbContext (code first)

I have asp.net MVC application, entity framework 6 with code first feature
Here is my code for identity classes
namespace Core.Domain.Identity
{
public partial class AspNetRoles : BaseEntity
{
public AspNetRoles()
{
AspNetUsers = new HashSet<AspNetUsers>();
}
public string RlId { get; set; }
public string Name { get; set; }
public virtual ICollection<AspNetUsers> AspNetUsers { get; set; }
}
public partial class AspNetUserClaims : BaseEntity
{
public string UserId { get; set; }
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
public virtual AspNetUsers AspNetUsers { get; set; }
}
public partial class AspNetUserLogins : BaseEntity
{
//[Key]
//[Column(Order = 0)]
public string LoginProvider { get; set; }
//[Key]
//[Column(Order = 1)]
public string ProviderKey { get; set; }
//[Key]
//[Column(Order = 2)]
public string UserId { get; set; }
public virtual AspNetUsers AspNetUsers { get; set; }
}
public partial class AspNetUsers : BaseEntity
{
public AspNetUsers()
{
AspNetUserClaims = new HashSet<AspNetUserClaims>();
AspNetUserLogins = new HashSet<AspNetUserLogins>();
AspNetRoles = new HashSet<AspNetRoles>();
}
public string UsId { get; set; }
//[StringLength(256)]
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public bool TwoFactorEnabled { get; set; }
public DateTime? LockoutEndDateUtc { get; set; }
public bool LockoutEnabled { get; set; }
public int AccessFailedCount { get; set; }
//[Required]
//[StringLength(256)]
public string UserName { get; set; }
public virtual ICollection<AspNetUserClaims> AspNetUserClaims { get; set; }
public virtual ICollection<AspNetUserLogins> AspNetUserLogins { get; set; }
public virtual ICollection<AspNetRoles> AspNetRoles { get; set; }
}
}
code for mapping
public partial class AspNetRolesMap : EntityTypeConfiguration<AspNetRoles>
{
public AspNetRolesMap()
{
this.ToTable("AspNetRoles");
this.HasKey(anr => anr.RlId)
.Property(anr => anr.RlId).IsRequired().HasMaxLength(128);
this.Property(anr => anr.Name).IsRequired();
this.HasMany(anr => anr.AspNetUsers)
.WithMany(anr => anr.AspNetRoles)
.Map(m => m.ToTable("AspNetUserRoles").MapLeftKey("RoleId").MapRightKey("UserId"));
}
}
public partial class AspNetUserClaimsMap : EntityTypeConfiguration<AspNetUserClaims>
{
public AspNetUserClaimsMap()
{
this.ToTable("AspNetUserClaims");
this.HasKey(anuc => anuc.Id)
.Property(anuc => anuc.Id).IsRequired();
this.Property(anuc => anuc.ClaimType).IsMaxLength();
this.Property(anuc => anuc.ClaimValue).IsMaxLength();
//this.HasRequired(anuc => anuc.AspNetUsers)
// .WithMany()
// .HasForeignKey(anuc => anuc.UserId);
}
}
public partial class AspNetUserLoginsMap : EntityTypeConfiguration<AspNetUserLogins>
{
public AspNetUserLoginsMap()
{
this.ToTable("AspNetUserLogins");
this.HasKey(x => new { x.LoginProvider, x.ProviderKey, x.UserId });
//this.HasKey(anul => anul.LoginProvider)
//.Property(anul => anul.LoginProvider).HasMaxLength(128).IsRequired();
//this.HasKey(anul => anul.ProviderKey)
// .Property(anul => anul.ProviderKey).HasMaxLength(128).IsRequired();
//this.HasKey(anul => anul.UserId)
// .Property(anul => anul.UserId).HasMaxLength(128).IsRequired();
//this.HasRequired(anul => anul.AspNetUsers)
// .WithMany()
// .HasForeignKey(anul => anul.UserId);
}
}
public partial class AspNetUsersMap : EntityTypeConfiguration<AspNetUsers>
{
public AspNetUsersMap()
{
this.ToTable("AspNetUsers");
this.HasKey(anr => anr.UsId)
.Property(anr => anr.UsId).HasMaxLength(128).IsRequired();
this.Property(anu => anu.EmailConfirmed).IsRequired();
this.Property(anu => anu.PasswordHash).IsMaxLength();
this.Property(anu => anu.SecurityStamp).IsMaxLength();
this.Property(anu => anu.PhoneNumber).IsMaxLength();
this.Property(anu => anu.PhoneNumberConfirmed).IsRequired();
this.Property(anu => anu.TwoFactorEnabled).IsRequired();
this.Property(anu => anu.LockoutEndDateUtc).IsOptional();
this.Property(anu => anu.LockoutEnabled).IsRequired();
this.Property(anu => anu.AccessFailedCount).IsRequired();
this.Property(anu => anu.UserName).HasMaxLength(256).IsRequired();
this.HasMany(anu => anu.AspNetUserClaims)
.WithRequired(anu => anu.AspNetUsers)
.HasForeignKey(anu => anu.UserId);
this.HasMany(anu => anu.AspNetUserLogins)
.WithRequired(anu => anu.AspNetUsers)
.HasForeignKey(anu => anu.UserId);
//this.HasMany(anu => anu.AspNetRoles)
// .WithMany(anu => anu.AspNetUsers)
// .Map(m => m.ToTable("AspNetUserRoles").MapLeftKey("UsId").MapRightKey("Id"));
}
}
and context
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class PsCoDbContext : IdentityDbContext<ApplicationUser>, IDbContext
{
public PsCoDbContext() : base("name=DataModel")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach(var type in typesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
base.OnModelCreating(modelBuilder);
}
public static PsCoDbContext Create()
{
return new PsCoDbContext();
}
}
If i start application i get an exception
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: The entity types 'IdentityRole' and 'AspNetRoles' cannot share table 'AspNetRoles' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them.
Can enybody help me what i do wrong?

Entity Framework - The foreign key component … is not a declared property on type

I have the following Model
public class FilanthropyEvent : EntityBase, IDeleteable
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime EventDate { get; set; }
public string Description { get; set; }
public decimal Target { get; set; }
public decimal EntryFee { get; set; }
public bool Deleted { get; set; }
public ICollection<EventAttendee> EventAttendees { get; set; }
}
public class Attendee : EntityBase, IDeleteable
{
public int Id { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool MailingList { get; set; }
public bool Deleted { get; set; }
public ICollection<EventAttendee> EventAttendees { get; set; }
}
Events and Attendees is a many to many relationship but I needed another property on the association so I created an association entity
public class EventAttendee : EntityBase
{
public int FilanthropyEventId { get; set; }
public int AttendeeId { get; set; }
public bool InActive { get; set; }
public virtual Attendee Attendee { get; set; }
public virtual FilanthropyEvent FilanthropyEvent { get; set; }
}
These are the configurations for each FilanthropyEvent and Attendee
public class FilanthropyEventConfiguration : EntityTypeConfiguration<FilanthropyEvent>
{
public FilanthropyEventConfiguration()
{
HasKey(x => x.Id);
Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.EventAttendees).WithRequired(x => x.FilanthropyEvent).HasForeignKey(x => x.FilanthropyEvent);
}
}
public AttendeeConfiguration()
{
HasKey(x => x.Id);
Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.EventAttendees).WithRequired(x => x.Attendee).HasForeignKey(x => x.AttendeeId);
}
public class EventAttendeesConfiguration : EntityTypeConfiguration<EventAttendee>
{
public EventAttendeesConfiguration()
{
HasKey(x => new {x.FilanthropyEventId, x.AttendeeId});
}
}
When I try and initialise the database via the update-database command in the package manager console I get the following error.
System.InvalidOperationException: The foreign key component 'FilanthropyEvent' is not a declared property on type 'EventAttendee'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.
I realise I'm probably missing a mapping in the EventAttendeesConfiguration class but what would be the correct mapping to model this relationship?
This code
HasMany(x => x.EventAttendees)
.WithRequired(x => x.FilanthropyEvent)
.HasForeignKey(x => x.FilanthropyEvent);
Should be
HasMany(x => x.EventAttendees)
.WithRequired(x => x.FilanthropyEvent)
.HasForeignKey(x => x.FilanthropyEventId);

Categories