Разработка оболочки вокруг веб-службы WCF для использования общих методов CRUD - хорошее решение? - PullRequest
4 голосов
/ 14 декабря 2009

Как вы все знаете, создание веб-службы с использованием общих методов невозможно. Вы должны разработать сообщение.

Но у меня была идея создать оболочку вокруг WCF с помощью Reflection.

public class WcfRepository<T> : IWcfRepository<T> where T : class 
    {
        public IList<T> GetList()
        {   
            Type wcfService = typeof (Service1Client);

            string entityName = typeof(T).Name;
            string methodName = String.Format("Get{0}List", entityName);

            object instance = Activator.CreateInstance(wcfService, true);

            var result = (IList<T>) wcfService.InvokeMember(methodName,
                BindingFlags.InvokeMethod | BindingFlags.Default, null, instance, null);
            return result;
        }

        public T Save(T entity)
        {
            throw new NotImplementedException();
        }

        public void Update(T entity)
        {
            throw new NotImplementedException();
        }

        public void Delete(int id)
        {
            throw new NotImplementedException();
        }
    }

Вы бы использовали обертку так:

var result = new WcfRepository<Employee>().GetList();

Вот громоздкий путь без обертки:

var customers = myWcfService.GetCustomerList();
var teams = myWcfService.GetTeamList();
var products = myWcfService.GetProductList();

Так что вы думаете о моей обертке? Какие преимущества и недостатки вы видите?

Ответы [ 2 ]

3 голосов
/ 15 декабря 2009

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

Основная проблема с дизайном заключается в том, что предполагается, что каждый тип объекта поддерживает один и тот же контракт / интерфейс CRUD. В действительности у вас могут быть такие, которые предназначены только для чтения (начальная загрузка и другие метаданные), некоторые - только для CR (журнальные или транзакционные данные), некоторые - только для CRU (критические базовые данные, такие как «хранилище» или «учетная запись»). ") и некоторые, которые вообще не являются истинными сущностями и требуют дополнительных параметров для извлечения. Кроме того, вам, вероятно, понадобится несколько методов типа «GetByID / GetByXYZ», которые варьируются от одного типа к другому; редко потребитель действительно хотел бы перечислить каждый элемент, содержащийся в базе данных, без какого-либо фильтра или прогноза.

Пытаясь создать общую абстракцию, вы скрываете критическую информацию о том, что на самом деле может поддерживать служба, и позволяете потребителям делать предположения, которые они не будут знать, недействительными, пока не станет слишком поздно. Вы создали сценарий, который позволяет коду ломаться неожиданным и даже непредсказуемым образом.

Основная проблема с вашей концепцией заключается в том, что веб-службы предназначены для инкапсуляции бизнес-логики или логики приложения, а не источников данных . Создание тонкого шпона поверх DAL не добавляет реальной ценности к общему решению, это просто еще один уровень косвенности, с которым клиенты будут вынуждены иметь дело. Веб-сервисы хороши, но они значительно увеличивают затраты на разработку и обслуживание, потому что каждое обновление схемы / данных должно происходить в двух местах, а не в одном. Добавление 3-го (или 4-го, или 5-го) уровня к приложению обычно целесообразно, только если этот уровень обеспечивает некоторый дополнительный интеллект или, по крайней мере, инкапсуляция . Архитектуры сервисов, построенные исключительно на операциях доступа к данным, представляют собой «неприятный запах архитектуры», поскольку они просто воссоздают базу данных со строго ограниченными функциями.

Например, сервис-ориентированный или ориентированный на сообщения запрос на «Заказы», ​​вероятно, позволит потребителю указать любого или всех клиентов, диапазон дат и ряд других критериев, специфичных для домена - типы продуктов, количества, общая стоимость, способ оплаты и тд. Вы хотели бы объединить все это в одну Сервисную операцию; потребитель отправляет одно сообщение , в котором подробно указывается, чего он хочет, и вы предоставляете его соответствующим образом. Конечно, подобный запрос для «Клиентов» не будет иметь ни одного из этих критериев; вместо этого вы можете возвращать результаты на основе даты регистрации, географического положения, кредитного рейтинга и т. д. Каждый запрос будет в этом смысле совершенно уникальным; вы предоставляете услугу потребителям, чтобы предоставить им гибкость, которую редко предоставляет простой уровень CRUD. Вы могли бы сделать это для того, чтобы иметь возможность выставлять его различным потребителям с различными потребностями без необходимости постоянно менять договор на обслуживание . Предложенная вами архитектура не подходит для этой конечной цели.

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

1 голос
/ 05 марта 2011

Я нашел эту ветку случайно, но любопытно, что я в свое свободное время работаю над решением той же проблемы, которая здесь рассматривается просто как хобби, потому что сейчас на работе у нас около 300 сервисов, и это растет и есть базовые операции (CRUD), которые имеют одинаковую структуру и меняют только EntityType, и есть также много пользовательских методов, и я не люблю повторять это, так что я думаю, что это не плохая идея, но я Я хочу позаботиться о некоторых вопросах, которые, по моему мнению, очень хорошо объяснил Ааронагут, потому что теперь я буду думать о том, как реагировать на эти вещи. тоже.

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

Итак, что касается различных параметров, у меня есть метод в службе, называемый Execute (Запрос запроса), который принимает запрос, этот запрос может быть общим объектом Request или пользовательским объектом Request, таким как CreateRequest или DeleteRequest. У пользовательского запроса есть свойства, необходимые для этой операции, поэтому я мог бы также создать ApproveRequest. Этот запрос содержит информацию о EntityType, об имени операции, объявленном в BusinessComponent, и о других параметрах (запрос действует как пакет свойств или словарь), а методы бизнес-компонентов определяются следующим образом: Update (сущность BusinessEntity) или Approve (Guid orderId, bool anotherParameterHere).

Когда вызывается execute, я обрабатываю запрос и извлекаю необходимую мне информацию, в основном EntityType и OperationName, а затем сопоставляю входные параметры объекта запроса с ожидаемыми параметрами метода, который я получаю, используя отражение тоже ... и в BaseBusinessComponent я могу создать объект строго типа BusinessEntity, скажем, Customer, этот клиент наследует от BusinessEntity.

Другая важная вещь - это ответ, который обрабатывается так же, как и запрос, если у запроса есть соответствующий класс ответа (просто замените Request by Response), тогда я создаю объект этого запроса и добавляю значения, возвращаемые методом. в бизнес-компоненте, чтобы я мог вернуть пакет свойств и скопировать все свойства в Response или, если это не пакет свойств, я присваиваю значение (если метод не void) общему свойству «Результат», и этот результат может быть в моем CustomResponse я мог бы переопределить его, прочитать значение и назначить каждое отдельное свойство, известное в классе CustomResponse (но я еще этого не сделал).

Это пример кода, который я могу написать прямо сейчас - это нечто очень простое:

            AppService service = new AppService();

            //Create an order
            BusinessEntity order = new BusinessEntity("Order");
            order["OrderId"] = Guid.NewGuid();
            order["CustomerName"] = "Greivin Britton";

            CreateRequest request = new CreateRequest();
            request.Entity = order;

            CreateResponse response = (CreateResponse)service.Execute(request);

            //Create a customer
            Customer customer = new Customer();
            customer.FirstName = "Greivin";
            customer.LastName = "Britton";

            Request request2 = new Request();
            request2.MessageName = "Create";
            request.Entity = customer;

            Response response2 = service.Execute(request2);

Для извлечения данных с использованием различных фильтров, возможно, мне придется создать класс ExpressionBuilder или что-то в этом роде ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...