0

I have an issue with the context and availability in my extension methods.

Basically i am using UNITY and currently it provides me a NEW instance of my DBContext everytime i called my data layer (Its injected into the contructor).

I have also placed some extension methods for use with IQueryable that the data layer returns so i can in effect do the following.

var result = dataLayer.GetItems().WithId(3)

The withID is an extension method, i have other extension methods too where i need to do a join on a table because the tables / fields are not in my IQueryable.

Problem with this is that my dbContext is registered to give me a new instance each time so i get an error in form of "Different Contexts .....".

But i should be configuring Unity to provide me with the same instance of the dbcontext each time as the dbcontext should be SHORT LIVED. Of course if i did this i think my problem would be fixed as the Data Layer and extensions methods would be using the same DBContext object.

I have using EF 4.1 with POCO classes, there is no tracking and i have a model. Hence the only way to do joins on a additional tables is to have access to my dbcontext?

Anybody have any suggestions what my options would be ?

Thanks in advance.

Martin
  • 23,844
  • 55
  • 201
  • 327

2 Answers2

2

Frankly, it is a bit smelly to have extension methods that depend on or affect the life cycle of data contexts. Extension methods should do relatively simple tasks without depending on external state and without creating side effects. They fit in a functional programming paradigm. The extension methods of Queryable are a good example. They don't need anything but the supplied parameters, never change global state or even a parameter object and return a new value or object.

If you want to use extension methods this way, you should pass the data context through by parameter.

But I would not use extension methods this way. I would rather have a data layer that has (service) methods that let you specify what you need and that return IEnumerables (note: not IQueryables) and totally encapsulates contexts. But that may be a major departure of your current design. At the very least I would only exend the IQueryables from the data layer by Linq statements. If you need Joins or Includes, let the data layer to apply them according to the data you ask in our methods.

Just an advice :D

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • Great, thanks for the advice. So i should return all the fields using joins in the datalayer and then in the service layer i only need to call my extension method, i don't need to pass in the context as the field i need to do a WHERE on is already included in the fields.. hence no join? This is what you are refering to ? If i am not mistaken... that sounds great. – Martin Jun 12 '12 at 06:55
  • What advantages would i get by returning IEnumerable vs IQueryable? – Martin Jun 12 '12 at 06:55
  • As for IQueryable vs IEnumerable: IQueryable is a "leaky" interface: it exposes implementation details (it matters which linq-to-xxx is behind it). Further, it allows code outside your services to deeply affect queries that are sent to the database. This may affect performance and testability. It is is something you want the data layer to control. Try google and you'll see a lot of discussion on this subject. – Gert Arnold Jun 12 '12 at 07:05
  • As for your first comment: yes, the service will shape the data you need. I'm not sure whether you'd still need to use home-made extension methods. – Gert Arnold Jun 12 '12 at 07:10
  • @Martin Have a look at [this post](http://blog.ploeh.dk/2012/03/26/IQueryableIsTightCoupling.aspx) by Mark Seemann. He discusses why you should be carefull when using `IQueryable` – Sebastian Weber Jun 13 '12 at 12:41
  • Thanks everyone for there comments, been a great help – Martin Jun 14 '12 at 06:23
0

If you have to bridge between lifetimes you can create a wrapper around your context that handles the creation and teardown of your context. Have a look at Mark Seemann's post.

Community
  • 1
  • 1
Sebastian Weber
  • 6,766
  • 2
  • 30
  • 49