Фабрики или внедрение зависимостей для создания объектов в WCF при кодировании интерфейса?

Я пишу клиент-серверное приложение, где клиент — это приложение Windows Forms, а сервер — служба WCF, размещенная в службе Windows. Обратите внимание, что я контролирую обе стороны приложения.

Я пытаюсь реализовать практику кодирования интерфейса: т.е. у меня есть общая сборка, на которую ссылается клиентское приложение. Этот проект содержит мои сервисные контракты WCF и интерфейсы, которые будут доступны клиентам. Я пытаюсь предоставлять клиентам только интерфейсы, чтобы они зависели только от контракта, а не от какой-либо конкретной реализации. Одна из причин этого заключается в том, что я могу реализовать свою службу и изменить домен в любое время без необходимости повторной компиляции и повторного развертывания клиентов. Интерфейсы/контракты в этом случае не изменятся. Мне нужно только перекомпилировать и повторно развернуть мою службу WCF.

Проблема дизайна, с которой я сталкиваюсь сейчас, заключается в следующем: на клиенте, как мне создавать новые экземпляры объектов, например. ICustomer, если клиент не знает о Customer конкретной реализации? Мне нужно создать нового клиента для сохранения в БД.

Использую ли я внедрение зависимостей или класс Factory для создания экземпляров новых объектов, или я должен просто разрешить клиенту создавать новые экземпляры конкретных реализаций?

Я не использую TDD, и обычно у меня будет только одна реализация ICustomer или любого другого открытого интерфейса.


person Saajid Ismail    schedule 14.08.2009    source источник


Ответы (4)


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

Однако при обсуждении наличия контрактов в общей библиотеке (между клиентом и службой) обычно это включает в себя оба контракта службы ([ServiceContract]), а также сущности ([DataContract]) для любых параметров операций обслуживания. Эти типы традиционно являются конкретными типами, которые вы ожидаете для этих операций службы.

В вашем случае ваш DTO реализует интерфейс, такой как ICustomer, реализующий свойства, представляющие Customer, и является [DataContract]. Предполагая, что тип будет правильно сериализоваться в службе (с использованием NetDataContractSerializer), тогда я полагаю, что клиент может в значительной степени подтолкнуть любую конкретную реализацию, которую он хочет - службу интересует только то, что соответствует ICustomer.

Клиент может создать любой конкретный экземпляр ICustomer, который ему нужен: OrderingCustomer, FinanceCustomer и т. д. Поскольку тип реализует интерфейс службы ICustomer, его вполне можно передать в качестве значения операции службы, если он правильно сериализуется. например

public class OrderingCustomer : ICustomer
{
}

Я не уверен, что вы добьетесь нулевого воздействия на клиента, к которому стремитесь. Если вы измените тип интерфейса (добавите свойство в ICustomer), вашим клиентам потребуется перекомпилировать. Если вы добавите параметр, даже одного из основных типов .NET (например, int), вашим клиентам потребуется перекомпилировать. По сути, это то же самое влияние, что и обновление клиентской ссылки на службу и повторная компиляция.

Однако, если вы не меняете реализацию или поведение службы (например, исправление ошибки), то в оба случаях (общие типы или ссылка на службу) клиентам не нужно ничего делать, поскольку пока этот контракт между вами и вашим клиентом не изменится. Конечно, я также хотел бы услышать о вашем опыте, который доказывает, что это неправильно!

Эта практика также полностью убьет вашу интероперабельную историю с системами, отличными от .NET. Как бы я ни заметал это под ковер, какой-нибудь отдел где-нибудь услышит о вашем супер-элегантном сервисе и захочет его использовать... и они будут использовать какой-нибудь стек Java, или COBOL, или FORTRAN... и т.д. :)

person Zach Bonham    schedule 17.08.2009
comment
Вы точно описали, чего я пытаюсь добиться, но не ответили на мой вопрос - как мне создавать новые экземпляры объектов на стороне клиента. Вы, очевидно, не можете сделать это: ICustomer cus = new ICustomer, и вы не можете сделать это: ICustomer cus = new Customer(), так как у клиента нет доступа к сборке, содержащей реализацию Customer: ICustomer. - person Saajid Ismail; 17.08.2009
comment
ах, я думаю, что я пропустил пункт в своем ответе! Вот что я получаю за слишком быстрое чтение! :) Клиент должен иметь доступ к ICustomer, если ICustomer является параметром операции службы. В противном случае, как они когда-либо вызовут службу? Они не будут знать, как выглядит ICustomer — они не могут просто придумать это на лету, это бинарный контракт между клиентом и службой. Клиент не может создать свою собственную реализацию ICustomer и использовать ее, вы должны предоставить ее. :) - person Zach Bonham; 17.08.2009
comment
Вы снова пропустили критический момент. У клиента есть доступ к ICustomer, он находится в общей сборке, на которую ссылается как клиент, так и сервер. Это обязательно! Но ICustomer — это интерфейс, и вы не можете создать экземпляр типа интерфейса, как объяснялось выше. - person Saajid Ismail; 17.08.2009
comment
Прости. Думаю, я понял, просто плохо сформулировал свой ответ! Вы предоставили клиентам ICustomer, чтобы клиенты могли реализовать ICustomer по своему усмотрению и перенаправить его в вашу службу. Пока тип клиента соответствует Service.ICustomer, все будет в порядке. Эти конкретные типы были бы вне вашего контроля - вы бы владели только интерфейсом ICustomer, чтобы вы могли контролировать, как будет выглядеть конкретный Customer. Я имею больше смысла? Если нет, дай мне знать! - person Zach Bonham; 17.08.2009
comment
Я подчеркнул этот момент в своем ответе выше, чтобы посмотреть, поможет ли это? - person Zach Bonham; 17.08.2009
comment
Да, это имеет смысл :-) Думаю, я мог бы создать новую реализацию ICustomer только на стороне клиента и передать ее серверу. Я больше думал об использовании фабрики или DI в общей сборке, чтобы создать новый экземпляр Customer, но вернуть его как ICustomer. Это сработает и имеет для меня больше смысла, поскольку я контролирую и сервер, и клиент. Мне не нужна еще одна конкретная реализация ICustomer. Суть в том, что я изо всех сил стараюсь программировать только для интерфейса. - person Saajid Ismail; 18.08.2009

Я столкнулся с тем же вопросом в моей разработке WCF. Дело в том, что должна быть конкретная реализация ваших контрактов на данные с обеих сторон коммуникации. Так откуда берутся эти реализации? На стороне сервера реализациями обычно являются ваши бизнес-объекты со всей их бизнес-логикой и так далее. И вы не хотите использовать эти реализации на клиенте — это означало бы поместить их в общую сборку, а вы этого не хотите по нескольким причинам. Так что клиенту нужен свой, отдельный набор конкретных классов. Либо вы будете писать их сами, либо они будут генерироваться автоматически.

Самостоятельно писать реализации утомительно. Поскольку клиентские объекты, как правило, просты — все, что они на самом деле делают, — это хранят данные, — генерация кода, вероятно, является выходом из положения. На этом этапе у вас есть несколько вариантов: вы можете сгенерировать код на основе WSDL через Добавить ссылку на службу или вы можете создать классы во время выполнения на основе интерфейсов, используя какую-либо структуру.

Оба подхода дают вам преимущества кодирования по сравнению с интерфейсом: пока интерфейс не изменяется, сервер и клиент могут быть изменены и перестроены независимо друг от друга. В одном случае интерфейс — это WSDL, а в другом — интерфейс CLR, но поскольку WCF генерирует WSDL на основе интерфейса CLR, на самом деле это одно и то же. Лично мне нравится Добавить ссылку на службу, потому что она уже есть и не добавляет никаких зависимостей в мой проект, но выбирайте то, что вам больше нравится.

person Aaron    schedule 14.01.2010

Ну, я думаю, вы путаете (или смешиваете) две вещи:

  • ваш Контракт на обслуживание будет описывать только функции, предоставляемые вашей службой, такие как FindCustomer или AddCustomer — для этих операций вашему клиенту нужно знать только интерфейс, и когда вы добавляете клиентский прокси (используя «Добавить Service Reference"), вы также получите YourServiceClient конкретный класс в своем клиентском прокси, который реализует эти вызовы.

  • Контракт данных описывает данные, передаваемые туда и обратно, и это всегда конкретные классы — Customer, Invoice и т. д. — поскольку они основаны на схеме XML. Они определяются на сервере, и клиент при добавлении ссылки на службу выводит эти типы из WSDL/XSD, опубликованных службой, и создает конкретные классы на стороне клиента для этих данных. . Это НЕ те же самые классы, которые использует сервер — они выглядят одинаково, и их основное требование состоит в том, чтобы они сериализовались в XML и десериализованы из него одинаково — но на самом деле это разные классы (разные файлы . NET, скорее всего).

Таким образом, для вашей функциональности у вас есть сервисный контракт, который вам действительно нужно использовать только как интерфейс - этого достаточно, и клиент создаст «прокси-класс» (класс YourServiceClient) из этого интерфейса. Клиент не зависит от реализации сервера или его конкретного класса.

С другой стороны, обмениваемые данные — сериализованные в формате XML по умолчанию — всегда будут конкретными классами (без интерфейсов), которые выводятся из WSDL/XSD — опять же, клиент не зависит от классов сервера — только от их «отпечаток XML-сериализации», так сказать.

Теперь я не уверен, что это сильно вам поможет :-) но, по крайней мере, надеюсь, это сделает вещи немного яснее - или нет?

Марк

person marc_s    schedule 14.08.2009
comment
@marc_s: я думаю, вы совершенно неправильно поняли мой вопрос. Я думаю, что я должен изменить его, чтобы сделать его более понятным. Я полностью понимаю WCF и все, что вы объяснили. Но я не использую WCF так. И клиент, и сервер совместно используют сборку MyProject.Shared, которая содержит мои типы и интерфейсы (или контракты, как я это буду называть, но не [DataContract]). Поэтому они оба используют одни и те же типы из одной сборки. Я использую NetDataContractSerializer и использую WCF для эмуляции чего-то более близкого к удаленному взаимодействию .Net. - person Saajid Ismail; 14.08.2009

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

http://geekswithblogs.net/ballhaus/archive/2009/12/12/unit-testing-your-wcf-service.aspx http://www.silverlightshow.net/items/Deep-dive-into-WCF-part-1-TDD.aspx

ХТН

Киаран

person Ciaran O'Neill    schedule 30.03.2010