3

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:

  1. 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.
  2. It even seems that the type provided has to be a DbContext class: I tried and used the MyEntityRepository 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 by IDbSet instead of IQueryable.

BTW: The following posts are not duplicates:

Community
  • 1
  • 1
chiccodoro
  • 14,407
  • 19
  • 87
  • 130
  • What is MyEntity? Apparently it is not an "entity type". Have you tried using the "IgnoreProperties" attribute? – Fernando Jul 19 '11 at 13:56
  • @Fernando: "MyEntity" is a POCO class which serves as an entity. It is reflected in the database, and EF successfully maps it. The only thing that does not work is the DataService. – chiccodoro Jul 19 '11 at 14:28
  • I think to use DataService<>, MyEntity needs to inherit from Entity. – cadrell0 Jul 19 '11 at 20:48
  • FYI: At least as of WCF Data Services 5.0 and EF 5.0 (4.3 on .NET 4.0) this works out-of-the-box. I'm doing something similar. In fact, my DbContext derivative is really just a locus for the code-first fluently configured model, without any `IQueryable` or `DbSet` properties, and this still works. These upgraded tools are available to the toolsets available when the question was asked, so this would seem to be moot. – Marc L. Oct 12 '13 at 02:56

2 Answers2

3

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 use IObjectContextAdapter to get ObjectContext.

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.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Thank you for your answer! Indeed, I forgot to mention that we use the CTP that supports Code First. Default Constructor: I first didn't have a default constructor, and it complained about that. Why not use the DbContext: For the same reason as not to use DbContext in the MVC controllers: To separate the data access implementation from the web layer. – chiccodoro Jul 20 '11 at 07:57
  • So you will spend a week to create useless abstraction (also called overarchitecting) for WCF Data Service instead of using it as is? Spending week to create something that doesn't have any additional business value and which even doesn't make it more maintainable is a waste. Data service as its name hints is for direct communication with data access layer. – Ladislav Mrnka Jul 20 '11 at 08:12
  • So, if we start implementing some validation, authorization or security-trimming logic, we always have to do it twice? Once for the controllers, once for the service? What's the whole point of using a repository then? – chiccodoro Jul 20 '11 at 08:39
  • Once you start doing it perhaps but your example simply exposes `IQueryable`. The question is if security (= cross cutting) is part of repository? In my opinion it is not. Whole point of repository is abstracting data access - pure data access. – Ladislav Mrnka Jul 20 '11 at 08:50
  • 4
    "You have context so use it. Don't create onion architecture just because you want to use pattern." -but what if I want to expose business logic functions (not directly EF model) and still benefit from WCF data services (OData queries, strongly typed .NET client code, etc.)? – gius Mar 27 '12 at 13:19
  • @Ladislav, reviving an already a bit old thread: "is security (= cross cutting) ... part of repository?" - Say it is not, and we introduce yet another layer such as a `MyEntityManager` that encapsulates the repository plus the logic. Then this simply shifts the question to how to use the `MyEntityManager` in the Data Service, doesn't it? – chiccodoro May 02 '12 at 14:55
  • @Ladislav, very old thread in the meanwhile... But to answer "your example simply exposes `IQueryable`" - that is because it is a simplified example. To make a question short and simple there is no way around simplifying a code sample to the core relevant to the question, and for the question the security trimming part etc. was not relevant. – chiccodoro Nov 26 '14 at 16:11
1

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 the System.Data.Services namespace:

[DataServiceKey("TheNameOfYourPrimaryKeyProperty")]

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.

Irf
  • 4,285
  • 3
  • 36
  • 49
Jerther
  • 5,558
  • 8
  • 40
  • 59