Как сделать альтернативное использование интерфейсов для контрактов Wcf - PullRequest
2 голосов
/ 17 августа 2011

Допустим, у меня есть 3 сборки: Example.Core, Example.Contracts, Example.WcfServices. В моей сборке контрактов я определяю интерфейс и добавляю некоторые операции, например, ICalculator, который имеет операцию Add (double a, double b). В моей сборке WcfServices у меня есть реализация ICalculator, рассматриваемая как служба Wcf.

Теперь мой вопрос заключается в следующем ... в моей сборке Example.Core, как программировать для этого интерфейса, сохраняя все разъединенным (чтобы у меня была альтернативная реализация интерфейса). Если у меня есть класс, которому нужен ICalculator, я могу создать его, скажем, из ChannelFactory и использовать его, или я могу вставить экземпляр в конструктор. Если я создаю его в классе, я помещаю зависимости в своем классе в ChannelFactory / Wcf, и я действительно не хочу этого делать. Если я внедряю экземпляр в свой конструктор, то как класс инъекций будет управлять и приводить в порядок службу wcf? Кажется, что хотя у меня есть интерфейс, у меня нет чистого способа его использования. Я смотрел на что-то вроде NInject, но я не уверен, что он очистит ChannelFactory, если он выйдет из строя (по крайней мере, я не нашел никакой документации, показывающей, что он знает, когда вызывать Abort, а не Close на канале).

Я закончил тем, что снова улучшил свой интерфейс и использовал метод, описанный в этом вопросе: создание WCF ChannelFactory и просто вызов методов в службе. Это немного "пахнет" для меня, когда я снова завершаю все свои звонки, просто чтобы убедиться, что канал правильно закрыт / прерван.

Есть ли у кого-нибудь шаблоны / методы, которые чисто имеют две реализации интерфейса, один из которых - сервис Wcf?

Спасибо

Mike.

1 Ответ

5 голосов
/ 24 августа 2012

Я использовал вариант ответа , связанный с комментарием Марка и этой статьи здесь , чтобы найти решение, которое сработало для меня.

С учетом контракта на обслуживание, который вы определили, на шаге 1 будет определен интерфейс, реализующий ваш сервис, и IClientChannel.

// Service Contract
public interface ICalculator
{
    Add(double a, double b);
}

// Interface to expose Close and Abort
public interface ICalculatorChannel : ICalculator, IClientChannel { }

Шаг 2 включает создание кода многократного использования, который будет обрабатывать создание прокси-серверов и реализацию кода для закрытия или прерывания соединений.

public class ServiceClient<T> where T : class, IClientChannel
{
    private ProxyGenerator _generator = new ProxyGenerator();

    public T CreateProxy(string endpointConfigurationName)
    {
        return _generator.CreateInterfaceProxyWithoutTarget<T>
            (new WcfInterceptor<T>(endpointConfigurationName));
    }
}

T в ServiceClient<T> примет ICalculatorChannel, определенный на шаге 1. ProxyGenerator является частью проекта Castle. Здесь он используется для перехвата вызовов службы WCF и выполнения действий до и после публикации.

public class WcfInterceptor<T> : IInterceptor where T : IClientChannel
{
    private ChannelFactory<T> _factory = null;

    public WcfInterceptor(string endpointConfigurationName)
    {
        _factory = new ChannelFactory<T>(endpointConfigurationName);
    }

    public void Intercept(IInvocation invocation)
    {
        T channel = _factory.CreateChannel();

        try
        {
            invocation.ReturnValue = invocation.Method.Invoke
                (channel, invocation.Arguments);
        }
        finally
        {
            closeChannel(channel);
        }
    }
}

Как видите, метод Intercept инкапсулирует канал и вызов для закрытия канала. Метод closeChannel будет обрабатывать решение о вызове Close() или Abort().

    private void closeChannel(T channel)
    {
        if (channel != null)
        {
            try
            {
                if (channel.State != CommunicationState.Faulted)
                {
                    channel.Close();
                }
                else
                {
                    channel.Abort();
                }
            }
            catch
            {
                channel.Abort();
            }
        }
    }

Теперь мы создаем класс, чтобы завершить использование этого ServiceClient.

public class Calculator : ICalculator
{
    private var _calcualtor = new ServiceClient<ICalculatorChannel>();

    public void Add(double a, double b)
    {
        var proxy = _calculator.CreateProxy("endpointConfigGoesHere");

        return proxy.Add(a, b);
    }
}

Обратите внимание, что реализация add больше не связана с проблемами соединения WCF. Класс потребления должен знать только о договоре на обслуживание. Ужасность работы с неисправными соединениями теперь о нас заботится класс ServiceClient.

Теперь клиенту нужна только одна зависимость.

public class MyClient
{
    private ICalculator _calculator;

    public MyClient(ICalculator calculator)
    {
        _calculator = calculator;
    }
}

И МОК может заполнить клиента:

Bind<ICalculator>().To<Calculator>();
...