【问题标题】:Problem with Automapper 8.0 in a .NET WCF Service.NET WCF 服务中的 Automapper 8.0 问题
【发布时间】:2018-11-26 10:27:21
【问题描述】:

我刚刚将我的 WCF 服务项目中的 Automapper 升级到最新版本 8.0.0。通过 WCF 测试客户端调用服务时出现以下错误:

System.InvalidOperationException: '映射器已经初始化。您必须为每个应用程序域/进程调用一次 Initialize。'

使用旧的 Automapper 版本 6.0.2,它可以正常工作并且不会引发异常。我用一个单独的服务行为类初始化了 Mapper。

服务类EdiPartners.cs

[AutomapServiceBehavior]
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = 
InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class EdiPartners : IPartners
{
    [FaultContract(typeof(ExceptionObject))]
    public Supplier GetPartnerData(string SupplierNumber)
    {
       // Code
    }
}

服务行为类AutomapServiceBehavior

    public sealed class AutomapServiceBehavior : Attribute, IServiceBehavior
    {
    #region IServiceBehavior Members

    public void AddBindingParameters(ServiceDescription serviceDescription, 
    ServiceHostBase serviceHostBase,
    Collection<ServiceEndpoint> endpoints, BindingParameterCollection 
    bindingParameters)
    {
        AutomapBootstrap.InitializeMap();
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    #endregion
}

静态Automapper初始化类AutomapBootstrap

   public class AutomapBootstrap
{
    public static void InitializeMap()
    {
        Mapper.Initialize(config =>
        {
            config.CreateMap<Supplier, IEdiPartnerData>();
            config.CreateMap<IEdiPartnerData, Supplier>();
            config.CreateMap<IEdiPartner, EdiPartner>();

        });
    }
}

也许你们可以帮助我。我不明白为什么它适用于旧版本而不适用于新版本。有没有更好的方法在 WCF 服务中初始化 Automapper?

提前谢谢, 桑德罗

【问题讨论】:

  • 顺便说一句,ConcurrencyMode.MultipleInstanceContextMode = InstanceContextMode.PerCall 时毫无意义。无法共享服务器
  • 不确定它是否与您的问题有关,但您可以更改冗余的供应商-IEdiPartnerData 映射(来回)并改用config.CreateMap&lt;Supplier, IEdiPartnerData&gt;().ReverseMap()
  • @Edword thx,不知道这个方法
  • 因为老版本没扔。但是你的代码还是被破坏了。

标签: c# .net wcf automapper


【解决方案1】:

由于您的服务是InstanceContextMode.PerCall,每次调用都会创建一个新实例。此外,由于您使用[AutomapServiceBehavior] 装饰了您的服务,因此它不会为每个 AppDomain 初始化一次(如错误所示),而是每个实例

所以把你的代码改成:

public sealed class AutomapServiceBehavior : Attribute, IServiceBehavior
{
    static bool _initialised;

#region IServiceBehavior Members
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
        if (_initialised)  // already init'd per this AppDomain
            return;

        AutomapBootstrap.InitializeMap();
        _initialised = true;
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
#endregion
}

有用的服务行为或自定义服务主机?

顺便说一句,AutomapServiceBehavior 可能不是编写服务行为的好例子,因为如果您打算在同一个程序集中用它装饰多个服务(可能使用不同的映射),它将无法工作。 AutoMapper 喜欢在应用程序启动初始化一次,所以如果要在任何地方初始化它,我会将它放在自定义ServiceHost并消除这种行为。

【讨论】:

  • 感谢您为我节省了一天的答案。我已经通过服务主机工厂实施了您的建议。我会单独发布。
  • 我在这里真的很困惑。如果我们按照您的建议检查 _initialised,那么每个应用程序域的初始化只会发生一次,因为静态 _initialised 是一个应用程序状态变量。那么为什么 Automapper 不能正常工作呢?另外,如果我想按照您的建议使用自定义服务主机,就像 Sandro 发布的答案一样,我需要为每个服务类创建一个单独的工厂吗?
  • @Karamafrooz AutoMapper 如果每个 AppDomain 只初始化一次,它将正常工作。否则你会遇到"Mapper has been initialized. You must call Initialize once per application domain/process"
【解决方案2】:

解决方案是实现一个自定义的服务宿主工厂类:

public class EdiPartnersFactory : ServiceHostFactory
{
   protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
   {
    // Initialize Automapper
    AutomapBootstrap.InitializeMap();

    var host = new ServiceHost(serviceType, baseAddresses);

    return host;
    }
}

我更改了服务合同和绑定的 web.config:

    <system.serviceModel>
  <services>
    <service name="Company.Sap.EdiPartners"
             behaviorConfiguration="MEXServiceTypeBehavior">
      <endpoint address=""
                binding="basicHttpBinding"
                contract="Company.Sap.IPartners" />
      <endpoint address="mex"
                binding="mexHttpBinding"
                contract="IMetadataExchange" />
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="MEXServiceTypeBehavior">
        <serviceMetadata httpGetEnabled="true" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

更改 .svc 文件:

<%@ ServiceHost Language="C#" Debug="true" Service="Company.Sap.EdiPartners" Factory="Company.Sap.EdiPartnersFactory" CodeBehind="EdiPartners.svc.cs" %>

所以在服务类中没有使用服务绑定(看起来更干净):

public class EdiPartners : IPartners
{
    [FaultContract(typeof(ExceptionObject))]
    public Supplier GetPartnerData(string SupplierNumber)
    {
       // Code
    }
}

【讨论】:

  • 别忘了说明是谁建议的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多