【问题标题】:Exposing LinqToSQL business layer via WCF data service通过 WCF 数据服务公开 LinqToSQL 业务层
【发布时间】:2010-07-06 06:24:15
【问题描述】:

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

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

  2. 我的业务对象的一些属性被隐藏了。

  3. 数据服务可以处理比较大的数据库(上万个业务实体)

  4. 实施数据服务不需要我几周时间

我尝试了几种不同的方法,但每种方法都有相当大的缺点。对于我忽略的事情的任何指导将不胜感激。

这些是我迄今为止尝试过的方法:

第一种方法:基于反射的提供者实现 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

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

那么,我忽略了什么?接下来我该怎么办?我的时间不多了,所以任何建议都将不胜感激。

谢谢,

阿德里安

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

      猜你喜欢
      • 2011-07-21
      • 2011-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多