Generics 围绕使用通用CRUD方法的WCF web服务设计包装器-一个好的解决方案?

Generics 围绕使用通用CRUD方法的WCF web服务设计包装器-一个好的解决方案?,generics,web-services,repository,Generics,Web Services,Repository,众所周知,使用通用方法创建web服务是不可能的。你必须设计信息 但是我想到了使用反射在WCF周围创建一个包装器 public class WcfRepository<T> : IWcfRepository<T> where T : class { public IList<T> GetList() { Type wcfService = typeof (Service1Client);

众所周知,使用通用方法创建web服务是不可能的。你必须设计信息

但是我想到了使用反射在WCF周围创建一个包装器

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();
        }
    }
你觉得我的包装怎么样?
您可以看到哪些优点和缺点?

您的实现的主要问题是WCF代理并不便宜。反射不会对您造成太大的伤害,但是为每个请求创建一个新的代理(顺便说一句,没有正确地处理它)肯定会


设计的主要问题是它假定每种类型的实体都支持相同的CRUD契约/接口。实际上,您可能有一些是只读的(引导和其他元数据),一些是仅CR的(日志或事务数据),一些是仅CRU的(关键基础数据,如“存储”或“帐户”),还有一些根本不是真正的实体,需要额外的参数来检索。此外,您可能需要几个不同类型的“GetByID/GetByXYZ”类型方法;消费者很少真正希望在没有任何筛选或投影的情况下列出数据库中包含的每个项目

通过尝试创建一个通用的抽象,您隐藏了有关服务实际可以支持什么的关键信息,并允许消费者做出直到为时已晚才知道是无效的假设。您已经创建了一个场景,允许代码以意外甚至不可预测的方式中断


概念的主要问题是Web服务旨在封装业务或应用程序逻辑,而不是数据源。在DAL上创建一层薄薄的饰面不会给整个解决方案带来真正的价值,而只是客户将被迫处理的另一层间接问题。Web服务很棒,但它们增加了大量的开发和维护开销,因为每个模式/数据更新都必须在两个地方进行,而不是一个地方。向应用程序添加第三层(或第四层或第五层)通常仅在该层提供一些额外的智能或至少是封装时才合适。完全围绕数据访问操作构建的服务体系结构是一种“糟糕的体系结构味道”,因为它们只是在功能严重受限的情况下重新实现数据库

例如,面向服务或消息的“订单”请求可能允许消费者指定任何或所有客户、日期范围和一系列其他特定于领域的标准—产品类型、数量、总成本、付款方式等。您可能希望将所有这些整合到单个服务操作中;消费者发送一条消息,详细说明他想要什么,然后您相应地提供它。当然,对“客户”的类似请求不会有任何这些标准;相反,您可能会根据注册日期、地理位置、信用评级等返回结果。从这个意义上说,每个请求都是完全唯一的;您正在向消费者提供一项服务,以允许他们具有简单CRUD层很少提供的灵活性。您这样做是为了能够将它暴露给具有不同需求的各种不同消费者,而不必不断更改服务的合同。您提议的体系结构不适合实现这一最终目标


这可能只是我的观点,其他人可能对此有其他的看法;我所能补充的是,我将这些陈述建立在个人使用Web服务(我每天使用Web服务)的经验基础上也不一定是传统智慧——尽管我相信传统智慧在这里与我一致。

我偶然发现了这条线索,但奇怪的是,我在空闲时间为这里暴露的同一问题制定解决方案,就像一个流浪汉一样,因为现在在工作中,我们有大约300项服务,而且这一数字还在增长,还有一些基本操作(CRUD)具有相同的结构,只改变EntityType,还有许多自定义方法,我不喜欢重复这些,所以我认为这是个不错的主意,但我想关注一些由Aaronaguth解释的问题——在我看来——非常好,因为现在我将思考如何应对这些问题。也是

因此,基本上这个概念是相同的,只有一个服务(考虑到完整的解决方案可能会更多,但现在只有一个)可以处理所有类型的请求

因此,关于不同的参数,我在服务中有一个名为Execute(Request-Request)的方法,它接收一个请求,这个请求可以是一个通用请求对象,也可以是一个自定义请求对象,比如CreateRequest或DeleteRequest。自定义请求具有该操作所需的属性,因此我还可以创建ApproverRequest。此请求包含有关EntityType的信息、在BusinessComponent中声明的操作名称以及其他参数(请求的行为类似于属性包或字典),业务组件方法的定义类似于:Update(BusinessEntity实体)或Approve(Guid orderId,bool anotherParameterHere)

调用execute时,我处理请求并提取基本上需要的信息EntityType和OperationName,然后我将请求对象中的输入参数映射到方法的预期参数,我也使用反射来完成此操作。。。在BaseBusinessComponent中,我可以创建BusinessEntity的强类型对象,比如说Customer,这个Customer继承自BusinessEntity

另一件重要的事情是,响应的处理方式与请求的处理方式相同,如果请求有相应的响应类(只需将请求替换为响应),那么我将创建该请求的一个对象,并添加
var result = new WcfRepository<Employee>().GetList();
var customers = myWcfService.GetCustomerList();
var teams = myWcfService.GetTeamList();
var products = myWcfService.GetProductList();
            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);