EF Core related data is null with AutoMapper - c#

In my app one case can have many companies.
My models:
public class Case
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public IList<CaseCompany> CaseCompanies { get; set; }
}
public class CaseInput
{
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public IList<CaseCompanyInput> CaseCompanyInputs { get; set; }
}
public class Company
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
}
public class CompanyInput
{
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
}
public class CaseCompany
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid CaseId { get; set; }
public Guid CompanyId { get; set; }
public class Case Case { get; set; }
public class Company Company { get; set; }
}
public class CaseCompanyInput
{
public Guid Id { get; set; }
public Guid CaseId { get; set; }
public Guid CompanyId { get; set; }
public class CaseInput CaseInput { get; set; }
public class CompanyInput CompanyInput { get; set; }
}
AutoMapperProfiles.cs:
// In Startup.cs: services.AddAutoMapper(typeof(AutoMapperProfiles));
public class AutoMapperProfiles : Profile
{
public AutoMapperProfiles()
{
CreateMap<Case, CaseInput>().ReverseMap();
CreateMap<CaseCompany, CaseCompanyInput>().ReverseMap();
CreateMap<Company, CompanyInput>().ReverseMap();
}
}
EditCase.cs:
private readonly DBContext _dbContext;
private readonly IMapper _mapper;
[BindProperty]
public CaseInput CaseInput { get; set; }
public EditCase(DBContext dbContext, IMapper mapper)
{
_dbContext = dbContext;
_mapper = mapper;
}
public async Task<IActionResult> OnGetAsync(Guid caseId)
{
var getCase = await _dbContext.Cases.Include(x => x.CaseCompanies).ThenInclude(x => x.Company).FirstOrDefaultAsync(x => x.Id == caseId);
CaseInput = _mapper.Map<CaseInput>(getCase);
Console.WriteLine(getCase.CaseCompanies[0].Company.Name) // gets the company name
Console.WriteLine(CaseInput.CaseCompanyInputs[0].CompanyInput.Name) // CaseInput.Name is not null but CaseCompanyInputs is null
return Page();
}
I've also tried:
CaseInput = await _dbContext.Cases.ProjectTo<CaseInput>(_mapper.ConfigurationProvider).FirstOrDefaultAsync(x => x.Id == caseId);
and
CaseInput = await _dbContext.Cases.Include(x => x.CaseCompanies).ThenInclude(x => x.Company).ProjectTo<CaseInput>(_mapper.ConfigurationProvider).FirstOrDefaultAsync(x => x.Id == caseId);
with the same result: CaseCompanyInputs is null.
My best guess is that it's an error in the relations of the input models, but I just can't see it. I believe I've followed the naming conventions to make the relations right.
Any help would be appreciated.

For automapping to work, you have to rename ur destination property name the same as the source property name
public class CaseInput
{
public Guid Id { get; set; }
public string Name { get; set; }
//public IList<CaseCompanyInput> CaseCompanyInputs { get; set; }
public IList<CaseCompanyInput> CaseCompanies { get; set; }
}
public class CaseCompanyInput
{
public Guid Id { get; set; }
public Guid CaseId { get; set; }
public Guid CompanyId { get; set; }
//public CaseInput CaseInput { get; set; }
public CaseInput Case { get; set; }
//public CompanyInput CompanyInput { get; set; }
public CompanyInput Company { get; set; }
}
If you don't want to change the property names, change ur configuration to the following.
CreateMap<Case, CaseInput>()
.ForMember(dest => dest.CaseCompanyInputs, opt => opt.MapFrom(src => src.CaseCompanies));
CreateMap<CaseCompany, CaseCompanyInput>()
.ForMember(dest => dest.CaseInput, opt => opt.MapFrom(src => src.Case))
.ForMember(dest => dest.CompanyInput, opt => opt.MapFrom(src => src.Company));

Related

Property Foreign Key can not be set to NULL

I have 4 tables
public class StokGroup:BaseModel
{
public string GroupName { get; set; }
public List<GroupFeature> GroupFeatures { get; set; }
public List<StokCard> StokCards { get; set; }
}
public class GroupFeature : BaseModel
{
public int StokGroupId { get; set; }
public StokGroup StokGroup { get; set; }
public string FeatureName { get; set; }
public List<StokCardAdditionalFeatureValue> StokCardAdditionalFeatureValues { get; set; }
}
public class StokCard : BaseModel
{
public string IPMCode { get; set; }
public int StokGroupId { get; set; }
public StokGroup StokGroup { get; set; }
.......................................
}
public class StokCardAdditionalFeatureValue:BaseModel
{
public int StokCardId { get; set; }
public StokCard StokCard { get; set; }
public int GroupFeatureId { get; set; }
public GroupFeature GroupFeature { get; set; }
public string Value { get; set; }
}
And my BaseModel is the class that has Primary Key ID all classes inherit from BaseModel
public class BaseModel
{
[Key]
public int Id { get; set; }
}
And I have also :
modelBuilder.Entity<StokCard>()
.HasOne(o => o.StokGroup)
.WithMany(m => m.StokCards)
.OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<GroupFeature>()
.HasOne(o => o.StokGroup)
.WithMany(m => m.GroupFeatures);
modelBuilder.Entity<StokCardAdditionalFeatureValue>()
.HasOne(o => o.StokCard)
.WithMany(m => m.StokCardAdditionalFeatureValues)
.OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<StokCardAdditionalFeatureValue>()
.HasOne(o => o.GroupFeature)
.WithMany(m => m.StokCardAdditionalFeatureValues);
Now the problem is when the stokgroup is deleted StokCard is deleted automatically because of the relationship
How can I achieve this problem ?
I set NULL to StokGroupId on StokCards table but migration FAILED
Would you mind please help ?
Thanks in advance

.Net Core Web Api / HttpGet / About Get ICollection

Firstly, sorry for my English, it is not perfect. I hope i can explain my problem.
WebApi has Movie Class like this
public class Movie
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public DateTime ReleaseDate { get; set; }
public float Price { get; set; }
public int DirectorID { get; set; }
public Director Director { get; set; }
public int GenreID { get; set; }
public Genre Genre { get; set; }
public ICollection<Actor> Actors { get; set; }
}
When I used HttpGet Movies
it happens
It cant read actors. How can i fix this?
GetMoviesQuery
namespace WebApi.Application.MovieOperations.Queries.GetMovies
{
public class GetMoviesQuery
{
private readonly IMovieStoreDbContext _context;
private readonly IMapper _mapper;
public GetMoviesQuery(IMovieStoreDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public List<GetMoviesModel> Handle()
{
var _movieList = _context.Movies.Include(x => x.Genre).Include(y => y.Director).OrderBy(z => z.Id).ToList<Movie>();
List<GetMoviesModel> mv = _mapper.Map<List<GetMoviesModel>>(_movieList);
return mv;
}
}
public class GetMoviesModel
{
public string Name { get; set; }
public string ReleaseDate { get; set; }
public string Genre { get; set; }
public string Director { get; set; }
public float Price { get; set; }
public ICollection<Actor> Actors { get; set; }
}
}
Thank you.
you have to include actors too
var _movieList = _context.Movies
.Include(x => x.Actors)
.Include(x => x.Genre)
.Include(y => y.Director)
.OrderBy(z => z.Id)
.ToList();
UPDATE:
If you only need the First and the last names of actors you will have to create a model class
public class ActorViewModel
{
public string Name { get; set; }
public string SurName { get; set; }
}
and fix GetMoviesModel accordingly
public class GetMoviesModel
{
public string Name { get; set; }
.....
public ICollection<ActorViewModel> Actors { get; set; }
}

AutoMapperMappingException Strange Behaviour

I've found some strange behaviour regarding AutoMapper when I'm trying to map from this class
public class SkinAnalyzerResult {
public Guid Id { get; set; }
public ICollection<SkinAnalyzerQuestionAnswer> SelectedAnswers { get; set; }
public string Description { get; set; }
public ICollection<Product> RecommendedProducts { get; set; }
}
public class SkinAnalyzerQuestionAnswer {
public Guid Id { get; set; }
public String Answer { get; set; }
public ICollection<SkinAnalyzerResult> Results { get; set; }
public SkinAnalyzerQuestion Question { get; set; }
}
to this class
public class SkinAnalyzerResultDataModel {
[Key] public Guid Id { get; set; }
[Required] public ICollection<SkinAnalyzerResultQuestionAnswerDataModel> SelectedAnswers { get; set; }
public string Description { get; set; }
public ICollection<SkinAnalyzerResultProductDataModel> RecommendedProducts { get; set; }
public SkinAnalyzerDataModel SkinAnalyzer { get; set; }
}
public class SkinAnalyzerResultQuestionAnswerDataModel {
public Guid ResultId { get; set; }
public SkinAnalyzerResultDataModel Result { get; set; }
public Guid QuestionAnswerId { get; set; }
public SkinAnalyzerQuestionAnswerDataModel QuestionAnswer { get; set; }
}
public class SkinAnalyzerQuestionAnswerDataModel {
[Key] public Guid Id { get; set; }
[Required] public String Answer { get; set; }
public SkinAnalyzerQuestionDataModel Question { get; set; }
public ICollection<SkinAnalyzerResultQuestionAnswerDataModel> Results { get; set; }
}
using this config map
cfg.CreateMap<SkinAnalyzerResultDataModel, SkinAnalyzerResult>()
.ForMember(dest => dest.SelectedAnswers,
opt => opt.MapFrom(
src => src.SelectedAnswers.Select(x => x.QuestionAnswer)
))
.ForMember(dest => dest.RecommendedProducts,
opt => opt.MapFrom(
src => src.RecommendedProducts.Select(x => x.Product)
))
.ReverseMap();
When I tried to do that I got this AutoMapperMappingException
as you can see I've specified to map SkinAnalyzerResult.Answers into SkinAnalyzerResultDataModel.SelectedAnswers.QuestionAnswer
is there something that I missed or do something wrong?
cfg.CreateMap<SkinAnalyzerResultDataModel, SkinAnalyzerResult>()
.ForMember(dest => dest.SelectedAnswers,
opt => opt.MapFrom(
src => src.SelectedAnswers.Select(x => x.QuestionAnswer)
))
The type of SelectedAnswers is ICollection<SkinAnalyzerQuestionAnswer> while the type of QuestionAnswer is SkinAnalyzerQuestionAnswerDataModel, so you also need to create mapping between the two models:
CreateMap<SkinAnalyzerQuestionAnswerDataModel, SkinAnalyzerQuestionAnswer>()
.ForMember( //config if necessary)

One to Many relationship always bring me empty

I try to use Entity Framework with code first and fluent api to implement a one to many relationship
I have two classes
namespace Mantenimiento.Business.Entities
{
public class Personal : Entity
{
[Key]
public int Id { get; set; }
public int? Dni { get; set; }
public string Nombre { get; set; }
public string Apellido { get; set; }
public string Cuil { get; set; }
public string Legajo { get; set; }
[ForeignKey("Dni")]
public ICollection<ContactoEmergencia> Contacto { get; set; }
}
namespace Mantenimiento.Business.Entities
{
public class ContactoEmergencia : Entity
{
[Key]
public int Id { get; set; }
public int? Dni { get; set; }
public string ApellidoNombre { get; set; }
public string Vinculo { get; set; }
public string Domicilio { get; set; }
public string telefono { get; set; }
public string Comentario { get; set; }
public int CreateUserId { get; set; }
[ForeignKey("Dni")]
public virtual Personal Personal { get; set; }
}
}
This is my dbContext
#region personals
modelBuilder.Entity<Personal>().ToTable("InfoPersonal").HasKey(t => t.Id);
modelBuilder.Entity<Personal>().Property(c => c.Id).UseSqlServerIdentityColumn().IsRequired();
modelBuilder.Entity<Personal>().Property(c => c.CreatedDate).HasDefaultValue(DateTime.Now);
modelBuilder.Entity<Personal>().Property(c => c.LastModifiedDate).HasDefaultValue(DateTime.Now);
modelBuilder.Entity<Personal>().Property(c => c.Deleted).HasDefaultValue(false);
modelBuilder.Entity<Personal>().HasMany<ContactoEmergencia>(c => c.Contacto).WithOne(p => p.Personal).HasForeignKey(s => s.Dni);
#endregion
#region contactoEmergencias
modelBuilder.Entity<ContactoEmergencia>().ToTable("InfoEmergencia").HasKey(d => d.Dni);
modelBuilder.Entity<ContactoEmergencia>().Property(c => c.CreatedDate).HasDefaultValue(DateTime.Now);
modelBuilder.Entity<ContactoEmergencia>().Property(c => c.LastModifiedDate).HasDefaultValue(DateTime.Now);
modelBuilder.Entity<ContactoEmergencia>().Property(c => c.Deleted).HasDefaultValue(false);
#endregion
And my query is
return await _context.personals
.Include(c => c.Contacto)
.Where(p => p.Deleted == false)
.OrderBy(s => s.Apellido)
.ToListAsync(
);
But the properties is always empty.
i need to relate Personal.Di with Contacto.Dni, i had to change the key?
You should remove ForeignKey attribute from Personal entity. In one to many relationship only child entity could accept ForeignKey.

Automapper many to many mapping confusion

I have many to many relationship tables such as "User & Notification & UserNotification" and their entities, view models also.
There is only a difference between ViewModel and Entity classes. HasRead property is inside NotificationViewModel. How Can I map this entities to view models? I could not achieve this for HasRead property.
What I did so far is,
Mapping Configuration:
CreateMap<Notification, NotificationViewModel>();
CreateMap<User, UserViewModel>().ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)));
Notification class:
public class Notification : IEntityBase
{
public Notification()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
User Class
public class User : IEntityBase
{
public User()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
UserNotification class:
public class UserNotification : IEntityBase
{
public int Id { get; set; }
public int UserId { get; set; }
public int NotificationId { get; set; }
public bool HasRead { get; set; }
public virtual Notification Notification { get; set; }
public virtual User User { get; set; }
}
UserViewModel class
public class UserViewModel : IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public IList<NotificationViewModel> Notifications { get; set; }
}
NotificationViewModel class
public class NotificationViewModel
{
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public bool HasRead { get; set; } // this is the difference
}
In order to fix up the HasRead, maybe you can utilize the AfterMap(Action<TSource, TDestination> afterFunction) function. It's not as elegant as the rest of automapper, but it might work.
CreateMap<User, UserViewModel>()
.ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)))
.AfterMap((src, dest) =>
{
foreach (var notificationVM in dest.Notifications)
{
notificationVM.HasRead = src.UserNotification.Where(x => x.NotificationId == notificationVM.Id).Select(x => x.HasRead).FirstOrDefault();
}
});

Categories