Ninject InThreadScope Binding
Asked Answered
I

1

6

I have a Windows service that contains a file watcher that raises events when a file arrives. When an event is raised I will be using Ninject to create business layer objects that inside of them have a reference to an Entity Framework context which is also injected via Ninject. In my web applications I always used InRequestScope for the context, that way within one request all business layer objects work with the same Entity Framework context. In my current Windows service scenario, would it be sufficient to switch the Entity Framework context binding to a InThreadScope binding?

In theory when an event handler in the service triggers it's executed under some thread, then if another file arrives simultaneously it will be executing under a different thread. Therefore both events will not be sharing an Entity Framework context, in essence just like two different http requests on the web.

One thing that bothers me is the destruction of these thread scoped objects, when you look at the Ninject wiki:

.InThreadScope() - One instance of the type will be created per thread.
.InRequestScope() - One instance of the type will be created per web request, and will be destroyed when the request ends.

Based on this I understand that InRequestScope objects will be destroyed (garbage collected?) when (or at some point after) the request ends. This says nothing however on how InThreadScope objects are destroyed. To get back to my example, when the file watcher event handler method is completed, the thread goes away (back to the thread pool?) what happens to the InThreadScope-d objects that were injected?

EDIT: One thing is clear now, that when using InThreadScope() it will not destroy your object when the handler for the filewatcher exits. I was able to reproduce this by dropping many files in the folder and eventually I got the same thread id which resulted in the same exact Entity Framework context as before, so it's definitely not sufficient for my applications. In this case a file that came in 5 minutes later could be using a stale context that was assigned to the same thread before.

Immixture answered 23/12, 2010 at 13:24 Comment(0)
J
4

Objects that are thread-static could possibly live for a very long time, which means that at some time that ObjectContext will get stale and work with old (cached) values, which will result in hard-to-find bugs.

I normally create an ObjectContext with the same scope as I create a database transaction (I often even wrap a ObjectContext in a database transaction and dispose them right after each other). One (web) request could possibly have multiple database transactions, but will usually have one 'business transaction', which executes the business logic. Other transactions could be started for things as logging (before, after, and sometimes during the business transaction). When you reuse the ObjectContext for the complete request you could end up with a mess, because when a business transaction fails, the ObjectContext could be in an invalid state, which might have effect on operations (such as logging) that reuse that same ObjectContext.

With your Windows Service, I think every event raised by the file watcher possibly triggers a new business transaction. In that case I would create a new ObjectContext per event.

Long story short, I would not inject an ObjectContext that has a lifetime that is managed by your IoC framework. I would inject a factory that allows your code to create a new ObjectContext and dispose it. I just answered another question about this a few ours back. Look at the solution I propose in that answer.

Good luck.

Jeopardize answered 23/12, 2010 at 13:49 Comment(2)
In regard to http request scoped stuff since EF implements a UnitOfWork type patterns my SaveChanges call is basically does the commit of a business level transaction as you described. I see your proposed solution but I believe it would require my windows service to also have a reference to my data access layer. One benefit of using Ninject to provide me my business layer objects is it also takes care of injection downstream, so I request a business layer object but it really injects 3 levels ProductManager-hasa-ProductRepository-hasa-EFContext.Immixture
@e36M3: Your argument doesn't really change that you can use a factory. You don't need a dependency on your data access technology if you don't want to. Take a look at this article to see how to abstract your LINQ provider: cuttingedge.it/blogs/steven/pivot/entry.php?id=84.Jeopardize

© 2022 - 2024 — McMap. All rights reserved.