Base solution for your next web application
Open Closed

AutoMapper Collection #3948


User avatar
0
agmachado created

Hi. How can I get automapper to run collections to automatically update one-to-many relationships?


5 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @agmachado,

    If your Dto is matching your entity (with it's collections), AutoMapper should map collections as well. Do you have a sample code or any error about this ?

  • User Avatar
    0
    agmachado created

    Thank for your reply ismcagdas

    My reduced sample code:

    public class ModuloPl : Entity { public virtual string Nombre { get; protected set; } public virtual string Html { get; set; } public ICollection<CampoModulo> Campos { get; set; } .... }

    public class CampoModulo : Entity { public virtual string Nombre { get; set; } public virtual string Marca { get; set; } public virtual bool Requerido { get; set; } .... }

    public class ModuloPlEditDto { public virtual int? Id { get; set; } public virtual string Nombre { get; set; } public virtual string Html { get; set; } public Collection<CamposEnModuloEditDto> Campos { get; set; } }

    public class CamposEnModuloEditDto { public virtual int? Id { get; set; } public string Nombre { get; set; } public string Marca { get; set; } public bool Requerido { get; set; } }

    private async Task<ModuloPl> GetModuloByIdAsync(int moduloId) { var modulopl = await _moduloRepository.GetAll() .Include(p => p.Campos) .Include(p => p.imagen) .Include(p => p.imagen.Ubicacion) .Where(p => p.Id == moduloId).SingleAsync();

    if (modulopl == null)
    {
        throw new Abp.UI.UserFriendlyException("No existe ningún módulo con id: " + moduloId);
    }
    
    return modulopl;
    

    }

    Now, in runtime, the user add a new CamposEnModuloEditDto in a existing ModuloPlEditDto

    ModuloPlEditDto input; // this DTO is mapped from a existting entity CamposEnModuloEditDto newCampo = new CamposEnModuloEditDto(); newCampo.Nombre = "name"; newCampo.Marca = "Html"; newCampo.Requerido = True; input.Campos.Add(newCampo);

    and calll to UpdateModuloPl(ModuloPlEditDto input)

    [AbpAuthorize(AppPermissions.Pages_Administration_TAuxiliares)] public async Task UpdateModuloPl(ModuloPlEditDto input) {

     var oldmodulopl = await GetModuloByIdAsync(input.Id.Value);
    

    ObjectMapper.Map(input, oldmodulopl);

    await _moduloRepository.UpdateAsync(oldmodulopl);
    

    }

    Mvc.ExceptionHandling.AbpExceptionFilter - The instance of entity type 'CampoModulo' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context. System.InvalidOperationException: The instance of entity type 'CampoModulo' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context. en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap1.Add(TKey key, InternalEntityEntry entry) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap1.Add(InternalEntityEntry entry) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph(EntityEntryGraphNode node, Func2 handleNode) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState entityState) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.NavigationCollectionChanged(InternalEntityEntry entry, INavigation navigation, IEnumerable1 added, IEnumerable1 removed) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.NavigationCollectionChanged(InternalEntityEntry entry, INavigation navigation, IEnumerable1 added, IEnumerable1 removed) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectNavigationChange(InternalEntityEntry entry, INavigation navigation) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(InternalEntityEntry entry) en Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(IStateManager stateManager) en Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.DetectChanges() en Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.Entries() en Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase3.AttachIfNot(TEntity entity) en Castle.Proxies.EfCoreRepositoryBase2Proxy_4.AttachIfNot_callback(ModuloPl entity) en Castle.Proxies.Invocations.EfCoreRepositoryBase3_AttachIfNot_27.InvokeMethodOnTarget() en Castle.DynamicProxy.AbstractInvocation.Proceed() en Abp.Domain.Uow.UnitOfWorkInterceptor.PerformSyncUow(IInvocation invocation, UnitOfWorkOptions options) en Abp.Domain.Uow.UnitOfWorkInterceptor.PerformUow(IInvocation invocation, UnitOfWorkOptions options) en Abp.Domain.Uow.UnitOfWorkInterceptor.Intercept(IInvocation invocation) en Castle.DynamicProxy.AbstractInvocation.Proceed() en Castle.Proxies.EfCoreRepositoryBase2Proxy_4.AttachIfNot(ModuloPl entity) en Abp.EntityFrameworkCore.Repositories.EfCoreRepositoryBase3.UpdateAsync(TEntity entity) en Castle.Proxies.EfCoreRepositoryBase2Proxy_4.UpdateAsync_callback(ModuloPl entity) en Castle.Proxies.Invocations.EfCoreRepositoryBase3_UpdateAsync_27.InvokeMethodOnTarget() en Castle.DynamicProxy.AbstractInvocation.Proceed() en Abp.Domain.Uow.UnitOfWorkInterceptor.PerformAsyncUow(IInvocation invocation, UnitOfWorkOptions options) en Abp.Domain.Uow.UnitOfWorkInterceptor.PerformUow(IInvocation invocation, UnitOfWorkOptions options) en Abp.Domain.Uow.UnitOfWorkInterceptor.Intercept(IInvocation invocation) en Castle.DynamicProxy.AbstractInvocation.Proceed() en Castle.Proxies.EfCoreRepositoryBase`2Proxy_4.UpdateAsync(ModuloPl entity) en icaCronos.Plantilla.ModuloPlAppService.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @agmachado,

    Thanks for sharing sample code. Do you have AutoMap attribute on your Dtos ? If not, you need to add it, see <a class="postlink" href="https://aspnetboilerplate.com/Pages/Documents/Object-To-Object-Mapping#auto-mapping-attributes">https://aspnetboilerplate.com/Pages/Doc ... attributes</a>.

  • User Avatar
    0
    agmachado created

    Yes

    [AutoMapFrom(typeof(CampoModulo))] [AutoMapTo(typeof(CampoModulo))] public class CamposEnModuloEditDto ....

    [AutoMapFrom(typeof(CampoModulo))] [AutoMapTo(typeof(CampoModulo))] public class CamposEnModuloEditDto ......

  • User Avatar
    0
    ismcagdas created
    Support Team

    @agmachado, do you also send necessary Id values for your Dtos when you call your app service ? If you are doing it correctly, please send your project to us (<a href="mailto:[email protected]">[email protected]</a>) and let us check it.

    Thanks.