通过WCF数据服务公开LinqToSQL业务层

时间:2010-07-06 06:24:15

标签: linq-to-sql wcf wcf-data-services

我正在寻找一种方法,通过WCF数据服务公开我现有业务层的一个子集(基于LinqToSql),以便

  1. 随着我的业务层随着时间的推移,旧的odata客户端仍然可以使用(读取和写入)数据服务。看来保证这一点的唯一方法是确保数据服务不会发生变化,即使业务层发生了变化。

  2. 我的业务对象的某些属性已隐藏。

  3. 数据服务可以处理相对较大的数据库(数以万计的商业实体)

  4. 实施数据服务不需要几周时间
  5. 我尝试过几种不同的方法,但每种方法都有相当大的缺点。对我忽略的事情的任何指导都将不胜感激。

    这是我到目前为止尝试过的方法:

    第一种方法:基于反射的提供程序实现IUpdateable,业务对象实现明确定义的接口

    我没有直接暴露我的LinqToSQL对象,而是尝试让它们实现一个接口,该接口定义了我想要公开的属性,并且只公开数据服务上的那个接口。例如,如果我有一个Customer linq实体,我让它实现一个ICustomer接口:

    public interface ICustomer{
       int ID{get;set;}
       string Name{get;set;}
    }
    
    
    //My Linq entity 
    public partial class Customer: ICustomer{
       public int ID{get;set;}
       public string Name{get;set;}
       public string SomeOtherPropertyIDoNotWantToExpose{get;set;}
    }
    

    然后在我的数据服务提供商中,我只是添加了一个属性,如

    public IQueryable<ICustomer> Customers    {
          get { 
            //Note that the method below returns an IQueryable<Customer>
            return MyBusinessLayerCustomersProvider.LoadAllCustomers();
          }
        }
    

    不幸的是,这不起作用。当尝试从客户端访问数据服务并使用任何依赖于OrderBy的Linq方法(例如myContext.Customers.First())时,我收到以下错误:

    类型'System.Linq.Queryable'上没有泛型方法'OrderBy'与提供的类型参数和参数兼容。如果方法是非泛型的,则不应提供类型参数。

    我不确定为什么会发生这种情况,但我现在已经陷入困境。

    第二种方法:基于反思的提供商围绕我的现有实体实施IUpdateable包装类

    在这种情况下,我尝试在我的linq实体周围实现类,并使用数据服务公开包装类。类似的东西:

      //My Linq entity 
      public partial class Customer 
      {
        public int ID { get; set; }
        public string Name { get; set; }
        public string SomeOtherPropertyIDoNotWantToExpose { get; set; }
      }
    
      //the wrapper
      public partial class WrappedCustomer
      {
        internal Customer _wrappedEntity;
        public int ID { get{ return _wrappedEntity.ID;} set{ _wrappedEntity.ID = value;} }
        public string Name { get { return _wrappedEntity.Name; } set { _wrappedEntity.Name = value; } }
      }
    

    这种方法的问题在于它不起作用,除非我将每个数据服务请求从我的数据库加载到内存中。这是因为数据服务提供程序上的Customers属性如下所示:

    public IQueryable<Customer> WrappedCustomer
    {
      get
      {
        IQueryable<WrappedCustomer> customers = from c in MyBusinessLayerCustomersProvider.LoadAllCustomers
                     select new WrappedCustomer{_wrappedEntity = c};
    
        //I am casting to list to avoid "no supported translation to sql" errors. 
        return tasks.ToList().AsQueryable();
      }
    }
    

    因此,即使我的wcf数据服务的页面大小限制为100,上述代码也会将所有客户实体加载到内存中,然后才能找到客户端实际要加载的正确实体。所以这似乎没有正确扩展。

    第三种方法:直接暴露LinqToSQL实体,使用IgnoreProperties属性

    在这种情况下,我会让我的数据服务直接返回源自我的业务层的linqtosql实体,并使用IgnoreProperties属性隐藏我不想在数据层中公开的属性。此方法可以扩展并易于实现,但它不支持正确地对数据服务进行版本控制。例如,如果我想实现我的数据服务的第2版,我可能需要隐藏比第一个服务更少的linq实体属性。并且似乎没有办法在单个LinqToSQL实体上使用多个IgnoreProperties属性。

    第四种方法:实施自定义数据服务提供商

    自定义数据服务提供商似乎可以轻松满足我的所有要求。现在它可能只是我,但这看起来太复杂了。我似乎必须实现IDataServiceMetadataProvider,IDataServiceQueryProvider,IDataServiceUpdateProvider和IServiceProvider,其中没有一个是微不足道的。即使在阅读Alex J's article series关于这个主题并查看odata网站上提供的示例之后,我仍然无法获得我需要实现的大部分功能。

    实现IUpdateable似乎要复杂得多,特别是因为我不知道样本中提供的哪些代码部分可以简单地复制和粘贴,或者需要自定义。

    第五种方法:使用EF并找到一些方法使业务层可以使用Linq to SQL和EF

    我成功地为此实现了一个概念验证,但它相当混乱,所以我只把它作为最后的手段。

    那么,我忽略了什么?接下来我该怎么办?我已经没时间了,所以任何建议都会非常感激。

    谢谢,

    阿德里安

1 个答案:

答案 0 :(得分:1)

看来,满足我所有要求的唯一方法是实施自定义数据服务提供商(see this thread)。这不是一项小任务,因此我决定立即使用反射提供程序+ IUpdateable实现,并在以后需要(由于版本控制问题)时实现自定义数据服务提供程序。