views:

710

answers:

3

I'm really stuck here.

I have a asp.net mvc application and use StructureMap 2.5.3 (SM) to inject service and repository classes in my controllers. All controller are made by a SM factory.

I also have a Linq to SQL datacontext which I wanted to cache by hybrid.

public class DBRegistry:Registry
{
    public  DBRegistry()
    {
        ForRequestedType<SharpShopDataContext>()
            .CacheBy(StructureMap.Attributes.InstanceScope.Hybrid)
            .TheDefault.IsThis(new SharpShopDataContext());
    }
}

The caching doesn't seem to work and I get issues with the datacontext because of it.

Multiple browser request all return the same dbcontext?! In one of my repository classes I've put this code. Debug.WriteLine("db hashcode: " + db.GetHashCode()+ " "+ DateTime.Now.ToString());

where db=the datacontext I also print the hashcodes of the repository using the db and the service using the repository, here is a print of multiple requests:

service hashcode: 6238972 26-3-2009 18:59:34

repository hashcode: 21756593 26-3-2009 18:59:34

db hashcode: 7043935 26-3-2009 18:59:34

service hashcode: 59389065 26-3-2009 18:59:34

repository hashcode: 8331620 26-3-2009 18:59:34

db hashcode: 7043935 26-3-2009 18:59:34

service hashcode: 11291358 26-3-2009 18:59:38

repository hashcode: 13848497 26-3-2009 18:59:38

db hashcode: 7043935 26-3-2009 18:59:38

service hashcode: 42509361 26-3-2009 18:59:38

repository hashcode: 56101068 26-3-2009 18:59:38

db hashcode: 7043935 26-3-2009 18:59:38

as you can see 7043935 is the hashcode for the datacontext every time for each request, but the service and repository get a new instance and hashcode every time.

I get weird errors because of this, for example a dataconcurrency exception because dbcontext has an original value from 4 webrequests ago, while the database was changed by another source.

A: 

i've got a workaround, but not to happy about it because know I have to change a lot of code, and I discovered it trough trial and error.

I now use:

            ForRequestedType<ISharpShopDataContextWrapper>()
            .CacheBy(StructureMap.Attributes.InstanceScope.Hybrid)
            .TheDefaultIsConcreteType<SharpShopDataContextWrapper>();

The injection/caching for interfaces seems to works. Here is the implementation of the interface + concrete wrapper.

So it really seems like a bug in SM, or am I missing something?

public interface ISharpShopDataContextWrapper
{
    SharpShopDataContext DataContext
    {
        get;
    }
}

public class SharpShopDataContextWrapper : ISharpShopDataContextWrapper
{
    SharpShopDataContext db;

    public SharpShopDataContextWrapper()
    {
        db = new SharpShopDataContext();
    }
    public SharpShopDataContext DataContext
    {
        get { return db; }
    }
}
+2  A: 

This is definitely a problem line:

.TheDefault.IsThis

You are specifying a specific instance, this is what causes it to return the same even if specifying PerRequest. Notice you changed to (in your workaround):

.TheDefaultIsConcreteType<SharpShopDataContextWrapper>();

Ps. I haven't used HybridScope, but I have a production app that uses the default instance scope (PerRequest) and it definitely gives a new one each time it passes a datacontext. If you want to control specifically how it is instantiated, try one of the methods that accept an expression, that way you send:

() => new MyDataContext()
eglasius
+2  A: 

If you are trying to get SM to create a single DataContext per HttpRequest than your new Registry configuration should work

ForRequestedType<ISharpShopDataContextWrapper>()
            .CacheBy(StructureMap.Attributes.InstanceScope.Hybrid)
            .TheDefaultIsConcreteType<SharpShopDataContextWrapper>();

InstanceScope.Hybrid is the SM ( v2.5.3 ) enum value you should be using if you want a "once per thread or ASP.NET request" lifecycle (lets go to the source Chad Myers, SM contributor)

If you configure your application infrastructure properly you shouldn't "have to change a lot of code". I'd be interesting to know why you decided to create a wrapper class for your SharpShopDataContext? The LinqToSql DataContext is declared as a partial class so you could easily create a SharpShopDataContext partial class that implements any additional interfaces:

LinqToSql generated partial class definition

public partial class SharpShopDataContext: System.Data.Linq.DataContext{
    /*Linq2Sql gen here*/
}

Your partial class definition

public partial class SharpShopDataContext: ISharpShopDataContext{
   /*your implementation here*/
}

You might consider reading some of Jeremy Miller's StructureMap articles. Once you have a good understanding of its capabilities you'll probably refactor your entire MVC application. I know my current base MVC app framework is highly configurable/testable because of SM IoC (and boat loads of trial/error/smell/refactor ).

njappboy