使用存储库模式实现 WCF 数据服务
我们在 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
时,存在两个问题:
- 为泛型 < 指定的类型code>DataService<..> 需要是一个具有默认构造函数的类,这打破了漂亮的契约设计和依赖注入设计。
- 甚至看起来提供的类型必须是
DbContext
类:我尝试并使用MyEntityRepository
来代替,但失败了(查看详细信息)。
我似乎迷失了......有人能带我回到正确的轨道吗?
详细信息:
我的第一次尝试是:
public class MyEntityService : DataService<MyEntityRepository>
{
...
但是,在调用该服务时,它失败并显示以下错误消息:
服务器在处理请求时遇到错误。异常消息是“在数据上下文类型“MyEntityRepository”上,有一个顶级 IQueryable 属性“MyEntities”,其元素类型不是实体类型。确保 IQueryable 属性属于实体类型,或者在数据上下文类型上指定 IgnoreProperties 属性以忽略此属性。'。
我尝试了以下步骤来修复此问题,但没有消除此错误消息:
- 将
[DataServiceKey("MyEntityId")]
添加到 MyEntity,其中 MyEntityId 是实体的正确关键属性。 - 将
Repository.MyEntities
的类型替换为IDbSet
而不是IQueryable
。
顺便说一句:以下帖子不重复:
We are using the repository pattern in our ASP.NET MVC 3 application. This means that, although we use EF 4.1 Code First to access the data in the backend, all MVC controllers do that via a generic repository class rather than directly over the DbContext subclass.
Simplified code snippet:
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;
...
}
We use interfaces and dependency injection for every dependency. It works fine. Looks nice, doesn't it? But now for the caveat:
We also provide a WCF Data Service (CTP supporting Code First) to access the entities. We want to use the repository in that service, too. But this seems tricky. When using the MyEntityContext
directly, the service looks like this:
public class MyEntityService : DataService<MyEntityContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("MyEntities", EntitySetRights.All);
}
}
But when I try to replace the MyEntityContext
by the repository, there are two issues:
- The type specified for the generic
DataService<..>
needs to be a class with a default constructor, which breaks the pretty design-by-contract and dependency injection design. - It even seems that the type provided has to be a
DbContext
class: I tried and used theMyEntityRepository
instead, but failed (see details).
I seem lost... Can anyone bring me back on the proper track?
Details:
My first go was:
public class MyEntityService : DataService<MyEntityRepository>
{
...
However, when calling the service, it fails with the following error message:
The server encountered an error processing the request. The exception message is 'On data context type 'MyEntityRepository', there is a top IQueryable property 'MyEntities' whose element type is not an entity type. Make sure that the IQueryable property is of entity type or specify the IgnoreProperties attribute on the data context type to ignore this property.'.
I tried the following steps to fix this, but did not get rid of this error message:
- Adding a
[DataServiceKey("MyEntityId")]
to MyEntity, where MyEntityId is the correct key property of the entity. - Replacing the type of
Repository.MyEntities
byIDbSet
instead ofIQueryable
.
BTW: The following posts are not duplicates:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
为什么要使用存储库?你有上下文,所以使用它。不要仅仅因为想要使用模式就创建洋葱架构。 WCF 数据服务本身已经可以处理您需要的一切。不抱歉,它有时提供更多(例如拦截器)。
通过使用自定义存储库,您将移至反射提供程序数据源。如果您还计划通过 WCF 数据服务修改实体,这也会针对您的存储库,因为反射提供程序是只读的,除非它也实现了
IUpdateable
。另请检查反射提供程序的规则。顺便提一句。 .NET 4 中的 WCF 数据服务不直接支持 DbContext(该支持仅在即将推出的版本的 CTP 中),但您可以解决方法。该链接适用于旧 CTP。在当前版本中没有
UnderlyingContext
属性,但您可以使用IObjectContextAdapter
来获取ObjectContext
。正如您在最后一个提供给服务的链接类型中看到的那样,不需要具有默认构造函数 - 这取决于您在创建数据源时使用的构造函数。如果您需要依赖项注入,您可能必须检查如何直接注入到服务本身的方式(例如 此处 适用于 Unity 和普通 WCF)并在
CreateDataSource
中使用注入的数据。Why do you want to use repository? You have context so use it. Don't create onion architecture just because you want to use pattern. WCF data service already handles everything you need itself. No sorry, it sometimes offers even more (for example interceptors).
By using custom repository you are moving to reflection provider data source. If you also plan to modify your entities through WCF data service that is also against your repository because reflection provider is read only unless it also implements
IUpdateable
. Check also rules for reflection provider.Btw. WCF Data Services in .NET 4 doesn't support DbContext directly (that support is only in CTPs of upcoming version) but you there is workaround for that. The link is for old CTP. In current version there is not
UnderlyingContext
property but you can useIObjectContextAdapter
to getObjectContext
.As you can also see in last link type provided to the service doesn't need to have default constructor - it is up to you what constructor you use when creating data source. If you need dependency injection you will probably have to check the way how to inject directly to the service itself (for example here for Unity and plain WCF) and use injected data in
CreateDataSource
.以下是如何使用您正在使用的任何模式(甚至根本没有模式)创建 WCF 数据服务。
OData 入门第 2 部分:从任何数据源构建 OData 服务
确保您的实体、poco、模型或任何内容具有 public int ID 属性,或者具有由 System.Data.Services 程序集提供的此类注释
System.Data.Services
命名空间:
这将使其能够被 WCF 数据服务识别为实体类型。
正如其他人指出的那样,只需确保在堆栈中添加另一层是一个不错的决定。
Here's how to make a WCF Data Service with whatever pattern you're using, even none at all.
Getting Started With OData Part 2: Building an OData Services from Any Data Source
Just make sure your entity, poco, model or whatever has a property public int ID , or has this class annotation provided by the
System.Data.Services
assembly in theSystem.Data.Services
namespace:
This will make it recognizable as an entity type by the WCF Data Service.
As others have pointed out though, just make sure that adding yet another layer in your stack is a good decision.