I didnt think this would be a problem originally, but as I keep getting exceptions thought I would post here incase im being an idiot...
I have 2 module classes, one sets up NHibernate and one sets up MVC Controllers, now the problem I have is that I have something like below:
public class NHibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionManager>().To<SessionManager>();
}
}
public class ControllerModule : NinjectModule
{
public override void Load()
{
Bind<SomeController>().ToSelf()
.WithConstructorArgument("sessionManager", Kernel.Get<ISessionManager>());
}
}
Whenever I try to use the controller, it just bombs out telling me that its having problems binding the sessionManager argument. I make sure the list has the Nhibernate module in before the Controller module when I create the kernel.
Is there anything immediately stupid in what im doing above?
Assuming:
public class SomeController : Controller
{
private readonly ISessionManager _sessionManager;
public HomeController(ISessionManager sessionManager)
{
_sessionManager = sessionManager;
}
public ActionResult Index()
{
return View();
}
}
The following should be sufficient:
public class NHibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionManager>().To<SessionManager>();
}
}
public class ControllerModule : NinjectModule
{
public override void Load()
{
Bind<SomeController>().ToSelf();
}
}
and in Global.asax:
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new NHibernateModule(),
new ControllerModule()
};
return new StandardKernel(modules);
}
}
Related
I'm pulling a razor view's markup from the database, as detailed in this question:
ASP.NET MVC load Razor view from database
I can pull the view, but it fails on execution because ViewBag is not recognized.
CS0103: The name 'ViewBag' does not exist in the current context
Any suggestions?
Here's the source:
global:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new BearForce.Web.Core.DbPathProvider());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
my path provider:
namespace BearForce.Web.Core
{
public class DbPathProvider : VirtualPathProvider
{
public DbPathProvider()
: base()
{
}
public override bool FileExists(string virtualPath)
{
var repo = new Repository();
var viewPage = repo.GetView(240, virtualPath);
if (base.FileExists(virtualPath))
{
return true;
}
if (viewPage != null)
{
return true;
}
return false;
}
public override VirtualFile GetFile(string virtualPath)
{
if (base.FileExists(virtualPath))
{
return base.GetFile(virtualPath);
}
var repo = new Repository();
var result = repo.GetView(240, virtualPath);
var vf = new DbVirtualFile(virtualPath, result.Markup);
return vf;
}
}
}
my Virtual File:
public class DbVirtualFile : System.Web.Hosting.VirtualFile
{
string _fileContents = string.Empty;
public DbVirtualFile(string path, string fileContents)
: base(path)
{
_fileContents = fileContents;
}
public override System.IO.Stream Open()
{
return new System.IO.MemoryStream(System.Text.ASCIIEncoding.ASCII.GetBytes(_fileContents));
}
}
My Controller:
public ActionResult Index()
{
ViewBag.Title = "aaah!!! Muppets!!! Help!!!!!";
return View();
}
Obviously, this is a proof of concept, so the names are all silly and the code sloppy as hell...
For future people who get this error, you can get this exact error if your web.config files are missing from your Views and your root project folder.
You should make sure that the view you are returning corresponds to a razor view. Here's a simplified working example:
public class CustomPathProvider : VirtualPathProvider
{
private class CustomVirtualFile : VirtualFile
{
public CustomVirtualFile(string path)
: base(path)
{ }
public override Stream Open()
{
return new MemoryStream(Encoding.UTF8.GetBytes("Hello #ViewBag.Name"));
}
}
public override bool FileExists(string virtualPath)
{
// This is very important: make sure that here you
// are returning true only for Razor view pages or
// you won't have ViewBag.
// In this oversimplified example we support
// the index view for the home controller
return virtualPath == "/Views/Home/Index.cshtml";
}
public override VirtualFile GetFile(string virtualPath)
{
return new CustomVirtualFile(virtualPath);
}
}
which would be registered in Application_Start:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
HostingEnvironment.RegisterVirtualPathProvider(new CustomPathProvider());
}
and finally a sample controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Name = "John";
return View();
}
}
And a final very important remark if you are implementing a custom VirtualPathProvider:
This doesn't work if your web application is precompiled. So if you are using precompilation (things like Publish... or Web Deployment Projects) your custom virtual path provider will never be used.
I must be doing something wrong because I have replicated many answers on this subject. My attribute binding is not being hit and I'm not sure why.
Controller.cs
[NatGeoUserAccessAuthorization]
[HttpGet]
public virtual ActionResult Teacher(string id)
{
Attribute/Fitler
public class NatGeoUserAccessAuthorizationAttribute : FilterAttribute{}
public class NatGeoUserAccessAuthorizationFilter : IAuthorizationFilter
{
private readonly IUsersService _usersService;
public NatGeoUserAccessAuthorizationFilter(IUsersService usersService)
{
_usersService = usersService;
}
public string QueryStringName { get; set; }
#region Implementation of IAuthorizationFilter
public void OnAuthorization(AuthorizationContext filterContext)
{
if (!_usersService.HasUserAccess(filterContext.HttpContext.User.Identity.Name, filterContext.HttpContext.Request.QueryString[QueryStringName ?? "id"]))
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "action", "AccessDenied" }, { "controller", "Error" } });
}
}
#endregion
}
Global.asax
protected void Application_Start()
{
// NLog Custom Layouts
ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("utc_date", typeof(UtcDateRenderer));
ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("web_variables", typeof(WebVariablesRenderer));
// Setup IoC Container
DependencyResolver.SetResolver(Bootstrap.Configure((kernel) =>
{
kernel.Bind<IDatabaseFactory>().To<DatabaseFactory<MySqlConnection>>().InRequestScope().WithConstructorArgument("connectionString", Config.Data.MySQLConnection);
ManagerBindings.Register(kernel);
ProviderBindings.Register(kernel);
RepositoryBindings.Register(kernel);
ServiceBindings.Register(kernel);
ValidationBindings.Register(kernel);
kernel.BindFilter<NatGeoUserAccessAuthorizationFilter>(FilterScope.Action, 0).WhenActionMethodHas<NatGeoUserAccessAuthorizationAttribute>();
kernel.Bind<IUserProfile>().To<UserProfile>();
kernel.Inject(Roles.Provider);
}));
// Custom Default Model Binder
ModelBinders.Binders.DefaultBinder = new ValidationModelBinder();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Bootstrap.cs
public class Bootstrap
{
public delegate void IocRegistrationDelegate(IKernel kernel);
public static IDependencyResolver Configure(IocRegistrationDelegate serviceBindings)
{
var kernel = new StandardKernel();
serviceBindings.Invoke(kernel);
return new NinjectDependencyResolver(kernel);
}
}
You are using an own bootstrapping mechanism that doesn't support filter bindings. Read the docu how to use the one that comes with Ninject.Mvc3 on http://github.com/ninject/ninject.web.mvc/wiki
Controller:
public class HomeController : Controller
{
IEmployeeTask _employeeTask;
public HomeController()
{
_employeeTask = new UnityContainer().Resolve<IEmployeeTask>();
}
public HomeController(IEmployeeTask employeeTask)
{
_employeeTask = employeeTask;
}
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult GetAndDisplayEmployee()
{
return View();
}
[HttpPost]
public ActionResult GetAndDisplayEmployee(int empid)
{
return View(_employeeTask.GetEmployeeModelFromService(empid));
}
}
Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
var container = new UnityContainer();
container.RegisterType<IEmployeeModelMap, EmployeeModelMap>();
container.RegisterType<IEmployeeService, EmployeeService>();
container.RegisterType<IEmployeeTask, EmployeeTask>();
container.RegisterType<IEmployee, Employee>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
Unity Dependency Resolver:
public class UnityDependencyResolver : IDependencyResolver
{
readonly IUnityContainer _container;
public UnityDependencyResolver(IUnityContainer container)
{
this._container = container;
}
public object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType);
}
catch
{
return new List<object>();
}
}
}
Employee Task class:
public class EmployeeTask: IEmployeeTask
{
IEmployeeService _employeeService;
IEmployeeModelMap _employeeModelMap;
public EmployeeTask(IEmployeeService employeeService, IEmployeeModelMap employeeModelMap)
{
_employeeService = employeeService;
_employeeModelMap = employeeModelMap;
}
public EmployeeViewModel GetEmployeeModelFromService(int empId)
{
return _employeeModelMap.ToModel(_employeeService.GetEmployeeFromEntities(empId));
}
}
Now, i have used unity 2.0 application block. Tried to resolve the dependency of the controller using unity with out using Custom Controller factory.
I am stuck with below error.
The current type, DataProviderInfrastructure.IEmployeeTask, is an
interface and cannot be constructed. Are you missing a type mapping?
I don't use Unity, but my guess would be that it attempts to use your parameterless constructor where you try to resolve the dependency. Creating this constructor is very very wrong. First - it defeats the purpose of having dependency injection and second - you create new container inside - which will be empty so it obviously can't resolve any dependencies. Try removing this from your code:
public HomeController()
{
_employeeTask = new UnityContainer().Resolve<IEmployeeTask>();
}
I just start using Ninject with MVC3 so here is my problem:
- I installed Ninject 2.2.1.4 and Ninject.MVC3 2.2.2.0 from Nuget
- In my WebUI (MVC3 project):
Global.asax.cs
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "home", action = "index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
return kernel;
}
}
In my Domain (class project), i have my LinQ to SQL datacontext, i want to load the context with the connection string from my Web.Config in my WebUI, so i have to pass the constructor parameter, i also have some services in my Domain project
public class LotteryDataService
{
LinQ.WebDataContext _context;
public LotteryDataService(LinQ.WebDataContext context)
{
_context = context;
}
public IEnumerable<LinQ.LotteryData> Get()
{
return _context.LotteryDatas.Take(10);
}
}
How to bind the datacontext with Ninject with constructor parameter (here is connection string)?
This is how you pass a constructor parameter. Ninject will resolve the constructor that matches the specified constructor arguments.
public class DataModule : NinjectModule
{
public override void Load()
{
string connectionString = "...";
Bind<WebDataContext>().ToSelf()
.WithConstructorArgument("connection", connectionString);
}
}
The first argument to .WithConstructorArgument() should be the name of the constructor parameter. This is fileOrServerOrConnection in the base class, but connection in derived class.
Below code snap might be helpful. Hope it will serve greater flexibility!
public class MvcModule : NinjectModule
{
//Bind the default connection string
public void BindDataContext()
{
ConstructorArgument parameter = new ConstructorArgument("connectionString", "[Config Value]");
Bind<DataContext>().ToSelf().InRequestScope().WithParameter(parameter);
}
public override void Load()
{
BindDataContext();
Bind(typeof(IRepository<>)).To(typeof(EntityRepository<>)).InRequestScope();
........
}
//Re-Bind the connection string (in case of multi-tenant architecture)
public void ReBindDataContext(string cn)
{
ConstructorArgument parameter = new ConstructorArgument("connectionString", cn);
Rebind<DataContext>().ToSelf().InRequestScope().WithParameter(parameter);
}
//Re-Bind the connection string (in case of multi-tenant architecture)
public static void ReBindDataContext(IKernal kernel,string cn)
{
IEnumerable<INinjectModule> ml = kernel.GetModules();
var myModule = ml.Where(i => i.Name.ToLowerInvariant().Contains("mvcmodule")).Select(i => i).Take(1);
MvcModule mm = myModule.ToList()[0] as MvcModule ;
mm.ReBindDataContext(cn);
}
//Return the module, for further modification like connection string
public static MvcModule GetModule(IKernal kernel)
{
IEnumerable<INinjectModule> ml = kernel.GetModules();
var myModule = ml.Where(i => i.Name.ToLowerInvariant().Contains("mvcmodule")).Select(i => i).Take(1);
MvcModule mm = myModule.ToList()[0] as MvcModule ;
return mm;
}
}
Having the code below in my Global.asax.cs and two controller (one based on a the other: MasterController) I don't seem to find how can I resolve the repository register in my WindsorContainer from the MasterController... the same applies in the HomeController and works perfectly... what am I doing wrong?
Global.asax.cs:
private IWindsorContainer _container;
protected void Application_Start()
{
InitializeContainer();
RegisterRoutes(RouteTable.Routes);
}
protected void Application_End()
{
this._container.Dispose();
}
protected void Application_EndRequest()
{
if (_container != null)
{
var contextManager = _container.Resolve<IContextManager>();
contextManager.CleanupCurrent();
}
}
private void InitializeContainer()
{
_container = new WindsorContainer();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(_container));
// Register context manager.
_container.Register(
Component.For<IContextManager>()
.ImplementedBy<EFContextManager>()
.LifeStyle.Singleton
.Parameters(
Parameter.ForKey("connectionString").Eq(ConfigurationManager.ConnectionStrings["ProvidersConnection"].ConnectionString)
)
);
//Products repository
_container.Register(
Component.For<IProductRepository>()
.ImplementedBy<ProductRepository>()
.LifeStyle.Singleton
);
// Register all MVC controllers
_container.Register(AllTypes.Of<IController>()
.FromAssembly(Assembly.GetExecutingAssembly())
.Configure(c => c.LifeStyle.Transient)
);
}
Controller base:
public class MasterController : Controller
{
private IProductRepository _productRepository;
public ProductController(IProductRepository product)
{
_productRepository = product;
}
public ActionResult Index()
{
ViewData["product"] = _productRepository.FindOne(123);
return View();
}
}
Controller based on MasterController:
public class ProductController : MasterController
{
private IProductRepository _productRepository;
public ProductController(IProductRepository product)
{
_productRepository = product;
}
public ActionResult Search(int id)
{
ViewData["product"] = _productRepository.FindOne(id);
return View();
}
}
It is working as expected now and the ViewDatas are accessible from any controller/view.
First I created a public class where I store my Windsor container so it can be accessed from any controller:
public static class IOCcontainer
{
public static IWindsorContainer Container { get; set; }
}
Then in my global.asax.cs I have:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
InitializeContainer();
}
private void InitializeContainer()
{
_container = new WindsorContainer();
// Register context manager.
_container.Register(
Component.For<IContextManager>()
.ImplementedBy<EFContextManager>()
.LifeStyle.Singleton
.Parameters(
Parameter.ForKey("connectionString").Eq(ConfigurationManager.ConnectionStrings["ProvidersConnection"].ConnectionString)
)
);
//Products repository
_container.Register(
Component.For<IProductRepository>()
.ImplementedBy<ProductRepository>()
.LifeStyle.Singleton
);
// Register all MVC controllers
_container.Register(AllTypes.Of<IController>()
.FromAssembly(Assembly.GetExecutingAssembly())
.Configure(c => c.LifeStyle.Transient)
);
IOCcontainer.Container = _container; //set the container class with all the registrations
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(_container));
}
So now in my master controller I can use:
public class MasterController : Controller
{
private IProductRepository g_productRepository;
public MasterController() : this(null,null,null,null,null)
{
}
public MasterController(IProductRepository productRepository)
{
g_productRepository = productRepository ?? IOCcontainer.Container.Resolve<IProductRepository>();
}
//I don't use an action here, this will make execute it for any action in any controller
protected override void OnActionExecuting(ActionExecutingContext context)
{
if (!(context.ActionDescriptor.ActionName.Equals("Index") && context.Controller.ToString().IndexOf("Home")>0)) {
//I now can use this viewdata to populate a dropdownlist along the whole application
ViewData["products"] = g_productRepository.GetProducts().ToList().SelectFromList(x => x.Id.ToString(), y => y.End.ToShortDateString());
}
}
}
Then the rest of controllers:
//will be based on MasterController
public class AboutController : MasterController
{
}
public ActionResult Index()
{
return View();
}
etc...
Probably not the most elegant way to do it but it will do until I find a better way or someone else brighten my mind up!