Фабрики или инжектиране на зависимост за инстанциране на обект в WCF, когато кодирате срещу интерфейс?

Пиша приложение клиент/сървър, където клиентът е приложение на Windows Forms, а сървърът е WCF услуга, хоствана в услуга на Windows. Имайте предвид, че контролирам и двете страни на приложението.

Опитвам се да приложа практиката на кодиране срещу интерфейс: т.е. имам споделено сглобяване, което е посочено от клиентското приложение. Този проект съдържа моите WCF ServiceContracts и интерфейси, които ще бъдат изложени на клиенти. Опитвам се да изложа само интерфейси на клиентите, така че те да зависят само от договор, а не от конкретно изпълнение. Една от причините да правя това е, за да мога да внедрявам услугата си и да променя домейна си по всяко време, без да се налага да компилирам и преразпределям клиентите. В този случай интерфейсите/договорите няма да се променят. Трябва само да прекомпилирам и преразпределя своята WCF услуга.

Проблемът с дизайна, с който се сблъсквам сега, е: на клиента, как да създам нови екземпляри на обекти, напр. ICustomer, ако клиентът не знае за Customer конкретното изпълнение? Трябва да създам нов клиент, който да бъде записан в базата данни.

Да използвам ли инжектиране на зависимости или фабричен клас за инстанциране на нови обекти, или просто трябва да позволя на клиента да създава нови екземпляри на конкретни реализации?

Не правя TDD и обикновено ще имам само една реализация на ICustomer или друг открит интерфейс.


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


Отговори (4)


Обсъждали сме да направим това вътрешно за корпоративни приложения, където контролираме и двете страни на приложението, като печалба в производителността, за .NET клиенти. Журито все още не е наясно с това.

Въпреки това, когато се обсъжда наличието на договори в споделена библиотека (между клиент и услуга), обикновено това ще включва и договорите за услуга ([ServiceContract]), както и обекти ([DataContract]) за всякакви параметри за обслужване на операции. Тези типове традиционно са конкретните типове, които очаквате за тези сервизни операции.

Във вашия случай вашият DTO внедрява интерфейс, като например ICustomer, внедряващ свойства, които представляват клиента, и е [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 ще притежавате, така че да можете да контролирате как ще изглежда конкретен клиент. Имам ли повече смисъл? Ако не, позволете ми да знам! - 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 чрез Add Service Reference, или можете да генерирате класове по време на изпълнение въз основа на интерфейсите, използвайки рамка от някакъв вид.

И двата подхода ви дават предимствата на кодирането спрямо интерфейс: докато интерфейсът не се промени, сървърът и клиентът могат да бъдат модифицирани и преустроени независимо. В единия случай интерфейсът е WSDL, а в другия е CLR интерфейс, но тъй като WCF генерира WSDL въз основа на CLR интерфейса, те наистина са едно и също. Лично аз харесвам Add Service Reference, защото вече е там и не добавя никакви зависимости към моя проект, но изберете това, което ви харесва повече.

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]s). Следователно и двамата споделят същите типове от едно и също сглобяване. Използвам NetDataContractSerializer и използвам WCF, за да емулирам нещо по-близко до .Net remoting - person Saajid Ismail; 14.08.2009

Вероятно вече сте получили отговора си, но в момента се опитвам да направя нещо подобно - да инжектирам моите хранилища в моите WCF услуги, така че тези услуги да нямат никакви зависимости от кода, който има достъп до db. Намерих следните статии, които смятам, че могат да помогнат да отговорите на вашия въпрос:

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

HTH

Сиаран

person Ciaran O'Neill    schedule 30.03.2010