7

Automapper を使用して、Entity と ViewModel オブジェクトの間を (両方向で) マッピングしています。モデルは EF4 DbContext POCO を使用し、LazyLoading (したがってプロキシ生成) を有効にする必要があります。

ビューモデルから既存のエンティティを更新しようとして問題が発生しました。Mapper.Map(vm, entity) を呼び出すと、Automapper が例外をスローします。私の質問は、Automapper を使用して EF Proxy オブジェクトをどのように操作するのですか?

コードは (簡略化して) 次のようになります。

public class MyEntity
{
    public int Id {get;set;}
    public int Name {get;set;}
}

public class ViewModel
{
    public int Id {get;set;}
    public int Name {get;set;}
}

Mapper.CreateMap<MyEntity, ViewModel>();
Mapper.CreateMap<ViewModel, MyEntity>();

public ActionResult Edit(ViewModel vm)
{
    MyEntity entity = db.MyEntities.Find(vm.Id);
    Mapper.Map(vm, entity);
    db.Entry(entity).State = EntityState.Modified;
    db.SaveChanges();
}

Mapper.Map(vm, entity) を呼び出して既存のエンティティ オブジェクトを更新すると、例外が発生します。

'Mapper.Map(viewModel, resultSet)' threw an exception of type 'AutoMapper.AutoMapperMappingException'
base {System.Exception}: {"Missing type map configuration or unsupported mapping.\n\nMapping types:\r\nResultSetView -> ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\r\nSciensus.Applications.ClinicalStudies.Web.Areas.Patient.Models.ResultSetView -> System.Data.Entity.DynamicProxies.ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\n\nDestination path:\nResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\n\nSource value:\nSciensus.Applications.ClinicalStudies.Web.Areas.Patient.Models.ResultSetView"}
Context: {Trying to map ResultSetView to ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2.}
Message: "Missing type map configuration or unsupported mapping.\n\nMapping types:\r\nResultSetView -> ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\r\nSciensus.Applications.ClinicalStudies.Web.Areas.Patient.Models.ResultSetView -> System.Data.Entity.DynamicProxies.ResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\n\nDestination path:\nResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2\n\nSource value:\nSciensus.Applications.ClinicalStudies.Web.Areas.Patient.Models.ResultSetView"
StackTrace: ""
4

3 に答える 3

6

AutoMapper のソース コードを確認しました。

    public TDestination Map<TSource, TDestination>(TSource source, TDestination destination)
    {
        return Map(source, destination, opts => { });
    }

    public TDestination Map<TSource, TDestination>(TSource source, TDestination destination, Action<IMappingOperationOptions> opts)
    {
        Type modelType = typeof(TSource);
        Type destinationType = (Equals(destination, default(TDestination)) ? typeof(TDestination) : destination.GetType());

        return (TDestination)Map(source, destination, modelType, destinationType, opts);
    }

この場所で問題が発生しました:

  Type destinationType = (Equals(destination, default(TDestination)) ? typeof(TDestination) : destination.GetType());

したがって、問題のない変更:

  Mapper.Map(vm, entity,typeof(ViewModel),typeof(MyEntity));
于 2013-01-11T03:41:34.337 に答える
3

ご想像のとおり、AutoMapper には、エンティティResultSet_692D75838D4DC59B922F3E88CF1B10516CBF6CD8A32C4BE2F3FCC28CE83F0BD2から派生した Lazy Loading ( )によって作成されたプロキシ クラスのマップがないため、この例外が発生していると思います。ResultSet

あなたが試すかもしれないことは次のとおりです:

public ActionResult Edit(ViewModel vm)
{
    // This returns the entity proxy
    MyEntity oldEntity = db.MyEntities.Find(vm.Id);
    // i.e. Create a 'plain' Entity, not a proxy.
    MyEntity newEntity = Mapper.Map<ViewModel, MyEntity>(vm);

    db.Entry(oldEntity).CurrentValues.SetValues(newEntity);
    // I don't think you need this now.
    // db.Entry(entity).State = EntityState.Modified;
    db.SaveChanges();
}
于 2012-11-16T12:57:35.133 に答える
1

したがって、私がやったことは、双方向のマッピングをサポートするために、独自のIMappableインターフェイスと単純な汎用マッピングユーティリティをローリングすることでした。マッピングの複雑さによっては、必要なコードがAutomapperよりも少なくなる可能性があります。以下のコード:

public interface IMappable<TEntity, TViewModel>
{
    void Map(TEntity source, TViewModel target);
    void Map(TViewModel source, TEntity target);
}

public class ModelMapper<TEntity, TViewModel> where TEntity : new() where TViewModel : IMappable<TEntity, TViewModel>, new()
{
    public static TViewModel Map(TEntity source)
    {
        TViewModel target = new TViewModel();
        Map(source, target);
        return target;
    }

    public static TEntity Map(TViewModel source)
    {
        TEntity target = new TEntity();
        Map(source, target);
        return target;
    }

    public static void Map(TEntity source, TViewModel target)
    {
        new TViewModel().Map(source, target);
    }

    public static void Map(TViewModel source, TEntity target)
    {
        new TViewModel().Map(source, target);
    }
}

My ViewModelsは、必要な数のEntityクラスにIMappableを実装し、各方向のデータ転送を処理するMapメソッドを実装します。

public class AddressView : IMappable<Address, AddressView>
{
    public long AddressId { get; set; }
    ...

    #region "IMappable members"
    public void Map(Address source, AddressView target)
    {
        target.AddressId = source.AddressId;
        target.City = source.City;
        target.FullAddress = source.FullAddress;
        if (source.CountryId.HasValue)
        {
            target.Country = source.Country.Name;
            target.CountryId = source.CountryId;
        }
        target.District = source.District;
        target.Postcode = source.Postcode;
        target.StreetName = source.StreetName;
    }

    public void Map(AddressView source, Address target)
    {
        target.AddressId = source.AddressId;
        target.City = source.City;
        target.CountryId = source.CountryId;
        target.District = source.District;
        target.Postcode = source.Postcode;
        target.StreetName = source.StreetName;
    }
    #endregion
}
于 2012-12-07T11:18:39.157 に答える