Реализация метода WCF, который поддерживает строку или объект в качестве параметра метода

1

У меня есть следующий контракт в веб-службе на основе WCF:

public List<string> GetAllPossibleQueryEngineHostNames(Instance instance);

Я называю это от клиента так:

string instance = "value";
svc.GetAllPossibleQueryEngineHostNames(instance);

instance Note - это объект String, а не Instance. Я получаю исключение:

При попытке десериализовать сообщение форматировщик исключил исключение: при попытке десериализации параметра http://tempuri.org/:instance произошла ошибка. Сообщение InnerException было "Ошибка в строке 1 позиция 152. Ожидание состояния" Элемент ".. Обнаружен" Текст "с именем" ", пространство имен". ". Дополнительную информацию см. В InnerException.

Я ищу способ исправить эту проблему, не меняя код клиента, так как мы должны иметь возможность поддерживать старых клиентов, подключающихся к этой службе.

Обратите внимание, что класс Instance на клиенте и сервере имеет неявный оператор преобразования:

public static implicit operator Instance(string value)
{
   // Converts string to an Instance
}

Однако WCF, похоже, не учитывает это при десериализации. Есть ли способ точно контролировать, как WCF будет десериализовать Instance экземпляра, позволяя ему быть либо строкой, либо Instance объекта?

Теги:
soap
web-services
wcf

2 ответа

0
Лучший ответ

Получил эту работу. Здесь мой код, если кто-то заинтересован в решении. В принципе, я создал новый файл Legacy.cs со следующими классами:

public class InstanceSerializer : XmlObjectSerializer
{
    const string localName = "instance";

    public override bool IsStartObject(XmlDictionaryReader reader)
    {
        return String.Equals(reader.LocalName, localName, StringComparison.OrdinalIgnoreCase);
    }

    public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
    {
        string xml = reader.ReadOuterXml();
        XDocument doc = XDocument.Parse(xml);

        string shortCode = doc.Descendants()
            .Where(e => e.Name.LocalName == "ShortCode")
            .Select(e => e.Value)
            .FirstOrDefault();

        string connStr = doc.Descendants()
            .Where(e => e.Name.LocalName == "ConnectionString")
            .Select(e => e.Value)
            .FirstOrDefault();

        if (connStr != null || shortCode != null) // Instance passed as Instance object
        {
            return new Instance(shortCode, connStr);
        }

        // Instance passed as String
        Instance instance = ((XElement) doc.FirstNode).Value;
        return instance;
    }

    public override void WriteEndObject(XmlDictionaryWriter writer)
    {
        writer.WriteEndElement();
    }

    public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
    {
    }

    public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
    {
        writer.WriteStartElement(localName);
    }
}

public class InstanceBehavior : DataContractSerializerOperationBehavior
{
    public InstanceBehavior(OperationDescription operation) : base(operation) { }

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
    {
        return typeof(Instance) == type
            ? new InstanceSerializer()
            : base.CreateSerializer(type, name, ns, knownTypes);
    }

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
    {
        return typeof(Instance) == type
            ? new InstanceSerializer()
            : base.CreateSerializer(type, name, ns, knownTypes);
    }
}

public class SupportStringInstanceAttribute : Attribute, IContractBehavior
{
    public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
        ReplaceSerializerOperationBehavior(contractDescription);
    }

    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
    {
        ReplaceSerializerOperationBehavior(contractDescription);
    }

    public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
    {
    }

    private static void ReplaceSerializerOperationBehavior(ContractDescription contract)
    {
        foreach (OperationDescription od in contract.Operations)
        {
            for (int i = 0; i < od.Behaviors.Count; i++)
            {
                DataContractSerializerOperationBehavior dcsob = od.Behaviors[i] as DataContractSerializerOperationBehavior;
                if (dcsob != null)
                {
                    od.Behaviors[i] = new InstanceBehavior(od);
                }
            }
        }
    }
}

Наконец, я добавил [SupportStringInstance] в начало моей реализации службы (должен либо работать интерфейс, либо класс).

Обратите внимание, что это поддерживает десериализацию объекта Instance (передается ли он как строка или объект). Вам нужно будет реализовать InstanceSerializer.WriteObjectContent а также поддерживать сериализацию, к счастью, мне не нужно делать (по крайней мере пока).

2

Стандартный DataContractSerializer не собирается автоматически конвертировать вашу строку в тип экземпляра. Вы не можете перегрузить этот метод в WCF, поэтому я бы предложил вам просто взять строку в качестве аргумента, а затем выполнить преобразование для экземпляра.

public List<string> GetAllPossibleQueryEngineHostNames(string instanceName)
{
    var instance = Instance(instanceName);
    // Do everything else
}

Тогда я подумал бы о том, чтобы сохранить его таким образом или добавить другой метод, который фактически примет аргумент экземпляра.

Последняя опция будет принимать в байте [] в качестве аргумента и попытаться обработать его до нужного вам типа и провести проверку, чтобы решить, является ли она строкой или типом.

  • 0
    Другой вариант - использовать Диспетчер сообщений и Форматер сообщений. blogs.msdn.com/b/drnick/archive/2007/03/07/...
  • 0
    Благодарю. Однако мне нужно поддерживать оба формата. Тип меняется, чтобы учесть новые функции.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню