EF Working With One To Many Models With Composite Keys - c#

I'm trying to learn .Net Core 2.1 and testing a little project with already existing database. We have 3 tables (Template, TemplateDesc, TemplateParameter) with one of them has one-to-many relationship. When I get one Template with controller, it returns null for TemplateDescriptions and ParameterValues. Also if I try to delete Template, it returns FK exception. Can someone point the problem with the below codes?
Note : I use Swagger extension to test codes.
public class Template
{
public decimal CompanyCode { get; set; }
public string TemplateCode { get; set; }
public List<TemplateDesc> TemplateDescriptions { get; set; }
public DateTime TemplateDate { get; set; }
public string RuleCode { get; set; }
public string SourceTypeCode { get; set; }
public string Description { get; set; }
public bool IsBlocked { get; set; }
public string CreatedUserName { get; set; }
public DateTime CreatedDate { get; set; }
public List<TemplateParameter> ParameterValues { get; set; }
}
public class TemplateDesc
{
public decimal CompanyCode { get; set; }
public string TemplateCode { get; set; }
public string LangCode { get; set; }
public string TemplateDescription { get; set; }
}
public class TemplateParameter
{
public decimal CompanyCode { get; set; }
public string TemplateCode { get; set; }
public string TemplateRuleCode { get; set; }
public string ParameterName { get; set; }
public string ParameterValue { get; set; }
}
modelBuilder.Entity<Template>(entity =>
{
entity.HasKey(e => new { e.CompanyCode, e.TemplateCode });
entity.HasMany(e => e.TemplateDescriptions).WithOne(e => e.Template).HasForeignKey(e => new { e.CompanyCode, e.TemplateCode });
entity.HasMany(e => e.ParameterValues).WithOne(e => e.Template).HasForeignKey(e => new { e.CompanyCode, e.TemplateCode });
}
modelBuilder.Entity<TemplateDesc>(entity =>
{
entity.HasKey(e => new { e.CompanyCode, e.TemplateCode, e.LangCode });
entity.HasOne(e => e.Template).WithMany(e => e.TemplateDescriptions).HasForeignKey(e => new { e.CompanyCode, e.TemplateCode }).OnDelete(DeleteBehavior.Cascade);
}
modelBuilder.Entity<TemplateParameter>(entity =>
{
entity.HasKey(e => new { e.CompanyCode, e.TemplateCode, e.TemplateRuleCode, e.ParameterName});
entity.HasOne(e => e.Template).WithMany(e => e.ParameterValues).HasForeignKey(e => new { e.CompanyCode, e.TemplateCode}).OnDelete(DeleteBehavior.Cascade);
}
[HttpGet]
public ActionResult<Template> GetWithKey([FromQuery] decimal companyCode, [FromQuery] string templateCode)
{
try
{
var template = this.mRepository.Find(e => e.CompanyCode == companyCode && e.TemplateCode.AreEqual(templateCode)).FirstOrDefault();
if (template == null)
return new JsonResult(new ApiResponse<Template>(ResponseType.Exception, null));
return new JsonResult(new ApiResponse<Template>(ResponseType.Success, template));
}
catch
{
throw;
}
}
[HttpDelete]
public ActionResult DeleteWithKey([FromQuery] decimal companyCode, [FromQuery] string templateCode)
{
if (this.mRepository.Find(e => e.CompanyCode == companyCode && e.TemplateCode.AreEqual(templateCode)).Count() < 1)
return new JsonResult(new ApiResponse<string>(ResponseType.NotFound, templateCode));
this.mRepository.Delete(companyCode, templateCode);
return new JsonResult(new ApiResponse<Template>(ResponseType.Success, null));
}

You have to either use lazy loading, or use Include construct:
db.Templates
.Include(x => x.TemplateDesc)
.Include(x => x.TemplateParameter)
.FirstOrDefault(...)
the includes can be put in extension method:
public IQueryable<Template> BuildTemplate(this DbSet<Template> set)
{
return set.Include(x => x.TemplateDesc)
.Include(x => x.TemplateParameter);
}
Then you can use
dbContext.Templates.BuildTemplate.FirstOrdefault(x => x.TemplateDescriptions.Any(td => td.TemplateCode == "xyz"));

Related

How to select an attribute with association ASP.NET Core 5.0

I have the following entities into my project
MnUsuario...
public MnUsuario()
{
MnRolUsuarios = new HashSet<MnRolUsuario>();
LogLogin = new HashSet<LogLogin>();
}
public int IdUsuario { get; set; }
public int IdEmpresa { get; set; }
public string Usuario { get; set; }
public string Login { get; set; }
public byte[] Password { get; set; }
public string Email { get; set; }
public virtual MnEmpresa IdEmpresaNavigation { get; set; }
public virtual ICollection<MnRolUsuario> MnRolUsuarios { get; set; }
public virtual ICollection<LogLogin> LogLogin { get; set; }
public string passwords { get; set; }
public int RolID { get; set; }
public bool RememberMe { get; set; }
MnEmpresa...
public partial class MnEmpresa
{
public MnEmpresa()
{
MnAplicactivos = new HashSet<MnAplicactivo>();
MnUsuarios = new HashSet<MnUsuario>();
}
public int IdEmpresa { get; set; }
public int? CodEmpresa { get; set; }
public string Empresa { get; set; }
public string Contacto { get; set; }
public string Imagen { get; set; }
public virtual ICollection<MnAplicactivo> MnAplicactivos { get; set; }
public virtual ICollection<MnUsuario> MnUsuarios { get; set; }
}
PBIContext...
public virtual DbSet<MnEmpresa> MnEmpresas { get; set; }
public virtual DbSet<MnUsuario> MnUsuarios { get; set; }
And my question here is... how can I access to the "Imagen" into MnEmpresas if i have the user MnUsuario, in SQL it's easy... Select with where condition for the relation, but in ASP.NET Core I don't know how to do it, I need to catch the image here...
MnUsuario user = null;
object[] parameters = new object[] { userName, password };
MnUsuario listad = _context.MnUsuarios.FirstOrDefault();
List<MnUsuario> listaUsuarios = await _context.MnUsuarios.FromSqlRaw<MnUsuario>("CALL sp_validate_login( {0}, {1})", parameters).ToListAsync();
user = listaUsuarios.FirstOrDefault();
if (user != null)
{
var ids = _context.MnRolUsuarios
.Include(t => t.IdRolNavigation)
.ThenInclude(t => t.IdAplicativoNavigation)
.ThenInclude(t => t.IdEmpresaNavigation)
.Where(x => x.IdUsuario == user.IdUsuario)
.Select(x => new { x.IdRol, x.IdRolNavigation.IdAplicativoNavigation.IdEmpresaNavigation.Imagen });
string consRolID = string.Join(',', ids.Select(x => x.IdRol).ToArray());
string imagen = ids.FirstOrDefault()?.Imagen ?? string.Empty;
string img = // Code here
}
Where it's the comment "Code here"
If you wanna return a anonymous type results in linq, you need to use = instead of ::
var ids = _context.MnRolUsuarios.Include(t => t.IdRolNavigation).ThenInclude(t => t.IdAplicativoNavigation)
.ThenInclude(t => t.IdEmpresaNavigation)
.Where(x => x.IdUsuario == user.IdUsuario)
.Select(x => new { x.IdRol, Imagen = x.IdRolNavigation.IdAplicativoNavigation.IdEmpresaNavigation.Imagen });
Then you can use ids.FirstOrDefault()?.Imagen to get the Imagen
If you change:
var ids = _context.MnRolUsuarios.Include(t => t.IdRolNavigation).ThenInclude(t => t.IdAplicativoNavigation)
.ThenInclude(t => t.IdEmpresaNavigation)
.Where(x => x.IdUsuario == user.IdUsuario)
.Select(x => new { x.IdRol, x.IdRolNavigation.IdAplicativoNavigation.IdEmpresaNavigation.Imagen });//give the property a name
To:
var ids = _context.MnRolUsuarios.Include(t => t.IdRolNavigation).ThenInclude(t => t.IdAplicativoNavigation)
.ThenInclude(t => t.IdEmpresaNavigation)
.Where(x => x.IdUsuario == user.IdUsuario)
.Select(x => new { x.IdRol, Imagen= x.IdRolNavigation.IdAplicativoNavigation.IdEmpresaNavigation.Imagen });
Then you could use the line above the comment.

Unknown column in field list error using Complex types in EF Core 3

For context, I'm in the process of migrating our EF6 Db Context to EF Core 3. Why EF Core 3 only? Currently we're not able to upgrade to the latest EF Core version because of project constraints. We're still using .NET Framework 4.5.6, we're slowly upgrading.
Libaries used
EF Core 3.1.19
Devart.Data.MySql.Entity.EFCore 8.19
The models
public class AutomatedInvestigation
{
public int AutomatedSearchScreenshotId { get; set; }
public int OrderId { get; set; }
public int OrderLineItemId { get; set; }
public int ServiceId { get; set; }
public int? ComponentId { get; set; }
public OrderLineItemResults Result { get; set; }
public int? PageSourceDocumentId { get; set; }
public string Errors { get; set; } = string.Empty;
public DateTime CreateDateTime { get; set; }
public DateTime EditDateTime { get; set; }
public SearchRequestParameters SearchParameters { get; set; }
public Service Service { get; set; }
public Subject Subject { get; set; }
public Order Order { get; set; }
public OrderLineItem OrderLineItem { get; set; }
public virtual Component Component { get; set; }
}
[ComplexType]
public class SearchRequestParameters
{
public SearchRequestParameters()
{
this.Serialized = string.Empty;
}
[NotMapped]
[JsonIgnore]
public string Serialized
{
get { return JsonConvert.SerializeObject(Parameters); }
set
{
if (string.IsNullOrEmpty(value)) return;
var parameters = JsonConvert.DeserializeObject<SearchParameters>(value);
Parameters = parameters ?? new SearchParameters();
}
}
public SearchParameters Parameters { get; set; }
}
[ComplexType]
public class SearchParameters
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public DateTime DOB { get; set; }
public string State { get; set; } = string.Empty;
}
The model builder (excluded irrelevant code)
internal static ModelBuilder BuildAutomationInvestigationModel(this ModelBuilder modelBuilder)
{
var entityTypeBuilder = modelBuilder.Entity<AutomatedInvestigation>();
entityTypeBuilder.OwnsOne(s => s.SearchParameters, sa =>
{
sa.OwnsOne(p => p.Parameters, pa =>
{
pa.Property(p => p.FirstName);
pa.Property(p => p.LastName);
pa.Property(p => p.DOB);
pa.Property(p => p.State);
});
});
entityTypeBuilder.ToTable("automated_investigations")
.HasKey(p => p.AutomatedSearchScreenshotId);
entityTypeBuilder.MapProperties()
.MapRelations();
return modelBuilder;
}
private static EntityTypeBuilder<AutomatedInvestigation> MapProperties(this EntityTypeBuilder<AutomatedInvestigation> entityTypeBuilder)
{
entityTypeBuilder.Property(p => p.AutomatedSearchScreenshotId).HasColumnName("automated_investigation_id");
entityTypeBuilder.Property(p => p.OrderId).HasColumnName("order_id").IsRequired();
entityTypeBuilder.Property(p => p.OrderLineItemId).HasColumnName("order_line_item_id").IsRequired();
entityTypeBuilder.Property(p => p.ServiceId).HasColumnName("service_id").IsRequired();
entityTypeBuilder.Property(p => p.ComponentId).HasColumnName("component_id").IsRequired(false);
entityTypeBuilder.Property(p => p.Result).IsRequired();
entityTypeBuilder.Property(p => p.PageSourceDocumentId).IsRequired(false);
entityTypeBuilder.Property(e => e.Errors)
.IsRequired()
.HasColumnName("errors")
.HasColumnType("mediumtext");
entityTypeBuilder.Property(p => p.CreateDateTime).HasColumnName("create_datetime").IsRequired();
entityTypeBuilder.Property(p => p.EditDateTime).HasColumnName("edit_datetime").IsRequired();
return entityTypeBuilder;
}
The error
I've tried adding HasColumnName but throws the same error. I've also tried using [Owned] annotation instead of the OwnsOne on the model builder but throws the same error. Also tried just specifying "SearchParameters" but will throw unknown column on "Parameters".

How do I map Objects in this scenario using c# AutoMapper

This is the Source Object I want to map
public class Post
{
public string PostId { get; set; }
public string PostTitle { get; set; }
public virtual List<Comment> Comments { get; set; }
}
This destination object i want to map the source to
public class PostResponse
{
public int PostId { get; set; }
public string PostTitle { get; set; }
public IEnumerable<CommentObj> Comments { get; set; }
}
This is the Controller throwing the error
AutoMapper.AutoMapperMappingException: Error mapping types.
[HttpGet(ApiRoutes.Posts.GetAll)]
public async Task<IActionResult> GetAll()
{
var posts = await _postServices.GetPostsAsync();
return Ok(_mapper.Map<List<PostResponse>>(posts));
}
This is the Service
public async Task<List<Post>> GetPostsAsync()
{
var queryable = _dataContext.Posts.AsQueryable();
var psts = await queryable.Include(x => x.Comments).ToListAsync();
return psts;
}
This is the Mapping Profile
public DomainResponseProfile()
{
CreateMap<Post, PostResponse>().
ForMember(dest => dest.Comments, opt => opt.MapFrom(src => src.Comments.Select(x => new CommentResponse
{ PostId = x.PostId, DateCommented = x.DateCommented })));
}
This is the Domain Comment Object
public class Comment
{
public int CommentId { get; set; }
public int PostId { get; set; }
}
This is the Response Comment Object
public class CommentResponse
{
public int CommentId { get; set; }
public List<CommentObj> Comments { get; set; }
}
I just found out what I did wrong.
wrong
CreateMap<Post, PostResponse>().
ForMember(dest => dest.Comments, opt => opt.MapFrom(src => src.Comments.Select(x => new CommentResponse
{ PostId = x.PostId, DateCommented = x.DateCommented })));
right
CreateMap<Post, PostResponse>().
ForMember(dest => dest.Comments, opt => opt.MapFrom(src =>
src.Comments));

Is there a way to do a loop in the select part of a linq query?

I am returning CommissionDocuments for a user. The CommissionDocument contains many policies that the user gets commission for.
So I want to sum up the commission for each policy in the CommissionDocument.
I've tried using the Sum() method that didn't work.
Query
public async Task<List<Commission>> FetchAsync(Agent agent)
{
return await _agentsContext.ScanCommDoc
.Include(x => x.CommissionStatement)
.Where(x => x.AgentId == agent.AgentId && x.Type == FileType.CommissionStatement
&& x.CommissionStatement.PaymentYear == x.ScanDate.AddMonths(-1).Year
&& x.CommissionStatement.PaymentMonth == x.ScanDate.AddMonths(-1).Month)
.Select(x => new AgentCommission { ScanDate = x.ScanDate, FileUrl = x.FileUrl Commission = (x.AgentCommission .Amount + x.AgentCommission .Vat) })
.GroupBy(x => x.ScanDate).Select(x => x.FirstOrDefault()).Take(2)
.OrderByDescending(x => x.ScanDate).ToListAsync();
Agent Commission(DB Table)
public class AgentCommissionStatement
{
public int BrokerId { get; set; }
public Byte PaymentMonth { get; set; }
public Int16 PaymentYear { get; set; }
public decimal Amount { get; set; }
public decimal Vat { get; set; }
}
Commission Mapping Class
public class AgentCommission
{
public int AgentId { get; set; }
public string FileUrl { get; set; }
public Guid? FileGuid { get; set; }
public DateTime ScanDate { get; set; }
public Decimal? Commission { get; set; }
}
public class ScanCommDoc
{
public int Id { get; set; }
public int AgentId { get; set; }
public string FileUrl { get; set; }
public Guid? FileGuid { get; set; }
public PolicyDetail Policy { get; set; }
public FileType Type { get; set; }
public DateTime ScanDate { get; set; }
public CommissionStatement CommissionStatement { get; set; }
}
Thank you for all your help and suggestions.
Below is what I was trying to do.
I was able to get the sum of the commission.
public async Task<List<Commission>> FetchAsync(Agent agent)
{
return await _agentsContext.ScanCommDoc
.Include(x => x.CommissionStatement)
.Where(x => x.AgentId == agent.AgentId && x.Type == FileType.CommissionStatement
&& x.CommissionStatement.PaymentYear == x.ScanDate.AddMonths(-1).Year
&& x.CommissionStatement.PaymentMonth == x.ScanDate.AddMonths(-1).Month)
.Select(x => new { x.ScanDate, x.FileUrl, x.FileGuid, commission = (x.CommissionStatement.Amount + x.CommissionStatement.Vat) })
.GroupBy(x => new { x.FileGuid, x.FileUrl, x.ScanDate })
.Select(p => new AgentCommission
{
FileGuid = p.Key.FileGuid,
FileUrl = p.Key.FileUrl,
ScanDate = p.Key.ScanDate,
Commission = p.Sum(x => x.commission)
})
.OrderByDescending(x => x.ScanDate).Take(10).ToListAsync();
}

ASP.Net C# MVC Issue Mapping Domain Model to ViewModel using AutoMapper

I'm having a little difficulty mapping a domain model to a view model, using AutoMapper.
My controller code is:
//
// GET: /Objective/Analyst
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM();
ovm.DatePeriod = new DateTime(2013, 8,1);
var objectives = db.Objectives.Include(o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm.ObList = Mapper.Map<IList<Objective>, IList<ObjectiveVM>>(objectives);
return View(ovm);
}
I am getting an error on the ovm.ObList = Mapper.... (ObList is underlined in red with the error):
'ObList': cannot reference a type through an expression; try 'Objectives.ViewModels.ObjectiveVM.ObList' instead
My Objective Class is:
public class Objective
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public Analyst Analyst { get; set; }
}
My ObjectiveVM (view model) is:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
In my startup/global.asax.cs I have used AutoMapper to map the Objective to the ObjectiveVM:
Mapper.CreateMap<Objective, ObjectiveVM.ObList>()
.ForMember(dest => dest.Include, opt => opt.Ignore())
.ForMember(dest => dest.AnalystName, opt => opt.MapFrom(y => (y.Analyst.AnalystName)));
Any help would be much appreciated,
Mark
Ok, thanks for all the suggestions - what I've ended up with is:
Controller:
//
// GET: /Objective/Analyst
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM().obList;
var objectives = db.Objectives.Include(o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm = Mapper.Map<IList<Objective>, IList<ObjectiveVM.ObList>>(objectives);
var ovm2 = new ObjectiveVM();
ovm2.obList = ovm;
ovm2.DatePeriod = new DateTime(2013, 8,1);
return View(ovm2);
}
ViewModel:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public IList<ObList> obList { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
CreateMap:
Mapper.CreateMap<Objective, ObjectiveVM.ObList>()
.ForMember(dest => dest.Include, opt => opt.Ignore())
.ForMember(dest => dest.AnalystName, opt => opt.MapFrom(y => (y.Analyst.AnalystName)))
;
If I've mis-understood any advice, and you provided the answer, please post it - and I'll mark it as such.
Thank you,
Mark
As the commenter nemesv has rightly mentioned, the problem is about
ovm.ObList = Mapper.Map<IList<Objective>, IList<ObjectiveVM>>(objectives);
ObList is not a member of ObjectiveVM so, you should change the ObjectiveVM like this:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public IList<ObList> obList { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
Update:
Controller:
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM { DatePeriod = new DateTime(2013, 8, 1) };
var objectives = db.Objectives.Include(
o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm.obList = Mapper.Map<IList<Objective>,
IList<ObjectiveVM.ObList>>(objectives);
return View(ovm);
}

Categories