Альтернативный wo uld должно реализовать управление вашими контрактами на обслуживание: в этот момент вы можете использовать собственные функции WCF, чтобы игнорировать незначительные изменения, которые не нарушают работу клиента, как указано на этой странице Versioning Strategies.
На рисунке 1 вы можете видеть, что при добавлении новых параметров к сигнатуре операции, удалении параметров из сигнатуры операции и добавлении новых операций клиент не подвергается воздействию.
В случае, если все еще происходят изменения, или ваш клиент должен поддерживать обе версии (пожалуйста, исправьте меня, если я ошибаюсь, так как не знаю вашу стратегию развертывания), вы можете предлагать разные версии службы на разных конечных точках и в вашем клиентском коде есть фабрика клиентов WCF, которая затем может быть настроена для возврата клиента для соответствующей конечной точки.
На данный момент вы изолировали различные реализации в разных клиентах, что, вероятно, является более чистым и менее кошмаром обслуживания, чем сейчас.
Очень простой пример реализации, чтобы очистить вещи: предположим, что мы имеем два различных контрактов на наш сервис, старый и новый.
[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/03")]
public interface IServiceOld
{
[OperationContract]
void DoWork();
}
[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/04")]
public interface IServiceNew
{
[OperationContract]
void DoWork();
[OperationContract]
void DoAdditionalWork();
}
Обратите внимание, что обе службы имеют одно и то же имя, но разные пространства имен.
Давайте рассмотрим проблему с клиентом, который должен поддерживать как расширенный, так и новый сервис и старый. Предположим, что мы хотим вызвать метод DoAdditionalWork, когда мы ранее называли DoWork, и что мы хотим обрабатывать ситуацию на стороне клиента, потому что гипотетически DoAdditionalWork может потребовать дополнительные аргументы от клиента. Тогда конфигурация службы может быть что-то вроде этого:
<service name="ConsoleApplication1.Service">
<endpoint address="http://localhost:8732/test/new" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceNew" />
<endpoint address="http://localhost:8732/test/old" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceOld" />
...
</service>
Хорошо, у нас есть сторона службы, то теперь к интересной части: мы хотели бы общаться с услугами, используя один и тот же интерфейс. В этом случае я буду использовать старый, но вам может понадобиться установить адаптер между ними. В идеале, в нашем клиентском коде, мы хотели бы сделать что-то вроде этого:
IServiceOld client = *Magic*
client.DoWork();
Магия в данном случае является простой завод, как это:
internal class ClientFactory
{
public IServiceOld GetClient()
{
string service = ConfigurationManager.AppSettings["Service"];
if(service == "Old")
return new ClientOld();
else if(service == "New")
return new ClientNew();
throw new NotImplementedException();
}
}
Я делегировал решение которой клиент использовать для app.config, но вы можете вставить свою версию. Реализация ClientOld просто обычный WCF клиент для IServiceOld:
public class ClientOld : IServiceOld
{
private IServiceOld m_Client;
public ClientOld()
{
var factory = new ChannelFactory<IServiceOld>(new WSHttpBinding(), "http://localhost:8732/test/old");
m_Client = factory.CreateChannel();
}
public void DoWork()
{
m_Client.DoWork();
}
...
}
ClientNew вместо реализует поведение мы желать, а именно вызов операции DoAdditionalWork:
public class ClientNew : IServiceOld
{
private IServiceNew m_Client;
public ClientNew()
{
var factory = new ChannelFactory<IServiceNew>(new WSHttpBinding(), "http://localhost:8732/test/new");
m_Client = factory.CreateChannel();
}
public void DoWork()
{
m_Client.DoWork();
m_Client.DoAdditionalWork();
}
...
}
Вот именно, теперь наш клиент может как в следующем примере:
var client = new ClientFactory().GetClient();
client.DoWork();
Что мы достигли? Код с использованием клиента абстрагируется от того, какую дополнительную работу должен выполнять фактический клиент WCF, и решение о том, какой клиент использовать, делегируется на заводе. Я надеюсь, что некоторые варианты/расширение этой выборки соответствуют вашим потребностям.
Спасибо за быстрый ответ! Как минимум, я думаю, что мы должны централизовать эту логику с помощью метода IsValidToRun. Я считаю, что класс ServerFeatures является отличной идеей и может помочь нам отслеживать, какие проверки можно устранить по мере выпуска новых сборок. –
@JohnRussell, это также даст вам хороший способ разбить сборку по назначению. Вы удаляете версию «ServerFeatures», когда вам больше не нужна проверка, компиляция, а затем исправление всех сломанных мест, которые полагаются на нее. –
Точно! Мне это нравится! Я думаю, что я действительно сделаю ServerFeature перечислением, а затем выполним сопоставление Version to ServerFeature в вспомогательном методе, например. большой оператор переключения. Это позволило бы нам повторно использовать одну и ту же версию для нескольких функций и иметь всю эту логику в одном месте. Затем IsValidToRun может взять ServerFeature в качестве параметра и, надеюсь, не позволит потребителям использовать объекты на лету. Спасибо за помощь! –