I have three stateless applications Application-A, Application-B, and Application-C. Application-B and Application-C were communicating with Application-A with remoting V1.

Recently I migrated Application-C to .Net 5.0. As V1 Remoting is not supported in .Net Core I made two listeners in Application-A(service application) as shown below to support both V1 and V2_1 listeners.

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new[]
    {
        new ServiceInstanceListener(this.CreateHeadersAwareServiceInstanceListener),
        new ServiceInstanceListener((c) =>
        {
            var settings = new FabricTransportRemotingListenerSettings {UseWrappedMessage = true};
            return new FabricTransportServiceRemotingListener(c, this,settings);

        },"ServiceEndpointV2_1")
    };
}

public async Task<ClientInfo> GetClientInfo(string clinetId, CancellationToken cancellationToken = default(CancellationToken))
{
        var data = await _logic.GetClientData(clinetId, cancellationToken);
        return data.FromInternalModel();
}

Application-B should communicate with Application-A using V1 Remoting and Application-C will communicate with Application-A using remoting V2_1 stack. Below is how Application-C calls Application-A

 var client ServiceProxy.Create<IBackEndService>(new Uri(_serviceURL), listenerName: "ServiceEndpointV2_1");
await clinet.GetClientInfo(clinetId);

GetClinetInfo method return ClinetInfo object and ClientInfo looks like

    [DataContract]
    public class ClientInfo : IExtensibleDataObject
    {

        [DataMember]
        public string ClinetId { get; set; }

        [DataMember]
        public ClientAddress Address { get; set; }

        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public X509Certificate2 ClientCertificate { get; set; }

        public virtual ExtensionDataObject ExtensionData { get; set; }
    }

When Application-C calls GetClinetInfo() I'm getting below exception

Exception Summary:
======================================
Message:Type 'System.Security.Cryptography.X509Certificates.X509Certificate2' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.
UTC Timestamp: 04/12/2021 11:34:39.298
Machine Name: id-b3000000
Assembly Full Name: Temp.MyAppDomain, Version=3.16.0.0, Culture=neutral, PublicKeyToken=null
Assembly Version: 3.16.0.0
App Domain Name: Temp.MyAppDomain 
Windows Identity: 
Additional Context: 

Exception Information Details:
======================================
Exception Type: System.Runtime.Serialization.InvalidDataContractException
Message: Type 'System.Security.Cryptography.X509Certificates.X509Certificate2' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. Alternatively, you can ensure that the type is public and has a parameterless constructor - all public members of the type will then be serialized, and no attributes will be required.
Help Link: 
HResult: -2146233088
Source: Microsoft.ServiceFabric.Services
Target Site: Void MoveNext()

Stack Trace Information Details: 
======================================
   at Microsoft.ServiceFabric.Services.Communication.Client.ServicePartitionClient`1.InvokeWithRetryAsync[TResult](Func`2 func, CancellationToken cancellationToken, Type[] doNotRetryExceptionTypes)
   at Microsoft.ServiceFabric.Services.Remoting.V2.Client.ServiceRemotingPartitionClient.InvokeAsync(IServiceRemotingRequestMessage remotingRequestMessage, String methodName, CancellationToken cancellationToken)
   at Microsoft.ServiceFabric.Services.Remoting.Builder.ProxyBase.InvokeAsyncV2(Int32 interfaceId, Int32 methodId, String methodName, IServiceRemotingRequestMessageBody requestMsgBodyValue, CancellationToken cancellationToken)
   at Microsoft.ServiceFabric.Services.Remoting.Builder.ProxyBase.ContinueWithResultV2[TRetval](Int32 interfaceId, Int32 methodId, Task`1 task)
   at Temp.MyAppDomain.Utils.DataProvider.GetMyData(String deviceId) in F:\Test\GH-LSU0BCDS-JOB1\Sources\MyLogic\Utils\MyProvider.cs:line 40
   at Temp.MyAppDomain.Logic.GetMyCertificate() in F:\Test\GH-LSU0BCDS-JOB1\Sources\MyLogic\MyLogic.cs:line 462
   at Temp.MyAppDomain.Logic.VerifySignature(Message message) in F:\Test\GH-LSU0BCDS-JOB1\Sources\MyLogic\MyLogic.cs:line 387

1

There are 1 best solutions below

0
On

As the exception indicates, the certificate you're looking at sending between the services isn't serializable, so it's throwing on the attempt. Just because you mark it with [DataMember] doesn't magically make it something that can be serialized.

Rather, I suggest you convert your certificate to something that is easily serializable like a string and use that value instead:

[DataContract]
public class ClientInfo : IExtensibleDataObject
{

    [DataMember]
    public string ClinetId { get; set; }

    [DataMember]
    public ClientAddress Address { get; set; }

    [DataMember]
    public string Name { get; set; }

    private X509Certificate2 _clientCertificate;
    [IgnoreDataMember]
    public X509Certificate2 ClientCertificate 
    {
         get => _clientCertificate;
         set 
         {
              _clientCertificate = value;

              //Set the PEM value - from https://stackoverflow.com/a/4740292/845356
              var sb = new StringBuilder();
              sb.AppendLine("-----BEGIN CERTIFICATE-----");
              sb.AppendLine(Convert.ToBase64String(value.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
              sb.AppendLine("-----END CERTIFICATE-----");
              CertificateAsPem = sb.ToString();
         }
    }

    [DataMember]
    public string CertificateAsPem { get; set; }

    public virtual ExtensionDataObject ExtensionData { get; set; }
}