Как да ползвам силно типизирани параметри в URI пътя в WCF с WebHttpBinding?

+4 гласа
137 прегледа
попитан 2016 април 11 в .NET от Nikoleta.V. (4,090 точки)

Как да ползвам силно типизирани параметри в URI пътя в WCF с WebHttpBinding 

Търсих отговори на този въпрос и намирам само статии и документи за .NET 3.5 от 2011 и назад. Надявам се да намеря по-скорошна информация за WCF в .NET 4.5 и нагоре. 

Имам WCF услуга, която е свързана с WebHttpBinding. Използва URI шаблон, който подава identity на обекта,който да бъде извлечен с URI шаблон:  

[WebInvoke(Method="GET", UriTemplate = "/{identity}")] 

public ResponseItem Get(string identity) 

{  

    // Конвертира identity в GUID, който е по-правилният тип 

    //извличане на обекта 

Това,което искам да направя, е да премахна обръщането в Guid и да го преместя по-нагоре с це по-изчистен код: 

[WebInvoke(Method="GET", UriTemplate = "/{identity}")] 

public ResponseItem Get(Guid identity) 

{  

    //извличане на обекта 

Разбирам, че такова bind-ване е възможно с използване на персонализирано поведение и QueryStringConverter. Разбирам и че причината за това да е низ по подразбиране в WebHttpBinding е, че наследените стойности, подадени в адреса трябва да са низове - както адресите се базират на низове. Най-вероятно, това, което питам, няма смисъл. 

В контекста на моето приложение, с низ не е семантично правилно и ме дразни, че този клас е затрупан с код за обръщане, кото не би трябвало да е грижа, но промяна в bind-ването не е опция,защото има клиенти,които вече го използват. 

Има ли възможност за разширяване в текущите версии на WCF(например в IParameterInspector, IServiceBehavior), където обръщането на тази стойност е възможно и подходящо,така че в момента на извикване на метода,параметъра да е от подходящия тип?

1 отговор

0 гласа
отговорени 2016 април 12 от valeri.hristov (7,340 точки)

Значи искаш да подадеш низа и да го преработиш преди да се включи OperationInovker. Поизцапах си ръцете, но накрая го подкарах.

Ето какво направих. Нов клас наследява WebHttpBehavior, което ни дава възможност да разширим или променим поведението на WebHttpBidning.

public class MyWebHttpBehavior : WebHttpBehavior

{

}

Въпреки, че е малко хакче, трябва да работи. Проблема с типизирания аргумент е, че форматирането на URL шаблона хвърля exception като проверява метода за тип string, така че го разкарах като промених BindingInformation чрез override-ване на метода GetRequestDispatchFormatter

protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)

    {

        foreach (var item in operationDescription.Messages[0].Body.Parts)

        {

            item.Type = typeof(string);

        }

        return base.GetRequestDispatchFormatter(operationDescription, endpoint);

    }

След прилагането на това поведение, повече няма да се хвърля exception при проверката за string аргумент. Сега трябва да се промени OperationInvoker, защото тогава той ще хвърля exception(Invalid cast),когато извикаш операцията от клиента.

И ето го IoperationInvoker. Просто взех стойността от input[] като в тип обект, конвертирах от string в guid и го подадох обратно на Invoker.

private object[] CastCorrections(object[] inputs)

    {

        Guid obj;

        var value = inputs[0] as string;

        if (Guid.TryParse(value, out obj))

        {

            return new[] { (object)obj }.Concat(inputs.Skip(1)).ToArray();

        }

        return inputs.ToArray();

    }

    public object Invoke(object instance, object[] inputs, out object[] outputs)

    {

        return _invoker.Invoke(instance, CastCorrections(inputs), out outputs);

    }

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)

    {

        return _invoker.InvokeBegin(instance, inputs, callback, state);

    }

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)

    {

        return _invoker.InvokeEnd(instance, out outputs, result);

    }

    public bool IsSynchronous

    {

        get { return _invoker.IsSynchronous; }

    }

}

Това,което ми отне доста време, беше да измисля как да инжектирам този персонализиран invoker в основния код.

Трябва да се имплементира нов IOperationBehavior и да се закачи за DispatcherRuntime.

public class MyOperationBehavior : IOperationBehavior

{

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)

    {

    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)

    {

    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)

    {

        dispatchOperation.Invoker = new ValueCastInvoker(dispatchOperation.Invoker);

    }

    public void Validate(OperationDescription operationDescription)

    {

    }

}

Сега в MyWebHttpBehavior правя override на ApplyDispatchBehavior и вкарвам горе имплементирания ApplyDispatchBehavior.

public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

    {

        foreach (var operation in endpoint.Contract.Operations)

        {

            if (operation.Behaviors.Contains(typeof(MyOperationBehavior)))

                continue;

            operation.Behaviors.Add(new MyOperationBehavior());

        }

        base.ApplyDispatchBehavior(endpoint, endpointDispatcher);

    }

С тези малки хакчета и разширения би трябвало да работи правилно.

  [WebInvoke(Method = "GET", UriTemplate = "/{id}")]

    string GetValue(Guid id);

...