Search code examples
c#dynamicdependency-injectionrepository-patternmassive

Dependency Injection with Massive ORM: dynamic trouble


I've started working on an MVC 3 project that needs data from an enormous existing database.

My first idea was to go ahead and use EF 4.1 and create a bunch of POCO's to represent the tables I need, but I'm starting to think the mapping will get overly complicated as I only need some of the columns in some of the tables. (thanks to Steven for the clarification in the comments.

So I thought I'd give Massive ORM a try. I normally use a Unit of Work implementation so I can keep everything nicely decoupled and can use Dependency Injection. This is part of what I have for Massive:

public interface ISession
{
    DynamicModel CreateTable<T>() where T : DynamicModel, new();

    dynamic Single<T>(string where, params object[] args) 
        where T : DynamicModel, new();

    dynamic Single<T>(object key, string columns = "*") 
        where T : DynamicModel, new();

    // Some more methods supported by Massive here
}

And here's my implementation of the above interface:

public class MassiveSession : ISession
{
    public DynamicModel CreateTable<T>() where T : DynamicModel, new()
    {
        return new T();
    }

    public dynamic Single<T>(string where, params object[] args) 
        where T: DynamicModel, new()
    {
        var table = CreateTable<T>();
        return table.Single(where, args);
    }

    public dynamic Single<T>(object key, string columns = "*") 
        where T: DynamicModel, new()
    {
        var table = CreateTable<T>();
        return table.Single(key, columns);
    }
}

The problem comes with the First(), Last() and FindBy() methods. Massive is based around a dynamic object called DynamicModel and doesn't define any of the above method; it handles them through a TryInvokeMethod() implementation overriden from DynamicObject instead:

public override bool TryInvokeMember(InvokeMemberBinder binder, 
    object[] args, out object result) { }

I'm at a loss on how to "interface" those methods in my ISession. How could my ISession provide support for First(), Last() and FindBy()?

Put it another way, how can I use all of Massive's capabilities and still be able to decouple my classes from data access?


Solution

  • The Interface

    Basically you have a couple of options interface wise for the signature for your ISession's Find, Last and FindBy.

    If you want to keep the same syntax with the dynamic argument names First, Last and Find should all be getters and return dynamic with a DynamicObject that Implements bool TryInvoke(InvokeBinder binder, object[] args, out object result) that will give you the same dynamic Find(column:val, otherColum:otherVal) syntax. Here is a rough basic example:

        public class MassiveSession : ISession
    { 
    
        ...
    
        public dynamic Find{
               get {
                   return new DynamicInvoker(this,name:"Find");
               }
        }
    
        public class DynamicInvoker : DynamicObject{
            ...
            public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
            {
                 ...
                 return true;          
            }
    
        }
    }
    

    If you want completely statically defined methods you are just going to have to do a single parameter of IDictionary or something to give you the key pairs.

    Forwarding the call to the Massive dynamic method

    There are two ways to do this as well.

    The easy way is to use the open source framework ImpromptuInterface that allows you to programmatically call dynamic methods just like the c# compiler (including dynamic named parameters).

    var arg = InvokeArg.Create;
    return Impromptu.InvokeMember(table, "Find", arg("column", val),arg("otherColum", otherVal));
    

    Or you can just try to fake the parameters coming into the TryInvokeMember;