exception:"type was not mapped" in entityframework codefirst with layers - asp.net-mvc-4

i'm trying to apply LAYERS Concept on demo project developed using mvc and entity framework both
Data Annotations : for validations in Data Access Layer and
Fluent API : for mapping and tables relations
Problem : DbContext didn't Create DB and there is a Runtime Exception :
The type 'Domain.DataLayer.Member' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.
Code : my solutions consists of :
1- class library (Domain.Classes project): where i wrote all of my classes
public class Member
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
}
2- DAL (Domain.DataLayer project): also another class library and i referenced domain.classes
namespace Domain.DataLayer.Repositories
{
[MetadataType(typeof(MemberMetadata))]
public partial class Member : Classes.Member , IValidatableObject
{
public Member()
{
Tasks = new HashSet<Task>();
History = new HashSet<Commint>();
}
public string ConfirmPassword { get; set; }
public HashSet<Task> Tasks { get; set; }
public HashSet<Commint> History { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var result = new List<ValidationResult>();
if (!string.Equals(Password,ConfirmPassword))
{
result.Add(new ValidationResult("mismatch pwsd", new[] {"ConfirmPassword" }));
}
return result;
}
}
}
and i used repository pattern :
public class MemberRepository : IRepository<Member>
{
public Task<IQueryable<Member>> GetAllEntities()
{
return Task<IQueryable<Member>>.Factory.StartNew(() => new Context().Members.AsQueryable());
}
}
3-BLL : for sake of simplicity : there is no Business Logic Layer
4- PL (Domain.Application MVC Project) : Member Controller :
public async Task<ActionResult> Index()
{
var members = await _repository.GetAllEntities();
return View(members);
}
Note : i depended on DbContext to create DB with name like : Domain.DataLayer.Context but it didn't craete DB so i created the DB and passed the connectionString through Context constructor like this :
namespace Domain.DataLayer
{
public class Context : DbContext
{
public Context(): base("InterviewDemo") // i tried also base("name=InterviewDemo")
{
}
public DbSet<Member> Members { get; set; }
public DbSet<Task> Tasks { get; set; }
public DbSet<Commint> TaskHistory { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MemberConfig());
modelBuilder.Configurations.Add(new TaskConfig());
modelBuilder.Configurations.Add(new CommintConfig());
base.OnModelCreating(modelBuilder);
}
}
}

Related

AutoMapperMappingException: Missing type map configuration or unsupported mapping IN .NET 6

I have this entity:
public class Genres
{
public int Id { get; set; }
[Required(ErrorMessage ="the field {0} is required")]
[StringLength(50)]
[FirstLetterUpperCase]
public string Name { get; set; }
}
And this DTO or model:
public class GenresDTO
{
public int Id { get; set; }
public string Name { get; set; }
}
I have initiated my mapper like this:
public class AutoMapperClass : Profile
{
public AutoMapperClass()
{
generateMapper();
}
private void generateMapper()
{
CreateMap<GenresDTO, Genres>().ReverseMap();
CreateMap<GenresCreationDTO, Genres>();
}
}
I have also written this part of code in my program.cs :
builder.Services.AddAutoMapper(typeof(IStartup));
I am using .NET 6 and Visual Studio, and when I run my project, I get an error that is mentioned in the title and its related to this section :
public async Task<ActionResult<List<GenresDTO>>> Get()
{
var genres = await dbContext.Genres.ToListAsync();
return mapper.Map<List<GenresDTO>>(genres);
}
which is in my Controller file, and I initiated the mapper like this :
private readonly ILogger<GenresController> ilogger;
private readonly ApplicationDBContext dbContext;
private readonly IMapper mapper;
public GenresController(ILogger<GenresController> ilogger,
ApplicationDBContext dbContext , IMapper mapper)
{
this.ilogger = ilogger;
this.dbContext = dbContext;
this.mapper = mapper;
}
Should be probably typeof(Program) in registration (assuming that you are using .Net6 where we have only Program.cs)
builder.Services.AddAutoMapper(typeof(Program))
If you have multiple projects in solution,then value used there should be a file in the assembly in which the mapping configuration resides.

How to update an existing entity that has a nested list of entities?

I'm trying to update an entity using entity framework but, everytime I try to do it, it raises an error saying that a nested entity the main class contains cannot be tracked.
These are my classes:
public abstract class BaseEntity
{
public int Id { get; set; }
}
public class Dashboard : BaseEntity
{
public int Order { get; set; }
public string Title { get; set; }
public bool Enabled { get; set; }
public virtual ICollection<Submenu> Submenu { get; set; }
}
public class Submenu : BaseEntity
{
public int Order { get; set; }
public bool Enabled { get; set; }
public string Title { get; set; }
public string Image { get; set; }
public string Descriptions { get; set; }
public virtual ICollection<Action> Actions { get; set; }
public int DashboardId { get; set; }
public virtual Dashboard Dashboard { get; set; }
}
public class Action : BaseEntity
{
public string Type { get; set; }
public string Label { get; set; }
public string Url { get; set; }
public string Extension { get; set; }
public virtual Submenu Submenu { get; set; }
public int SubmenuId { get; set; }
}
The one I am using to update is Dashboard, which contains the rest of the classes.
I'm trying to do it using a generic service layer and a generic repository that are defined this way:
public class GenericService<T> : IGenericService<T> where T : BaseEntity
{
private readonly IBaseRepository<T> baseRepository;
public GenericService(IBaseRepository<T> baseRepository)
{
this.baseRepository = baseRepository;
}
public async Task Update(T entity, T attachedEntity)
{
await baseRepository.Update(entity, attachedEntity);
}
}
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly PortalContext dataContext;
private DbSet<T> DbSet { get; set; }
public BaseRepository(PortalContext context)
{
dataContext = context;
DbSet = dataContext.Set<T>();
}
public async Task Update(T entity, T attachedEntity)
{
dataContext.Entry(attachedEntity).State = EntityState.Detached;
DbSet.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
await dataContext.SaveChangesAsync();
}
}
And, at last but no least, this is the way I am configuring everything at Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PortalContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("PortalContext"))
);
services.AddTransient(typeof(IGenericService<>), typeof(GenericService<>));
services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>));
services.AddTransient<Func<string, ClaimsPrincipal, IRoleCheck>>((serviceProvider) =>
{
return (controllerName, claimsPrincipal) =>
new RoleCheck(serviceProvider.GetRequiredService<IGenericService<Dossier>>(),
serviceProvider.GetRequiredService<IGenericService<DossierTemplate>>(),
serviceProvider.GetRequiredService<IGenericService<Dashboard>>(),
controllerName, claimsPrincipal);
});
}
What the application first does is calling the RoleCheck class to retrieve and filter the required entities and, after that, the user can update them.
When I call the update function at the controller
public async Task<ActionResult<Dashboard>> Put(int id, [FromBody] Dashboard dashboard)
{
var currentDashboard = await service.Get(id);
if (currentDashboard == null)
{
return NotFound();
}
await service.Update(dashboard, currentDashboard);
return Ok();
}
I always receive the next error at the repository:
error
Is there something I am doing wrong? I have been stuck with this for a week now...
Thanks in advance and sorry for the long text, but I wanted it to be clear.
I could finally solve it by adding .AsNoTracking() at the Get() method of my repository:
public async Task<T> Get(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> includes)
{
IQueryable <T> query = DbSet.AsNoTracking();
if (includes != null)
{
query = includes(query);
}
return await query.FirstOrDefaultAsync(m => m.Id == id);
}

Extending EF Core Identity UserStore with integer user ID

Using Blazor server, dotnet 5 and Entity Framework Core, I have successfully customised Identity using integer IDs
e.g
public class ApplicationUser : IdentityUser<int>
{
[Required]
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
[MaxLength(50)]
public string LastName { get; set; }
}
I now want to extend UserStore for custom login to store password history to prevent duplication. The documented way of doing this seems to be:
public class ApplicationUserStore : UserStore<ApplicationUser>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
public override async Task<IdentityResult> CreateAsync(ApplicationUser appUser)
{
await base.CreateAsync(appUser);
await AddToUsedPasswordAsync(appUser, appUser.PasswordHash);
}
public Task AddToUsedPasswordAsync(ApplicationUser appuser, string userpassword)
{
appuser.UserUsedPassword.Add(new UsedPassword() { UserID = appuser.Id, HashPassword = userpassword });
return UpdateAsync(appuser);
}
}
This works for the default GUID Id but when using an integer it throws the error:
The type ApplicationUser cannot be used as type parameter 'TUser' in the generic type or method 'UserStore'. There is no implicit reference conversion from ApplicationUser' to 'Microsoft.AspNetCore.Identity.IdentityUser'.
What is the correct way to do this?
EDIT:
Per #Yinqiu, modified for custom ApplicationRole to
public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, ApplicationDbContext, int>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
Builds successfully and creates users but gives run time error when trying to access ApplicationUserManager.IsInRoleAsync:
{"Cannot create a DbSet for 'IdentityUserRole' because this type is not included in the model for the context."}
However I have a custom User Role:
public class ApplicationUserRole : IdentityUserRole<int>
{
public ApplicationUserRole() : base() { }
public bool IsSystemEssential { get; set; }
}
With a definition in ApplicationDbContext :
public DbSet ApplicationUserRoles { get; set; }
SOLVED (provisionally)
After looking at the overloaded inheritance options you have to extend as follows:
public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, ApplicationDbContext, int, ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin, ApplicationUserToken, ApplicationRoleClaim>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
}
This seems to work. Still need to check all role and claim updates.
You can change your ApplicationUserStore as following.
public class ApplicationUserStore : UserStore<ApplicationUser,IdentityRole<int>, ApplicationDbContext,int>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
//...
}

select - keyword not working with odata, automapper and efcore

I am trying to apply the odata query to my automapper - mappings at my efcore context. Everything works as expected until I use the $select query option.
When I try to use the select keyword in the request to my odata - controller, I get the exception:
SerializationException: 'SourceSourceInjectedQuery`2' cannot be serialized using the ODataMediaTypeFormatter.
I am using the UseAsDataSource - Extension method because it was recommended here on github.
This is my oDataController:
public class StudentsController : ODataController {
private readonly SchoolContext schoolContext;
public StudentsController(SchoolContext schoolContext) {
this.schoolContext = schoolContext;
}
[EnableQuery]
public IActionResult Get() {
return Ok(
schoolContext
.Students
.UseAsDataSource()
.For<StudentVM>()
);
}
}
This is my Entity for EFCore:
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
And this is my mappingprofile for automapper:
public class StudentVM {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
}
public class StudentProfile : Profile {
public StudentProfile() {
CreateMap<Student, StudentVM>();
}
}
Do I need some specific mapping to do this?
I figured out I had a mistake in my configuration of the odataservice inside my startup.cs
private static IEdmModel GetEdmModel() {
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Student>("Students");
builder.EntitySet<Course>("Courses");
return builder.GetEdmModel();
}
I put my Entities instead of my ViewModels there. This is the fixed code:
private static IEdmModel GetEdmModel() {
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<StudentVM>("Students");
builder.EntitySet<CourseVM>("Courses");
return builder.GetEdmModel();
}
Now it's working as expected

Trouble Creating Specification Across Entities With NLinq

I am using the Specification pattern, and have a working implementation (taken from the WhoCanHelpMe Codeplex project) for getting data via NLinq, generic repositories and all that goodness.
The root method is:
public IList<Case> GetCasesByUsername(string username)
{
CaseByUserNameSpecification spc = new CaseByUserNameSpecification(username);
return this.caseRepository.FindAll(spc).ToList();
}
The FindAll() method does the following:
public IQueryable<T> FindAll(ILinqSpecification<T, T> specification)
{
return specification.SatisfyingElementsFrom(this.Session.Linq<T>());
}
And, SatisfyingElementsFrom() does this:
public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates)
{
if (this.MatchingCriteria != null)
{
return candidates.Where(this.MatchingCriteria).ToList().ConvertAll(this.ResultMap).AsQueryable();
}
return candidates.ToList().ConvertAll(this.ResultMap).AsQueryable();
}
So, for querying cases by CaseNb property of a Case, it's pretty straight-forward. A Specification like the one below works for me and gets the cases I'd want.
public class CaseByCaseNbSpecification : QuerySpecification<User>
{
private string caseNb;
public CaseByCaseNbSpecification(string caseNb)
{
this.caseNb = caseNb;
}
public string UserName
{
get { return this.caseNb; }
}
public override Expression<Func<Case, bool>> MatchingCriteria
{
get { return u => u.CaseNb.Equals(this.caseNb, StringComparison.CurrentCultureIgnoreCase); }
}
}
However, I am at a loss to understand how to do this when crossing multiple entities. What I'd like to have is a Specification that allows me to get Cases by UserName. Basically, in the database, there are three tables and these have been carried into entities. Here's are entities:
Here's the Case class:
public class Case : Entity
{
private ICollection<CaseUser> caseUsers = new HashSet<CaseUser>();
public virtual Patient Patient { get; set; }
public virtual string CaseNb { get; set; }
...
public virtual IEnumerable<CaseUser> CaseUsers { get { return caseUsers; } }
}
Here's the CaseUser:
public class CaseUser : Entity
{
public virtual Case Case { get; set; }
public virtual User User { get; set; }
...
}
And, User:
public class User : Entity
{
private ICollection<CaseUser> caseUsers = new HashSet<CaseUser>();
public virtual Account Account { get; set; }
public virtual string UserName { get; set; }
...
public virtual IEnumerable<CaseUser> CaseUsers { get { return caseUsers; } }
}
How would I write the Expression to get the data across the association table?
I believe your specification implementation should look something like this:
public class CaseByUsernameSpecification : QuerySpecification<Case>
{
private string userName;
public CaseByUsernameSpecification(string userName)
{
this.userName = userName;
}
public string UserName
{
get { return this.userName; }
}
public override Expression<Func<Case, bool>> MatchingCriteria
{
get { return c => c.CaseUsers.Any(cu => cu.User.Username == this.userName); }
}
}