【问题标题】:AutoMapper and WindsorAutoMapper 和温莎
【发布时间】:2014-06-24 09:49:16
【问题描述】:

我将 AutoMapperITypeConverter 一起使用,并且我希望将所有内容保留在 Castle Windsor 中。 在一个程序集中,我有我的 ITypeCoverter 并使用这种方法加载它们:

container
.Register(Types.FromAssembly(Assembly.GetExecutingAssembly())
.BasedOn(typeof(ITypeConverter<,>)));

到目前为止一切顺利,我可以看到 Windsor 正在正确加载我的所有转换器。

然后我在 Windsor 中注册 AutoMapper

Mapper.Initialize(m => m.ConstructServicesUsing(container.Resolve));
container.Register(Component.For<IMappingEngine>().Instance(Mapper.Engine));

但是当我询问 IMappingEngine 的实例时,没有加载约定。我错过了什么吗?

【问题讨论】:

    标签: castle-windsor automapper


    【解决方案1】:

    我让 AutoMapper 和 Windsor 与 Profiles 和 TypeConverters 配合得很好。我认为这对你来说应该足够不可知了,因为 AutoMapper 会自动选择 ITypeConverter 的任何实现。

    如果它解决了你的问题,请告诉我。

    首先我在 Windsor 注册了 AutoMapper:

    public class AutoMapperInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            // Replace by whatever assembly contains your ITypeConverter, IValueResolver and Profile implementations
            var myAssembly = Classes.FromAssemblyNamed("MyAssembly");
    
            // Register AutoMapper such that it uses a singleton configuration
            container.Register(
                myAssembly.BasedOn(typeof(ITypeConverter<,>)).WithServiceSelf(),
                myAssembly.BasedOn<IValueResolver>().WithServiceBase(),
                myAssembly.BasedOn<Profile>().WithServiceBase(),
                Component.For<IEnumerable<IObjectMapper>>().UsingFactoryMethod(() => MapperRegistry.Mappers),
                Component.For<ConfigurationStore>()
                    .LifestyleSingleton()
                    .UsingFactoryMethod(x =>
                    {
                        var typeMapFactory = x.Resolve<ITypeMapFactory>();
                        var mappers = x.Resolve<IEnumerable<IObjectMapper>>();
                        ConfigurationStore configurationStore = new ConfigurationStore(typeMapFactory, mappers);
                        configurationStore.ConstructServicesUsing(x.Resolve);
                        configurationStore.AssertConfigurationIsValid();
                        return configurationStore;
                    }),
                Component.For<IConfigurationProvider>().UsingFactoryMethod(x => x.Resolve<ConfigurationStore>()),
                Component.For<IConfiguration>().UsingFactoryMethod(x => x.Resolve<ConfigurationStore>()),
                Component.For<IMappingEngine>().ImplementedBy<MappingEngine>().LifestyleSingleton(),
                Component.For<ITypeMapFactory>().ImplementedBy<TypeMapFactory>()
            );
    
            // Add all Profiles
            var configuration = container.Resolve<IConfiguration>();
            container.ResolveAll<Profile>().ToList().ForEach(configuration.AddProfile);
        }
    }
    

    然后我创建了一个配置文件,在其中我使用一点反射注册了所有 ITypeConverter 实现:

    public class TypeConverterProfile : Profile
    {
        private readonly IConfiguration _configuration;
    
        public TypeConverterProfile(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    
        protected override void Configure()
        {
            // Replace by whatever assembly contains your ITypeConverter implementations
            var myAssembly = Classes.FromAssemblyNamed("MyAssembly");
            var typeConverters =
                from x in myAssembly.GetTypes()
                from type in x.GetInterfaces()
                where type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ITypeConverter<,>)
                select new
                {
                    Type = x,
                    Source = type.GetGenericArguments()[0],
                    Destination = type.GetGenericArguments()[1]
                };
    
            // Apply type converters to AutoMapper configuration
            foreach (var typeConverter in typeConverters)
                _configuration.CreateMap(typeConverter.Source, typeConverter.Destination).ConvertUsing(typeConverter.Type);
        }
    }
    

    AutoMapper 现在将自动拾取并使用“MyAssembly”中的任何 Profiles 和 ITypeConverters 实现。

    例如,您可能有一个个人资料:

    public class MapProfile : Profile
    {
        private readonly IConfiguration _configuration;
    
        public MapProfile(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    
        protected override void Configure()
        {
            // Create an auto-map from SearchResultModel -> SearchResult
            _configuration.CreateMap<SearchResultModel, SearchResult>();
        }
    }
    

    还有一个 ITypeConverter:

    public class SearchRequestConverter : ITypeConverter<SearchRequest, SearchRequestModel>
    {
        private readonly IConfiguration _configuration;
    
        public MapProfile(IConfiguration configuration)
        {
            // Also in the ITypeConverter you may have the IConfiguration injected in case you need it
            _configuration = configuration;
        }
    
        public SearchRequestModel Convert(ResolutionContext context)
        {
            var model = (SearchRequest) context.SourceValue;
            return new SearchRequestModel
            {
                Results = model.Results
            };
        }
    }
    

    最后,您通过依赖 IMappingEngine 在您的类中执行实际映射:

    public class FolderRepository
    {
        private readonly IMappingEngine _mappingEngine;
    
        public FolderRepository(IMappingEngine mappingEngine)
        {
            _mappingEngine = mappingEngine;
        }
    
        public SearchResult Search(SearchRequest searchRequest)
        {
            // This mapping will use the ITypeConverter implementation
            SearchRequestModel searchRequestModel = _mappingEngine.Map<SearchRequestModel>(searchRequest);
            SearchResultModel searchResultModel = Search(searchRequestModel);
    
            // This mapping will use the auto-map from the Profile
            SearchResult searchResult = _mappingEngine.Map<SearchResultModel>(searchResultModel);
            return searchResult;
        }
    
        private SearchResultModel Search(SearchRequestModel searchRequestModel)
        {
            SearchResultModel result = new SearchResultModel();
            // ... perform search ...
            return result;
        }
    }
    

    要拥有更多 ITypeConverter,您只需实现它们,Windsor 和 AutoMapper 就会自动获取它们。可以在配置文件中添加手动自动地图 (CreateMap)。

    这能解决你的问题吗?

    【讨论】:

      【解决方案2】:

      我没有看到创建/设置映射的代码。既不是Mapper.CreateMap&lt;Source, Destination&gt;(); 也不是configuration.CreateMap&lt;Source, Destination&gt;();

      很遗憾,没有足够的信息来说明同时使用 IoC 和 AutoMapper。你只能找到一个an example of usage AutoMapper and Structure Map in the official samples

      还有my example of usage AutoMapper and Castle Windsor。和上一个类似,但使用的是温莎城堡而不是结构图。

      /// <summary>
      /// Castle Windsor example.
      /// </summary>
      /// <remarks>
      /// StructureMap example
      /// see  https://github.com/AutoMapper/AutoMapper/blob/develop/src/AutoMapperSamples/CastleWindsorIntegration.cs
      /// </remarks>
      public class CastleWindsorIntegration
      {
          private readonly IWindsorContainer container;
      
          public CastleWindsorIntegration()
          {
              container = new WindsorContainer();
          }
      
          [Fact]
          public void Example()
          {
              container.Install(new ConfigurationInstaller());
      
              var configuration1 = container.Resolve<IConfiguration>();
              var configuration2 = container.Resolve<IConfiguration>();
              configuration1.Should().BeSameAs(configuration2);
      
              var configurationProvider = container.Resolve<IConfigurationProvider>();
              configurationProvider.Should().BeSameAs(configuration1);
      
              var configuration = container.Resolve<ConfigurationStore>();
              configuration.Should().BeSameAs(configuration1);
      
              configuration1.CreateMap<Source, Destination>();
      
              var engine = container.Resolve<IMappingEngine>();
      
              var destination = engine.Map<Source, Destination>(new Source { Value = 15 });
      
              destination.Value.Should().Be(15);
          }
      
          [Fact]
          public void Example2()
          {
              container.Install(new MappingEngineInstaller());
      
              Mapper.Reset();
      
              Mapper.CreateMap<Source, Destination>();
      
              var engine = container.Resolve<IMappingEngine>();
      
              var destination = engine.Map<Source, Destination>(new Source { Value = 15 });
      
              destination.Value.Should().Be(15);
          }
      
          public class Source
          {
              public int Value { get; set; }
          }
      
          public class Destination
          {
              public int Value { get; set; }
          }
      
          public class ConfigurationInstaller : IWindsorInstaller
          {
              public void Install(IWindsorContainer container, IConfigurationStore store)
              {
                  container.Register(
                      Component.For<IEnumerable<IObjectMapper>>()
                          .LifestyleSingleton()
                          .UsingFactoryMethod(() => MapperRegistry.Mappers),
                      Component.For<ConfigurationStore>().ImplementedBy<ConfigurationStore>(),
                      Component.For<IConfigurationProvider>().UsingFactoryMethod(k => k.Resolve<ConfigurationStore>()),
                      Component.For<IConfiguration>().UsingFactoryMethod(k => k.Resolve<ConfigurationStore>()),
                      Component.For<IMappingEngine>().ImplementedBy<MappingEngine>(),
                      Component.For<ITypeMapFactory>().ImplementedBy<TypeMapFactory>());
              }
          }
      
          public class MappingEngineInstaller : IWindsorInstaller
          {
              public void Install(IWindsorContainer container, IConfigurationStore store)
              {
                  container.Register(
                      Component.For<IMappingEngine>().UsingFactoryMethod(() => Mapper.Engine));
              }
          }
      }
      

      例如,为了使测试变为绿色,需要使用Mapper.CreateMap&lt;Source, Destination&gt;().ConvertUsing&lt;SourceToDestinqtionConvertor&gt;();

          [Fact]
          public void Example3()
          {
              container
                  .Register(Types.FromAssembly(Assembly.GetExecutingAssembly())
                  .BasedOn(typeof(ITypeConverter<,>)));
      
              Mapper.Initialize(m => m.ConstructServicesUsing(container.Resolve));
              container.Register(Component.For<IMappingEngine>().Instance(Mapper.Engine));
      
              var engine = container.Resolve<IMappingEngine>();
      
              Mapper.CreateMap<Source, Destination>().ConvertUsing<SourceToDestinqtionConvertor>();
      
              var destination = engine.Map<Source, Destination>(new Source { Value = 15 });
      
              destination.Value.Should().Be(15);
          }
      
          public class SourceToDestinqtionConvertor : ITypeConverter<Source, Destination>
          {
              public Destination Convert(ResolutionContext context)
              {
                  throw new System.NotImplementedException();
              }
          }
      

      编辑:

      为了能够解析“ITypeConverter”,需要指定要使用的服务。

              container
                  .Register(Types.FromAssembly(Assembly.GetExecutingAssembly())
                  .BasedOn(typeof(ITypeConverter<,>))
                  .WithService.AllInterfaces());
      
              // Makes sure that type converter can be resolved
              var resolver = container.Resolve<ITypeConverter<Source, Destination>>();
              resolver.Should().BeOfType<SourceToDestinqtionConvertor>();
      

      【讨论】:

      • 是的,这是需要的,但在每个配置文件中变得非常无用,因为您必须“了解”转换器,而我正在寻找 AutoMapper 找出它的完整不可知论的解决方案。无论如何,我刚刚与 AutoMapper 团队进行了交谈,并且计划在框架中添加此功能以完全动态加载映射配置,而无需使用 suntac Mapper.CreateMap ...
      • 是的,它会很有用,因为现在需要开发额外的抽象来通过 IoC 调用 Mapper.CreateMap()
      【解决方案3】:

      我执行以下操作以使 Automapper 正常工作。

      注册点第一名:

              foreach (var automapperProfileType in types.Where(t => t.IsInterface == false && typeof(Profile).IsAssignableFrom(t)))
              {
                  container.Register(
                      Component.For(typeof(Profile))
                          .ImplementedBy(automapperProfileType)
                          .LifestyleTransient()
                          .Named("AutoMapper / Profile / " + automapperProfileType.FullName));
                  automapperProfileTypes.Add(automapperProfileType);
              }
      

      然后在初始化所有类型之后:

              foreach (var profileMap in container.ResolveAll<Profile>())
              {
                  Mapper.AddProfile(profileMap);
              }
      

      希望对您有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-15
        • 1970-01-01
        相关资源
        最近更新 更多