Automapper multiple source to single target in v8.0 - asp.net-core

I'm new to the whole Automapper world in .net core 3.1 and was going through the docs and SO, but couldnt' find anything for my use case from their latest version 8.0.
Borrowing from another SO post, how could I do this in the new v8.0 configuration?
public class People {
public string FirstName {get;set;}
public string LastName {get;set;}
}
public class Phone {
public string Number {get;set;}
}
Convert to a PeoplePhoneDto like this:
public class PeoplePhoneDto {
public string FirstName {get;set;}
public string LastName {get;set;}
public string PhoneNumber {get;set;}
}
Would I use still do this?
Mapper.CreateMap<People, PeoplePhoneDto>();
Mapper.CreateMap<Phone, PeoplePhoneDto>()
.ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number));

Here is a working demo like below:
Model:
public class People
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Phone
{
public string Number { get; set; }
}
public class PeoplePhoneDto
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
}
AutoMapper profile:
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<People, PeoplePhoneDto>();
CreateMap<Phone, PeoplePhoneDto>()
.ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number));
}
}
Startup.cs:
services.AddAutoMapper(typeof(AutoMapperProfile));
Controller:
public class HomeController : Controller
{
private readonly IMapper _mapper;
public HomeController(IMapper mapper)
{
_mapper = mapper;
}
public IActionResult Index()
{
var people = new People() { FirstName = "aaa", LastName = "bbb" };
var phone = new Phone() { Number = "12345" };
var model = _mapper.Map<PeoplePhoneDto>(people); // map dto1 properties
_mapper.Map(phone, model);
//do your stuff...
return View();
}
}
Result:

Related

ASP.NET Core 6 MVC & Entity Framework Core 6 - execute stored procedure

I am doing for testing a small application with ASP.NET Core 6 MVC and Entity Framework Core.
I generated the database for testing based on the model.
Here is my dbContext:
using COBRAAuthentication.Repository.Models;
using Microsoft.EntityFrameworkCore;
namespace COBRAAuthentication.Repository.Data
{
public class CobraDbContext : DbContext
{
public CobraDbContext(DbContextOptions<CobraDbContext> options)
: base(options)
{
}
public DbSet<AspNetUser> AspNetUsers { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
}
and my model class:
namespace COBRAAuthentication.Repository.Models
{
public class AspNetUser : IEntity
{
public int Id { get; set; }
public string FirstName { get; set; } = String.Empty;
public string LastName { get; set; } = String.Empty;
public string UserName { get; set; } = String.Empty;
public string Email { get; set; } = String.Empty;
public DateTime AddedDate { get; set; }
public DateTime ModifiedDate { get; set; }
}
public interface IEntity
{
public int Id { get; set; }
public DateTime AddedDate { get; set; }
public DateTime ModifiedDate { get; set; }
}
}
After generating the database, I added a stored procedure that is not mapped in the model.
I want to call this stored procedure, but anything I try, I get different errors
namespace COBRAAuthentication.Repository.Repositories
{
public class GenericRepository<TEntity> : IGenericRepository<TEntity>
where TEntity : class, IEntity
{
private readonly CobraDbContext _dbContext;
public GenericRepository(CobraDbContext dbContext)
{
_dbContext = dbContext;
}
public IQueryable<TEntity>GetAllSP()
{
string studentName = _dbContext.Database.SqlQuery<TEntity> ("exec SP").ToList();
// or
var data = _dbContext.Database.SqlQueryRaw<TEntity>("exec SP" );
}
}
When I use SqlQuery I get an error
Can not convert from 'string' to 'System.FormattableString'
When I use SqlQueryRaw, the code does not execute at all.
How can I execute a stored procedure that it is not mapped?
Thanks

How to Map DTO class to "Model" class In generic Repository

I use DTO class in API layer and I struggle to map DTO class to "model" class in generic Repository.cs in core layer.
Repository.cs :
namespace DTOMap.Core.Repository.Generic
{
public class Repository<T> : IRepository<T> where T : class
{
private DTOMapContext _context;
private DbSet<T> _table;
private IMapper _mapper;
public Repository(DTOMapContext context)
{
_context = context;
_table = _context.Set<T>();
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MyMapper>();
});
_mapper = config.CreateMapper();
}
public T Add(T obj)
{
// Here how to use My Mapper to save a book or an author generically
// Sth like :
// temp = _table.Add(_mapper.Map<T>(obj)); Here I want to map Dto to model to save in the db
// return = (_mapper.Map<T>(temp)); Here I want to map Model to DTO to collect it in API
// but I can't have a reference to TDTO
throw new NotImplementedException();
}
}
}
I show you the other classes that I find useful (I only implement Add function for this example and I am a beginner in .Net) :
Author.cs
namespace DTOMap.Core.Models
{
[Table("Author")]
internal class Author
{
[Key]
public int id { get; set; }
[Required, MaxLength(255)]
public string firstName { get; set; }
[Required,MaxLength(255)]
public string lastName { get; set; }
}
}
Book.cs
namespace DTOMap.Core.Models
{
[Table("Book")]
internal class Book
{
[Key]
public int id { get; set; }
[Required,MaxLength(255)]
public string name { get; set; }
[Required]
public int authorId { get; set; }
[Required]
public Author author { get; set; }
}
}
AuthorDTO.cs
namespace DTOMap.Domain.DTO
{
public class AuthorDTO
{
public int id { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
}
}
BookDTO.cs
namespace DTOMap.Domain.DTO
{
public class BookDTO
{
public int id { get; set; }
public string name { get; set; }
public int authorId { get; set; }
public AuthorDTO author { get; set; }
}
}
IRepository.cs
namespace DTOMap.Domain.Interface
{
public interface IRepository<T>
{
T Add(T obj);
}
}
MyMapper.cs
namespace DTOMap.Core
{
public class MyMapper : Profile
{
public MyMapper()
{
CreateMap<Book, BookDTO>();
CreateMap<BookDTO, Book>();
CreateMap<Author, AuthorDTO>();
CreateMap<AuthorDTO, Author>();
}
}
}
program.cs
... Some Fcts
builder.Services.AddTransient<IRepository<BookDTO>, BookRepository>();
builder.Services.AddTransient<IRepository<AuthorDTO>, AuthorRepository>();
... Some Fcts
If you need any other information, please ask me.

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);
}

ef core 5 using Enumeration as composite keys

I'm trying to get benefit from eshoponcontainers code to use classes derived from Enumeration class to address it like CardType.Visa. I think it's very convenient.
But when i try to set this type as composite key it doesn't work.
I use ef core 5 with .net core 5.
Please, any suggestions.
public class FakeEntity: BaseEntity
{
public string Name { get; set; }
public int ContractId { get; set; }
}
public class FakeEntityLink
{
public FakeEntity FakeEntity { get; set; }
public int FakeEntityId { get; set; }
public int BillParamTypeEnumId { get; set; }
public BillParamTypeEnum BillParamTypeEnum { get; private set; } = BillParamTypeEnum.PriceCategory;
private FakeEntityLink() { }
public FakeEntityLink(FakeEntity fakeEntity, BillParamTypeEnum billParamTypeEnum)
{
FakeEntity = fakeEntity;
BillParamTypeEnum = billParamTypeEnum;
}
}
public class BillParamTypeEnum
: Enumeration
{
public static BillParamTypeEnum PriceCategory => new BillParamTypeEnum(1,"PriceCategory ");
public static BillParamTypeEnum VoltageTarifLevel => new BillParamTypeEnum(2, "VoltageTarifLevel ");
public static BillParamTypeEnum Sign => new BillParamTypeEnum(3,"Sign ");
public static BillParamTypeEnum VolumeCategory => new BillParamTypeEnum(4,"VolumeCategory");
public BillParamTypeEnum(int id, string name)
: base(id, name)
{
}
}
//and in dbContext
modelBuilder.Entity<FakeEntityLink>(entity => {
entity.HasKey(c => new { c.FakeEntityId, c.BillParamTypeEnumId });
entity.HasOne(link => link.FakeEntity).WithMany();
entity.HasOne(link => link.BillParamTypeEnum).WithMany().HasForeignKey(p=>p.BillParamTypeEnumId);
}
);
// code in controller
var fe =_db.FakeEntities.Find(1);
_db.FakeEntityLinks.Add(new FakeEntityLink(fe, BillParamTypeEnum.VoltageTarifLevel));
_db.SaveChanges(); // throws SqlException: Cannot insert explicit value for identity column in table 'BillParamTypeEnum' when IDENTITY_INSERT is set to OFF.

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