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

发布于 2024-09-08 00:03:35 字数 3517 浏览 6 评论 0原文

我正在寻找一种通过 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”上没有通用方法“OrderBy”。 Queryable' 与提供的类型参数和参数兼容。如果方法是非泛型的,则不应提供类型参数。

我不知道为什么会发生这种情况,但我被困在这一点上。

第二种方法:基于反射的提供程序实现 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 的文章系列 关于这个主题并查看了 odata 网站上提供的示例,我仍然不明白我需要实现的大多数功能实际上是做什么的。

看起来比仅仅实现 IUpdateable 要复杂得多,特别是因为我不知道示例中提供的哪些代码部分可以简单地复制和粘贴或需要自定义。

第五种方法:使用 EF 并找到某种方法使业务层与 Linq to SQL 和 EF 一起工作

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

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

谢谢,

阿德里安

I'm looking for a way to expose a subset of my existing business layer (which is based on LinqToSql) via WCF Data Services so that

  1. The Data Service can still be consumed (read AND write) by older odata clients as my business layer evolves over time. It seems that the only way to guarantee this is by making sure the data service does not change, even though the business layer changes.

  2. Some properties of my business objects are hidden.

  3. The data service can handle relatively large databases (tens of thousands of business entities)

  4. It doesn't take me several weeks to implement the data service

I've tried a few different approaches, but each of them has considerable downsides. Any guidance on things I have overlooked would be greatly appreciated.

These are the approaches I have tried so far:

First Approach: Reflection based provider implementing IUpdateable, business objects implementing a well-defined interface

Instead of exposing my LinqToSQL objects directly, I tried having them implement an interface, which defines the properties I want to expose and expose only that interface on the data service. For example If I have a Customer linq entity, I have it implement an ICustomer interface:

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;}
}

Then in my Data Service Provider I simply put a property like

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

Unfortunately this does not work. When trying to access the data service from the client side and use any Linq Method relying on OrderBy (such as myContext.Customers.First() ), I get the following error:

No generic method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.

I'm not sure why this happens, but I'm stuck at this point.

Second Approach: Reflection based provider implementing IUpdateable, wrapper class around my exising entities

In this case I tried implementing classes around my linq entities and exposing the wrapper class with the data service. Something like:

  //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; } }
  }

The problem with this approach is that it does not work unless I load all Customer entities from my database to memory with every request to the data service. That's because the Customers property on the on the Data Service provider looks like:

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();
  }
}

So even if the page size for my wcf data service is limited to 100, the code above would load ALL customer entities to memory before it can find the proper entities that the client actually wants to load. So this does not seem to scale properly.

Third Approach: Expose LinqToSQL entities directly, use IgnoreProperties attribute

In this case I would have my data service return the linqtosql entities originating from my business layer directly and use the IgnoreProperties attribute to hide the properties I do not want to expose in the data layer. This approach scales and is easy to implement, but it does not support versioning of the data service properly. For example if I want to implement a version 2 of my data service, I might need to hide less linq entity properties than in the first service. and there doesn't seem to be a way to use several IgnoreProperties attributes on a single LinqToSQL entities.

Fourth approach: Implement a custom data service provider

It seems that custom data service providers can easily cover all of my requirements. Now it might be just me, but this looks too complicated. It seems I have to implement IDataServiceMetadataProvider, IDataServiceQueryProvider, IDataServiceUpdateProvider, and IServiceProvider, none of which are trivial. And even after reading Alex J's article series on this subject and having a look at the sample provided on the odata website, I still don't get what most of the functions I need to implement actually do.

It seems much more complicated that just implementing IUpdateable, especially since I do not know which of the code portions provided in the samples can simply be copied and pasted or need to be customized.

Fifth approach: Use EF and find some way to make the business layer work with both Linq to SQL and EF

I succeeded in implementing a proof of concept for this, but it's rather messy, so I'd only use this as a last resort.

So, what have I overlooked? What should I do next? I'm running out of time, so any advice would be greatly appreciated.

Thanks,

Adrian

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

贩梦商人 2024-09-15 00:03:35

看来满足我所有要求的唯一方法是实现自定义数据服务提供程序(请参阅此线程)。但这不是一个小任务,因此我决定暂时使用反射提供程序 + IUpdateable 实现,并在以后需要时(由于版本控制问题)实现自定义数据服务提供程序。

It appears that the only way to cover all of my requirements is to implement a custom data service provider (see this thread). This is not a small task though, so I decided to use the reflection provider + IUpdateable implementation for now and implement a custom data service provider if the need should arise (due to versioning issues) later.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文