使用仓储模式实现WCF数据服务

3
我们在ASP.NET MVC 3应用程序中使用存储库模式。这意味着,尽管我们使用EF 4.1 Code First访问后端数据,但所有MVC控制器都通过通用存储库类而不是直接通过DbContext子类来实现数据访问。
简化代码片段:
public class MyEntityContext : DbContext, IMyEntityContext
{
    public IDbSet MyEntities { get; set; }
    ...
}

public class MyEntityRepository : IMyEntityRepository
{
    private IMyEntityContext _context;

    public IQueryable<MyEntity> MyEntities
    {
        return _context.MyEntities;
    }
    ...
}

public class MyEntityController : Controller
{
    private MyEntityRepository _repository;
    ...
}

我们对每个依赖项都使用接口和依赖注入。这很好用,看起来很不错,是吧?但现在有一个警告:
我们还提供了一个WCF数据服务(支持Code First的CTP)来访问实体。我们也想在该服务中使用存储库。但这似乎有些棘手。当直接使用MyEntityContext时,服务如下所示:
public class MyEntityService : DataService<MyEntityContext>
{
    public static void InitializeService(DataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule("MyEntities", EntitySetRights.All);
    }
}

但是,当我试图用存储库替换MyEntityContext时,出现了两个问题:

  1. 泛型DataService<..>指定的类型需要是具有默认构造函数的类,这破坏了美观的契约设计和依赖注入设计。
  2. 似乎提供的类型甚至必须是一个DbContext类:我尝试使用MyEntityRepository,但失败了(请参阅详细信息)。

我似乎迷失了... 有人能把我带回正确的轨道上吗?


细节:
我的第一次尝试是:
public class MyEntityService : DataService<MyEntityRepository>
{
    ...

然而,在调用服务时,出现以下错误消息:
服务器在处理请求时遇到错误。异常消息是“在数据上下文类型'MyEntityRepository'上,存在一个顶级IQueryable属性'MyEntities',其元素类型不是实体类型。确保IQueryable属性是实体类型或在数据上下文类型上指定IgnoreProperties属性以忽略此属性。”
我尝试了以下步骤来修复它,但无法摆脱这个错误消息:
- 将[DataServiceKey("MyEntityId")]添加到MyEntity中,其中MyEntityId是实体的正确关键属性。 - 将Repository.MyEntities的类型替换为IDbSet而不是IQueryable。

顺便提一下:以下帖子不是重复的:


什么是MyEntity?显然它不是一个“实体类型”。你尝试过使用“IgnoreProperties”属性吗? - Fernando
@Fernando:“MyEntity”是一个POCO类,用作实体。它在数据库中反映出来,并且EF成功地将其映射。唯一不起作用的是DataService。 - chiccodoro
我认为要使用 DataService<>,MyEntity 需要继承自 Entity。 - cadrell0
FYI:至少在WCF数据服务5.0和EF 5.0(在.NET 4.0上为4.3)中,这是开箱即用的。我正在做类似的事情。实际上,我的DbContext派生类只是一个代码优先流畅配置模型的聚集地,没有任何IQueryableDbSet属性,这仍然有效。这些升级工具可用于提问时可用的工具集,因此这似乎是无意义的。 - Marc L.
2个回答

3
为什么要使用存储库?你已经有上下文了,所以用它。不要只是因为想使用模式而创建洋葱架构。WCF数据服务已经自行处理了您需要的所有内容。不好意思,有时甚至提供更多功能(例如拦截器)。
通过使用自定义存储库,您正在转移到反射提供程序数据源。如果您还计划通过WCF数据服务修改实体,则这也与您的存储库相矛盾,因为反射提供程序是只读的,除非它还实现了IUpdateable。还请查看反射提供程序规则
顺便说一句,在.NET 4中,WCF数据服务不直接支持DbContext(该支持仅在即将推出的版本的CTP中),但是您可以使用解决方法。链接是旧版CTP的链接。在当前版本中,没有UnderlyingContext属性,但是您可以使用IObjectContextAdapter来获取ObjectContext

正如您在上一个链接中看到的,提供给服务的类型不需要具有默认构造函数 - 在创建数据源时使用哪个构造函数取决于您。如果您需要依赖注入,您可能需要检查如何直接注入到服务本身(例如Unity和纯WCF的here),并在CreateDataSource中使用注入的数据。


谢谢你的回答!事实上,我忘了提到我们使用支持Code First的CTP。默认构造函数:一开始我没有默认构造函数,然后它就抱怨了。为什么不使用DbContext:出于与在MVC控制器中不使用DbContext相同的原因:将数据访问实现与Web层分离。 - chiccodoro
那么,您会花费一周的时间来创建无用的抽象(也称为过度架构)以用于WCF数据服务,而不是直接使用它吗?花费一周的时间来创建没有任何额外业务价值甚至不能使其更易维护的东西是浪费。数据服务正如其名称所示,是用于与数据访问层进行直接通信的。 - Ladislav Mrnka
那么,如果我们开始实现一些验证、授权或安全修剪逻辑,我们总是要做两次吗?一次为控制器,一次为服务?那使用存储库的整个意义是什么呢? - chiccodoro
一旦你开始这样做,但你的例子只是暴露了IQueryable。问题是安全性(=横切面)是否是存储库的一部分?在我看来不是。存储库的整个重点是抽象数据访问-纯数据访问。 - Ladislav Mrnka
4
你有上下文,所以要善用它。不要仅仅因为想使用某种模式而创建洋葱架构。但是,如果我想暴露业务逻辑函数(而不是直接使用 EF 模型),并且仍然从 WCF 数据服务中受益(如 OData 查询、强类型的 .NET 客户端代码等),该怎么办? - gius
显示剩余2条评论

1
这是如何制作一个WCF数据服务的步骤,无论您使用什么模式,甚至没有任何模式。 从任何数据源构建OData服务:入门指南第二部分 只需确保您的实体、POCO、模型或任何内容都具有公共int ID属性,或者具有由System.Data.Services程序集提供的此类注释,在System.Data.Services命名空间中。
[DataServiceKey("TheNameOfYourPrimaryKeyProperty")]

这将使WCF数据服务能够识别它作为实体类型。
正如其他人指出的那样,确保在您的堆栈中添加另一个层次是明智的决定。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接