Loading multiple levels of nested navigation and collection properties with proxy disabled - azure

I'm having a hard time figuring out how to do queries with levels of alternating collection and navigation properties with proxy and lazy loading disabled to serialize the result.
public ApplicationDbContext()
: base("Debug")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
The entities look like this (omitting non-navigation or non-collections):
public class Comprobante: ComprobanteGeneric
{
}
public class ComprobanteGenericStructure
public int Id { get; set; }
[Required]
public virtual Conceptos Conceptos { get; set; }
}
public class Conceptos
{
public Conceptos()
{
Concepto = new List<Concepto>();
}
[ForeignKey("Comprobante")]
public int Id { get; set; }
[Required]
public virtual ICollection<Concepto> Concepto { get; set; }
public virtual Comprobante Comprobante { get; set; }
}
public class Concepto :RelatedComprobante
{
[Key]
public int Id { get; set; }
public virtual OrderOfImpuestosForConceptos Impuestos { get; set; }
public virtual Conceptos Conceptos { get; set; }
}
public class RelatedComprobante
{
[ForeignKey("Comprobante")]
public int ComprobanteId { get; set; }
public virtual Comprobante Comprobante { get; set; }
}
public class OrderOfImpuestosForConceptos : RelatedComprobante
{
public int Id { get; set; }
public virtual TrasladosNode Traslados { get; set; }
public virtual Retenciones Retenciones { get; set; }
}
public class TrasladosNode : RelatedComprobante
{
public TrasladosNode()
{
Traslado = new HashSet<Traslado>();
}
[Key]
public int Id { get; set; }
[Required]
public virtual ICollection<Traslado> Traslado { get; set; }
}
public class Traslado : RelatedComprobante
{
public int Id { get; set; }
public virtual TrasladosNode TrasladosNode { get; set; }
}
public class Retenciones : RelatedComprobante
{
public Retenciones()
{
Retencion = new List<Retencion>();
}
public int Id { get; set; }
[Required]
public virtual ICollection<Retencion> Retencion { get; set; }
}
public class Retencion : RelatedComprobante
{
public int Id { get; set; }
public virtual Retenciones Retenciones { get; set; }
}
I need the whole graph. With lazy loading enabled it returns it all eventually but serialization takes several seconds and several queries to DB. I disabled it and tried the following query:
comprobantes = _db.Comprobante
.Include(c => c.Conceptos.Concepto.Select(co => co.Impuestos.Retenciones).Select(r=>r.Retencion))
.Include(c => c.Conceptos.Concepto.Select(co => co.Impuestos.Traslados).Select(t => t.Traslado));
However I get an empty concepto list:
[{
"id": 324,
"conceptos": {
"concepto": []
}
},
{
"id": 340,
"conceptos": {
"concepto": []
}
}
}]
Even simpler queries like the following yield the exact same results:
comprobantes = _db.Comprobante
.Include(c => c.Conceptos.Concepto.Select(co => co.Impuestos));
I know is not the serialization for when I inspect the object while debugging is empty:
What am I doing wrong?

Thanks to David comment I was able to build a working query(es):
var comprobantes = _db.Comprobante
.Include(b => b.Conceptos.Concepto.Select(c => c.Impuestos.Retenciones.Retencion))
.Include(b => b.Conceptos.Concepto.Select(c => c.Impuestos.Traslados.Traslado));
foreach (var comprobante in comprobantes)
{
_db.ComprobanteTraslado.Where(t => t.ComprobanteId == comprobante.Id);
_db.ComprobanteRetencion.Where(r => r.ComprobanteId == comprobante.Id);
}

Related

EF Core with AutoMapper

I have a DTO containing properties and a Model, eg student can have more than one module and a module can be associated with more than one student. the properties are mapping fine but the Model doesn't map.
public class GetStudentByIdMapping : Profile
{
public GetStudentByIdMapping()
{
CreateMap<Student,StudentDetails>();
CreateMap<Module, StudentDetails>()
.ForPath(dest => dest.StudentModules.ModuleName, opt => opt.MapFrom(m => m.ModuleName))
.ForPath(dest => dest.StudentModules.ModuleCode, opt => opt.MapFrom(m => m.ModuleCode))
.ForPath(dest => dest.StudentModules.Description, opt => opt.MapFrom(m => m.Description))
.ReverseMap();
}
}
public async Task<StudentDetails> GetStudent(int studentId)
{
var student = context.Student
.Where(s => s.StudentId == studentId)
.FirstOrDefault();
var module = await context.Order
.Include(m => m.Module)
.Where(o => o.StudentId == studentId)
.Select(m => m.Module).ToListAsync();
var studMap = Mapper.Map<StudentDetails>(student);
Mapper.Map<StudentDetails>(module);
return studMap;
}
These are the ViewModels I want to map to the Models Model in the StudentDetails ViewModel
public class StudentDetails
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public StudentModule StudentModules { get; set; }
}
public class StudentModule
{
public string ModuleName { get; set; }
public string ModuleCode { get; set; }
public string Description { get; set; }
}
These are my Entities generated by EF Core
public partial class Student
{
public Student()
{
Order = new HashSet<Order>();
}
public int StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public virtual ICollection<Order> Order { get; set; }
}
public partial class Module
{
public Module()
{
Order = new HashSet<Order>();
}
public int ModuleId { get; set; }
public int? LectureId { get; set; }
public string ModuleName { get; set; }
public string ModuleCode { get; set; }
public string Description { get; set; }
public string ModulePath { get; set; }
public virtual Lecture Lecture { get; set; }
public virtual ICollection<Order> Order { get; set; }
}
You only need to pass the created destination to the second map call:
Just try the following code when mapping:
var studMap = Mapper.Map<Student,StudentDetails>(student);
Mapper.Map<Module,StudentDetails>(module,studMap);
Then the studMap will receive all mapped fields value.

Sending different entity types to same Blazor component

I have a blazor component that I want to pass data retreived from a backend. Each set of data has a different entity name so I'm having trouble converting the entity to a generic type when it gets to the component. Where is my error? Thanks
Getting the following error on ChildData=#sysList in the parent:
Argument1: Cannot convert from Test2.Shared.Models.MeasurementSystem[] to System.Collections.Generic.IReadOnlyList[]
Parent
<ChildComponent ChildData=#sysList TItem=#Test2.Shared.Models.MeasurementSystem</ChildComponent>
<ChildComponent ChildData=#unitList TItem=#Test2.Shared.Models.MeasurementUnit</ChildComponent>
<ChildComponent ChildData=#groupList TItem=#Test2.Shared.Models.MeasurementGroup</ChildComponent>
#functions {
private Test2.Shared.Models.MeasurementSystem[] sysList { get; set; }
private Test2.Shared.Models.MeasurementUnit[] unitList { get; set; }
private Test2.Shared.Models.MeasurementGroup[] groupList { get; set; }
protected override async Task OnInitAsync()
{
sysList = await Http.GetJsonAsync<Test2.Shared.Models.MeasurementSystem[]>("/api/System/Index");
unitList = await Http.GetJsonAsync<Test2.Shared.Models.MeasurementUnit[]>("/api/Unit/Index");
groupList = await Http.GetJsonAsync<Test2.Shared.Models.MeasurementGroup[]>("/api/Group/Index");
}
}
Child
#typeparam TItem
#foreach (var data in ChildData)
{
<p #data.Name>
}
#functions {
[Parameter] private IReadOnlyList<TItem>[] ChildData { get; set; }
}
Classes
public class MeasurementSystem
{
public int SystemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
public class MeasurementUnit
{
public int UnitId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Factor { get; set; }
public int Magnitude { get; set; }
}
}
public class MeasurementGroup
{
public int GroupId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}

Automapper to map child tables

I have 3 tables in my database and these are the classes:
public class PRAT
{
public PRAT()
{
PRAT_RIC = new HashSet<PRAT_RIC>();
}
public int ID { get; set; }
public string PRATICA { get; set; }
public int ANNO { get; set; }
public string VARIANTE { get; set; }
[...] other fields
}
public class PRAT_RIC
{
public int ID { get; set; }
public int IDPRAT { get; set; }
public int IDANAG { get; set; }
public int? IDTITRIC { get; set; }
public virtual ANAGRAFI IDANAGNavigation { get; set; }
}
public class ANAGRAFI
{
public int ID { get; set; }
public string TIPO { get; set; }
public string TITOLO { get; set; }
public string COGNOME { get; set; }
public string NOME { get; set; }
public string CODFISC { get; set; }
}
So, the relations are:
Prat -> Prat_Ric (Prat.Id=Prat_Ric.IdPrat) -> Anagrafi (Anagrafi.Id=Prat_Ric.IdAnag)
These are the destination classes:
public class PraticaResource
{
public int ID { get; set; }
public string PRATICA { get; set; }
public int ANNO { get; set; }
public string VARIANTE { get; set; }
public ICollection<RichiedentiResource> RICHIEDENTI { get; set; }
}
public class RichiedentiResource
{
public int ID { get; set; }
public int IDANAG { get; set; }
public string TITOLO { get; set; }
public string COGNOME { get; set; }
public string NOME { get; set; }
public string CODFISC { get; set; }
public string TITRIC { get; set; }
}
I created the map with
CreateMap<PRAT_RIC, RichiedentiResource>()
.ForMember(p => p.COGNOME, opt => opt.MapFrom(ti => ti.IDANAGNavigation.COGNOME))
.ForMember(p => p.NOME, opt => opt.MapFrom(ti => ti.IDANAGNavigation.NOME));
Is it possible to map all the properties of RichiedentiResource to the ANAGRAFI fields with the same name so that i haven't to write every single field mapping?
Thank you.
If you don't need anything from the navigation class, then you can just skip it when mapping at the top level:
CreateMap<PRAT, PraticaResource>()
.ForMember(p => p.PRAT_RIC, opt => opt.MapFrom(src => src.PRAT_RIC.Select(x => x.IDANAGNavigation))
Then you just need the mapping from ANAGRAFI -> RichiedentiResource, which should work out the field mappings automatically.

AutoMapper 6 - How can I create a mapping that ignore and map a list of object

I'm a little newbie on AutoMapper, I don't find almost nothing about v6.0 on Stackoverflow and Github. I need help on this problem
I have this two Entities:
public class DocFinanceiro
{
public int AutoId { get; set; }
public virtual ICollection<QuitacaoDocFinan> QuitacoesDocFinan { get; set; }
}
public class QuitacaoDocFinan
{
public int AutoId { get; set; }
public int DocFinanceiroId { get; set; }
public virtual DocFinanceiro DocFinanceiro { get; set; }
public decimal ValorTotal { get; set; }
}
}
And his ViewModels:
public class DocFinanceiroViewModel
{
public DocFinanceiroViewModel()
{
ValorPago = QuitacoesDocFinan.Where(x => x.Cancelada == false).Sum(x => x.ValorTotal);
}
public virtual ICollection<QuitacaoDocFinanViewModel> QuitacoesDocFinan { get; set; }
public decimal ValorPago { get; set; }
}
public class QuitacaoDocFinanViewModel
{
public int AutoId { get; set; }
public int DocFinanceiroId { get; set; }
public virtual DocFinanceiroViewModel DocFinanceiro { get; set; }
public decimal ValorTotal { get; set; }
}
And mapping between DocFinanceiro and DocFinanceiroViewModel:
public class DomainToViewModelMappingProfile : Profile
{
public DomainToViewModelMappingProfile()
{
CreateMap<DocFinanceiro, DocFinanceiroViewModel>().ForMember(x => x.ValorPago, y => y.Ignore())
.MaxDepth(3)
.PreserveReferences();
CreateMap<QuitacaoDocFinan, QuitacaoDocFinanViewModel>();
}
}
This mapping works when I set only one of these property
.ForMember(x => x.ValorPago, y => y.Ignore())
or
.MaxDepth(1).PreserveReferences();
, but when I try two cause an exception. I search on everywhere, but no success.
And controller where that I make the mapping:
var documentos = Mapper.Map<IEnumerable<DocFinanceiro>, IEnumerable<DocFinanceiroViewModel>>(*repository*);
Sorry if make some mistake, but I don't what to do...
You are trying to access QuitacoesDocFinan collection in DocFinanceiroViewModel constructor before initializing. Your DocFinanceiroViewModel should be something like this:
public class DocFinanceiroViewModel
{
public virtual ICollection<QuitacaoDocFinanViewModel> QuitacoesDocFinan { get; set; }
public decimal ValorPago
{
get
{
return QuitacoesDocFinan.Where(x => x.Cancelada == false).Sum(x => x.ValorTotal);
}
}
}

entity framework looking in different table during unittests

I have a single DbContext in a library along with entities and their configuration in fluent api.
I use this same context in two different projects.
The first one is a test project and the second one a simple console application.
I execute a query including some of it's relations like this:
var vacancies = (from v in ctx.Vacancies
.Include("Categories")
.Include("Levels")
.Include("Contracts")
.Include("JobTypes"));
Strangely enough the console application works perfectly, however the unittest starts looking in the wrong tables all of a sudden.
Both applications produce very different queries when i look at them.
I'm using EF5 atm and I've been stuck on this for a few days now with not a single clue to what might cause this problem.
EDIT:
The point is i don't do any different queries, i also only have on context and only one connectionstring which i have confirmed during runtime connects to the right database.
All i can say is that i feel like the fluent api has no effect in unittests even though it hits the code just fine like it should.
public class SchedulerContext : BaseContext<SchedulerContext>{
public DbSet<Click> Clicks { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<ContractType> Contracts { get; set; }
public DbSet<JobType> JobTypes { get; set; }
public DbSet<Level> Levels { get; set; }
public DbSet<ExternalMapping> ExternalMappings { get; set; }
public DbSet<GeoName> GeoNames { get; set; }
public DbSet<GeoAlternateName> GeoAlternateNames { get; set; }
public DbSet<Trigger> JobTriggers { get; set; }
public DbSet<RegistryValue> RegistryValues { get; set; }
public DbSet<JobResult> JobResults { get; set; }
public DbSet<JobInfo> JobInfo { get; set; }
public DbSet<JobError> JobErrors { get; set; }
public DbSet<JobNotification> JobNotifications { get; set; }
public DbSet<Company> Customers { get; set; }
public DbSet<Vacancy> Vacancies { get; set; }
public SchedulerContext() {
//this.Configuration.ProxyCreationEnabled = false;
//this.Configuration.LazyLoadingEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Configurations.Add(new VacancyEntityTypeConfiguration());
base.OnModelCreating(modelBuilder);
}
}
That is my context.
And my entity configuration:
public class VacancyEntityTypeConfiguration : EntityTypeConfiguration<Vacancy> {
public VacancyEntityTypeConfiguration() {
this.HasMany(c => c.Levels).
WithMany().
Map(
m => {
m.ToTable("VacancyLevels");
});
this.HasMany(c => c.Categories).
WithMany().
Map(
m => {
m.ToTable("VacancyCategories");
});
this.HasMany(c => c.Contracts).
WithMany().
Map(
m => {
m.ToTable("VacancyContractTypes");
});
this.HasMany(c => c.Jobtypes).
WithMany().
Map(
m => {
m.ToTable("VacancyJobTypes");
});
}
}
And the exact method which shows the problem.
public List<Vacancy> GetVacanciesForCustomer(Company customer, bool onlineInclusive) {
using (var ctx = new JHSchedulerContext.SchedulerContext()) {
var vacancies = (from v in ctx.Vacancies.Include("Categories").Include("Levels").Include("Contracts").Include("JobTypes") where v.SourceSite == customer.SourceSite && (v.Online || onlineInclusive == false) select v).ToList();
return vacancies.ToList();
}
}
And the Vacancy class:
public class Vacancy {
public Vacancy()
{
this.Language = "";
}
[SolrUniqueKey("id")]
[Key]
[Required]
public int Id { get; set; }
[StringLength(50, ErrorMessage = "Maximum length 50 characters")]
public string ExternalId { get; set; }
[SolrField("title")]
[StringLength(1024, ErrorMessage = "Maximum length 1024 characters")]
public string Title { get; set; }
[SolrField("company")]
[StringLength(1024, ErrorMessage = "Maximum length 1024 characters")]
public string Company { get; set; }
[StringLength(4096, ErrorMessage = "Maximum length 4096 characters")]
public string CompanyText { get; set; }
[SolrField("location")]
[StringLength(1024, ErrorMessage = "Maximum length 1024 characters")]
public string Location { get; set; }
[SolrField("url")]
[StringLength(1024, ErrorMessage = "Maximum length 1024 characters")]
public string Url { get; set;}
[SolrField("source")]
[StringLength(1024, ErrorMessage = "Maximum length 1024 characters")]
public string SourceSite { get; set; }
private string text;
[SolrField("text")]
[StringLength(4096, ErrorMessage = "Maximum length 4096 characters")]
public string Text {
get {
return StringHelper.TruncateAtWord(text, 500);
//return new String(text.Take(500).ToArray());
//return text.Substring(text.Length < 900 ? 0 : (text.Length / 2), (int)Math.Min(text.Length, 500));
}
set{
text = value;
}
}
[StringLength(4096, ErrorMessage = "Maximum length 4096 characters")]
public string TextProfile { get; set; }
[StringLength(4096, ErrorMessage = "Maximum length 4096 characters")]
public string TextOffer { get; set; }
[NotMapped]
[ScriptIgnore]
public SqlGeography Coordinate {
get
{
if (Latitude.HasValue && Longitude.HasValue)
return SqlGeography.Point(Latitude.Value, Longitude.Value, 4326);
else
return null;
}
}
[SolrField("coordinate_0_coordinate")]
public double? Latitude { get; set; }
[SolrField("coordinate_1_coordinate")]
public double? Longitude { get; set; }
[StringLength(1024, ErrorMessage = "Maximum length 1024 characters")]
public string NormalizedLocation { get; set; }
public DateTime? ScrapedDate { get; set; }
[SolrField("insertdate")]
public DateTime? ImportDate { get; set; }
public DateTime? ExpireDate { get; set; }
[NotMapped]
public string DaysAgo {
get {
if (ImportDate != null)
return (DateTime.Now - (DateTime)ImportDate).Days.ToString();
else {
return "N/A";
}
}
}
[SolrField("category")]
public ICollection<string> CategoryNames {
get { return Categories != null ? Categories.Select(c => c.Name).ToList() : null; }
}
[SolrField("level")]
public ICollection<string> LevelNames {
get { return Levels != null ? Levels.Select(l => l.Name).ToList() : null; }
}
[SolrField("contract")]
public ICollection<string> ContractNames {
get { return Contracts != null ? Contracts.Select(c => c.Name).ToList() : null; }
}
[SolrField("time")]
public ICollection<string> JobTypeNames {
get { return Jobtypes != null ? Jobtypes.Select(c => c.Name).ToList() : null; }
}
public string ContactName { get; set; }
[Required]
public bool Online { get; set; }
[NotMapped]
public int? Distance { get; set; }
public string Language { get; set; }
public string PriorityType { get; set; }
public int NumClicks { get; set; }
[NotMapped]
public string GridContactName {
get{
return string.Format("{0} (Total Clicks:{1})",ContactName,NumClicks);
}
}
public virtual ICollection<Level> Levels { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<ContractType> Contracts { get; set; }
public virtual ICollection<JobType> Jobtypes { get; set; }
public override string ToString() {
return string.Format("Id={0} ExternalId={1} Title={2} Company={3} Location={4} Url={5} SourceSite={6} Text={7} NormalizedLocation={8} InsertDate={9} ImportDate={10} DaysAgo={11}",Id, ExternalId,Title,Company,Location,Url, SourceSite,Text,NormalizedLocation,ScrapedDate,ImportDate,DaysAgo);
}
}
And the Level class(all the other relation classes have the same structure(name,id):
public class Level {
[Key]
[Required]
public int Id { get; set; }
[Required]
[StringLength(100, ErrorMessage = "Maximum length 100 characters")]
public string Name { get; set; }
public override string ToString() {
return string.Format("Id={0} Name={1}", Id, Name);
}
}
As far as connection to database goes you'll just have to believe me that i've checked both are connecting to the same database.(I even generated a new database with migrations enabled on this one since we use another context for migrations. That still had the exact same issue).
To clarify:
The unittest looks for table: CategoryVacancies.
The console application or even my windows service i use this in look for the right table: VacancyCategories.

Resources