1

I have a WCF service that I've written to accept objects passed to it by callers, which have been decorated with attributes that tell the service how to look up their string representation. For example, the service can translate a CAR_ID of 1 to "Toyota", CAR_ID 2 to "Honda", etc.

That part works great. Where I'm having some major issues is with my idea for loading the types so WCF can handle the serialization and deserialization without losing the critical attributes on the object properties. My goal was to have a folder on a remote share where people within my organization could drop their decorated .dll's, then call out to the service passing in objects which were defined in the dropped off .dll's. For the most part, this is working great, except for one thing: I'm having random issues with locks being placed on the .dll's, and so I'm unable to update them on the remote share.

I'd like to move the path definition to either point to a local folder where the service is installed, or to be able to customize it server side. As things currently stand, I have to publish the code for pulling in known types in my interface, which means any path I enter is evaluated from the perspective of the caller, not the service as I would love. Is there any way out of this predicament?

Below is a copy of my contract.

[ServiceKnownType("RegisterKnownTypes", typeof(MyStringTypeHelper))]
[ServiceContract]
public interface IMyStringParser
{
   [OperationContract]
   string GetStringRepresentation(object target);

   [OperationContract]
   string GetStringRepresentationWithComparison(object sourceTarget, object destinationTarget, bool useHtmlMarkup);
}

internal static class MyStringTypeHelper
{
   public static IEnumerable<Type> RegisterKnownTypes(ICustomAttributeProvider provider)
   {
      var files = Directory.GetFiles(@"\\path\to\remote\dlls"); // how can I move the definition of this path to be server side, not client side?

      if (files.Length == 0)
         return new List<Type>();

      var result = new List<Type>();
      foreach (var file in files)
      {
         result.AddRange(Assembly.LoadFile(Path.GetFullPath(file)).GetExportedTypes());
      }

      return result;
   }
}
Marisa
  • 732
  • 6
  • 22
  • Is a [SharedTypeResolver](https://stackoverflow.com/a/41460207/6666799) what you are looking for? Then solution 3 should solve your problem. – Rabban Jul 24 '17 at 12:02
  • @Rabban, that was perfect, thank you! could you post your comment as an answer so I can give you credit? – Marisa Jul 24 '17 at 12:59

1 Answers1

0

You can use a DataContractResolver

public class SharedTypeResolver : DataContractResolver
{
    public override bool TryResolveType(Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        if (!knownTypeResolver.TryResolveType(dataContractType, declaredType, null, out typeName, out typeNamespace))
        {
            XmlDictionary dictionary = new XmlDictionary();
            typeName = dictionary.Add(dataContractType.FullName);
            typeNamespace = dictionary.Add(dataContractType.Assembly.FullName);
        }
    }

    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? Type.GetType(typeName + ", " + typeNamespace);
    }
}

The Resolver accepts every type you use in your service. But you have to add the resolver to your Client endpoint and server endpoint. Like this:

Host = new ServiceHost(typeof(MyService));

ContractDescription cd = Host.Description.Endpoints[0].Contract;
foreach (var operation in cd.Operations)
{
    operation.Behaviors.Find<DataContractSerializerOperationBehavior>()
                .DataContractResolver = new SharedTypeResolver();
}
Rabban
  • 2,451
  • 1
  • 19
  • 21