无法获得Ninject ConstructorArgument的值(作为参数传入kernel.Get)

时间:2014-03-31 15:50:02

标签: parameters dependency-injection inversion-of-control ninject constructor-injection

无法获取传递给kernel.Get()的ConstructorArgument参数的值。我想使用参数的值来确定将两个字符串值中的哪一个传递给构造函数。事实上,参数在预期的时候就存在了,我只是无法达到它的价值。调用参数的GetValue方法后,我最终得到一个空引用异常:

namespace MyNS.DBProviders {
    public abstract class DBProviderBase {
        private ObjectContext _db;

        public DBProviderBase(ObjectContext db) {
            _db = db;
        }

        ...
        //abstract methods here
        ...
    }

    public class DBProviderB : DBProviderBase {
        public DBProviderB(ObjectContext db) : base(db) {}
        //implementation details... applies to two DBs w/ same schema
    }

    public class DBProviderModule : NinjectModule {
        public override void Load() {
            //Bind DB providers
            Bind<DBProviderBase>().To<DBProviderB>();

            //Bind LINQ contexts to DB providers
            Bind<ObjectContext>().To<EF_DB_b>()
                .When(UseEF_DB_b_conn1).WithConstructorArgument(
                    "connectionString"
                    , ConfigurationManager.ConnectionStrings["EF_DB_b_conn1"]
                        .ToString()
                )
            ;
            Bind<ObjectContext>().To<EF_DB_b>()
                .When(UseEF_DB_b_conn2).WithConstructorArgument(
                    "connectionString"
                    , ConfigurationManager.ConnectionStrings["EF_DB_b_conn2"]
                        .ToString()
                )
            ;
        }

        public static bool UseEF_DB_b_conn1(IRequest req) {
            string idNum = (string)req.Parameters.First(
                p => p.Name == "idNum"
            ).GetValue(null, null);
            //NULL REF EXCEPTION

            return x != null ?  idNum.Substring(0, 1) == "2" : false;
        }

        public static bool UseEF_DB_b_conn2(IRequest req) {
            return !UseEF_DB_b_conn1(req);
        }
    }
}

namespace MyNS {
    public static class DBProviderFactory {

        public static DBProviderBase GetDBProvider(string idNum) {
            var kernel = new Bootstrapper().Kernel;

            return kernel.Get<DBProviderBase>(
                new ConstructorArgument("idNum", idNum)
            );
        }
    }
}

...//idNum received from elsewhere
var db = DBProviderFactory.GetDBProvider(idNum);
var something = db.GetSomethingFromOneOfThreeDBs();

2 个答案:

答案 0 :(得分:2)

我需要使用ToMethod()来访问适当的上下文。

Bind<DBProviderBase>().ToMethod(
    ctx => {
        var idNum = ctx.Parameters.First(p => p.Name == "idNum")
            .GetValue(ctx, null) as string
        ;

        if (idNum == 2) {
            return new DBProviderB(new EF_DBEntities(
                ConfigurationManager.ConnectionStrings["EF_DB_b_conn1"]
            .ToString()));
        } else {
            return new DBProviderB(new EF_DBEntities(
                ConfigurationManager.ConnectionStrings["EF_DB_b_conn2"]
            .ToString()));
        }
    };
);

//later...
var db = kernel.Get<DBProviderBase>(
    new Ninject.Parameters.Parameter("idNum", idNum, true)
);

答案 1 :(得分:1)

编辑:在您的评论和修改之后,我重新制定了答案。以下建议只是解决这个问题的一种方法。我正在实施Provider<ObjectContextFactory>,以便在使用kernel.Get<ObjectContextFactory>()时检查发送给请求的参数。此参数与构造函数无关,它仅用作&#34;上下文变量&#34;所以你可以决定如何处理它。

以下是代码:

public class ObjectContextFactory
{
    private readonly string _idNum;
    private readonly IResolutionRoot _container;

    public ObjectContextFactory(IResolutionRoot container, string idNum)
    {
        _container = container;
        _idNum = idNum;
    }

    public ObjectContext CreateObjectContext()
    {
        //Use _idNum here as you wish, here is a sample implementation
        ConstructorArgument constructorArgument;
        if (_idNum.Substring(0,1) == "2")
        {
            constructorArgument = new ConstructorArgument("connectionString", "your conn string 1");
        }
        else
        {
            constructorArgument = new ConstructorArgument("connectionString", "your conn string 2");
        }

        return _container.Get<ObjectContext>(constructorArgument);
    }
}

public class ObjectContextFactoryProvider : Provider<ObjectContextFactory>
{
    protected override ObjectContextFactory CreateInstance(IContext context)
    {
        var idNum = context.Parameters.FirstOrDefault(p => p.Name == "idNum");
        var idNumValue = idNum.GetValue(context, null) as string;

        var factory = new ObjectContextFactory(context.Kernel, idNumValue);
        return factory;
    }
}

绑定:

public override void Load()
{
    Bind<DBProviderBase>().To<DBProviderB>();
    Bind<ObjectContextFactory>().ToProvider<ObjectContextFactoryProvider>();
}

检索工厂时:

var factory = kernel.Get<ObjectContextFactory>(new Parameter("idNum", "1", true));

实现这一目标的另一种方法是通过Named Bindings。只需从您的idNum到字符串(或直接使用它)进行翻译,并在获取实例时调用它。