Unity how to pass Request in Controller's constructor from Unity - asp.net-mvc

The old controller code with Concrete dependencies:
public SomeController: Controller
{
public SomeController()
{
}
public ActionResult Default()
{
**Something something = new Something(Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString());**
something.SomeMethod();
}
}
The new Controller code with TDD focus:
public SomeControllerNew: Controller
{
private readonly ISomething _something;
public SomeControllerNew(ISomething something)
{
_something = something;
}
public ActionResult Default()
{
_something.SomeMethod();
}
}
PROBLEM:
Now in new TDD approach i need to invoke constructor where I am registering the Interface. I have put it in UnityBootstraper common file, Something like:
var container = new UnityContainer();
container.RegisterType();
**Something something = new Something(Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString());**
something.SomeMethod();
This is not working here. Error is quite clear:
Object reference required for non-static field, method, property 'System.Web.Mvc.Controller.Request.get'.
I can't figure out how i can access http request here in UnityBootstrapper?
Edit:
Trying to do all this in RegisterRoutes.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
DependencyResolver.SetResolver(new Unity.Mvc3.UnityDependencyResolver(UnityBootstrapper.Initialise()));
var container = new UnityContainer();
container.RegisterType<ISometing, Something>();
}
}

One way to do it is to create an abstract factory like this:
public interface ISomethingFactory
{
ISomething Create(string url);
}
public class SomethingFactory : ISomethingFactory
{
public ISomething Create(string url)
{
return new Something(url);
}
}
And make your controller depend on it like this:
public class SomeControllerNew: Controller
{
private readonly ISomething _something;
public SomeControllerNew(ISomethingFactory somethingFactory)
{
_something = somethingFactory.Create(Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString();
}
public ActionResult Default()
{
_something.SomeMethod();
}
}
A better approach (IMO) is to use a custom Controller Factory instead of using the Dependency Resolver like this:
public class CustomFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var request = requestContext.HttpContext.Request; //Here we have access to the request
if (controllerName == "Some") //Name of controller
{
//Use the container to resolve and return the controller.
//When you resolve, you can use ParameterOverride to specify the value of the string dependency that you need to inject into Something
}
return base.CreateController(requestContext, controllerName);
}
}
This way you don't have to introduce the ISomethingFactory, and your controller would still depend on ISomething directly.
You would need to tell the MVC framework about this custom controller factory like this (in Application_Start):
ControllerBuilder.Current.SetControllerFactory(new CustomFactory());

Related

Xamarin Android: how to implement a ViewModelProvider factory?

I'm trying to create a viewmodel provider factory and I'm little bit lost. I've already added the required Nuget packages and my view models extend the AndroidViewModel type. Now, I'd like to create a factory that would use autofac to create the required view models from the OnCreate activitie's method. The creation call looks like this:
_viewModel = (ViewModelProviders.Of(this, _viewModelFactory)
.Get(Java.Lang.Class.FromType(typeof(MainActivityViewModel))) as JavaObjectWrapper<MainActivityViewModel>)
.Object;
Now, the factory:
public class ViewModelFactory : ViewModelProvider.AndroidViewModelFactory {
public ViewModelFactory(Application application) : base(application) {
}
public override Object Create(Class modelClass) {
// TODO: any way to get the .NET type that was passed here?
return base.Create(modelClass);
}
}
Can I retrieve the .NET type (MainActivityViewModel) from the Class instance that is passed into the Create method call (the type would be required to resolve it from the autofac container)? If there is, how can I do that?
Thanks.
This is how I do this with Unity, but this pattern can be used for passing anything through the ViewModel constructor:
The ViewModel itself
public class HomeViewModel : ViewModel
{
IUnityContainer _unityContainer;
public HomeViewModel(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
}
The HomeViewModelFactory (Default constructor required)
public class HomeViewModelFactory : Java.Lang.Object, ViewModelProvider.IFactory
{
IUnityContainer _unityContainer;
public HomeViewModelFactory()
{
}
public HomeViewModelFactory(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public Java.Lang.Object Create(Class p0)
{
return _unityContainer.Resolve<HomeViewModel>();
}
}
Usage in Fragment
public override void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
var homeViewModelFactory = _unityContainer.Resolve<HomeViewModelFactory>();
_homeViewModel = ViewModelProviders.Of(this, homeViewModelFactory).Get(Java.Lang.Class.FromType(typeof(HomeViewModel))) as HomeViewModel;
}

MVC 3 + IoC + NInject + Repositories + LINQ

I'm trying to work with NInject in my MVC 3 application, and i have one question.
Interface
public interface ITalesRepository
{
IEnumerable<Tale> GetAllTales();
}
Repository
public class TalesRepository : ITalesRepository
{
private FairyTalesMVC3DataContext _dataContext;
public TalesRepository(FairyTalesMVC3DataContext dataContext)
{
_dataContext = dataContext;
}
public IEnumerable<Tale> GetAllTales()
{
return _dataContext.Tales.OrderBy(c => c.NameAn);
}
}
Home controller
public class HomeController : Controller
{
private readonly ITalesRepository _talesRepository;
public HomeController(ITalesRepository talesRepository)
{
_talesRepository = talesRepository;
}
public ActionResult Index()
{
ViewBag.Tales = _talesRepository.GetAllTales();
return View();
}
}
So, i need to initialize my TalesRepository with DataContext, and now it is so:
private void RegisterDependencyResolver()
{
var kernel = new StandardKernel();
kernel.Bind<ITalesRepository>().To<TalesRepository>().WithConstructorArgument("dataContext", new FairyTalesMVC3DataContext(ConfigurationManager.ConnectionStrings["dbFairyTalesConnectionString"].ConnectionString));
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
}
So, my question, is it ok or something wrong?
First of all:
public IEnumerable<Tale> GetAllTales()
{
return _dataContext.Tales.OrderBy(c => c.NameAn);
}
I would add .ToList() to the end. Else you'll get data layer exceptions in your presentation layer which is not fine.
Next, I would recommend that you switch to ViewModels instead of using ViewBag. It's a lot easier to prevent that logic leaks into the views if you are using ViewModels. Since you can add the logic to the ViewModel and thus get the same behaviour in all views using the model.
Your application should inherit from NinjectHttpApplication. It registers dependency resolver, so you don't have to do it.
You should also override CreateKernel in application class and register your own module with bindings:
public class MvcApplication : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
return new StandardKernel(new INinjectModule[] {new MvcModule()});
}
}
public class MvcModule : NinjectModule
{
public override void Load()
{
Bind<ITalesRepository>().To<TalesRepository>();
Bind<FairyTalesMVC3DataContext>().To<FairyTalesMVC3DataContext>().InRequestScope();
}
}

Windsor container components not available on first controller action

I'm using a configuration within the global.asax.cs to register the components but it looks the container hasn't been initialized yet at the first http request (HomeController > Index action) and it gives me a "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection." error.
I can't find a solution for this and is driving me mad!
Extract of my global.asax.cs:
protected void Application_Start()
{
InitializeContainer();
InitializeDatabase();
RegisterRoutes(RouteTable.Routes);
}
private void InitializeContainer()
{
_container = new WindsorContainer();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(_container));
// Register context manager.
_container.Register(
Component.For<IContextManager>()
.ImplementedBy<CoursesContextManager>()
.LifeStyle.Singleton
.Parameters(
Parameter.ForKey("connectionString").Eq(ConfigurationManager.ConnectionStrings["CoursesConnection"].ConnectionString)
)
);
// Register specifc repository implementations (can we do this more generic?)
_container.Register(
Component.For<ICourseRepository>()
.ImplementedBy<CourseRepository>()
.LifeStyle.Singleton
);
[...other interfaces and controllers registered...]
}
Controller where the exception is thrown at first http request:
public class HomeController : Controller
{
private ICourseRepository _courseRepository;
public HomeController(ICourseRepository courseRepository)
{
_courseRepository = courseRepository;
}
public ActionResult Index()
{
var courses = _courseRepository.Find(); //here is where it fails
return View(courses);
}
}
Repository/interfaces:
Generic interface:
public interface IRepository<T>
{
IQueryable<T> Find();
}
Generic repository:
public class MyRepository<T> : IRepository<T> where T : class
{
private IContextManager _contextManager;
private string _qualifiedEntitySetName;
private string _keyName;
protected ObjectContext CurrentObjectContext
{
get { return _contextManager.GetContext(); }
}
protected ObjectSet<T> ObjectSet
{
get { return CurrentObjectContext.CreateObjectSet<T>(); }
}
public MyRepository(IContextManager contextManager)
{
this._contextManager = contextManager;
this._qualifiedEntitySetName = string.Format("{0}.{1}"
, this.ObjectSet.EntitySet.EntityContainer.Name
, this.ObjectSet.EntitySet.Name);
this._keyName = this.ObjectSet.EntitySet.ElementType.KeyMembers.Single().Name;
}
public IQueryable<T> Find()
{
return ObjectSet;
}
}
Interface course based on generic repository:
public interface ICourseRepository : IRepository<Course>
{
}
if you use Unit Of Work pattern you will solve your problem
Check this post Unit Of Work Pattern, is very usefull
I found a way to handle with this at least momentarily. Because the problem happens on the first request, I've just added another action in my controller and redirect the index action to it. Probably not the best solution but can't spend more time on this issue!
public class HomeController : Controller
{
private ICourseRepository _courseRepository;
public HomeController(ICourseRepository courseRepository)
{
_courseRepository = courseRepository;
}
public ActionResult Index() // Default action in the controller, first hit
{
return RedirectToAction("Home");
}
public ActionResult Home() //The repository is available here, no exception thrown
{
var courses = _courseRepository.Find(); //here is where it fails
return View(courses);
}
}

Ninject dependency injection into ASP.NET MVC controller where repository type is known only at runtime

I've set up DI with Ninject in my ASP.NET MVC application like this
Bind<IRepository>().To<XmlDefaultRepository>().WhenInjectedInto(typeof(PageController)).WithConstructorArgument("contentType", ContentType.Page);
Bind<IRepository>().To<XmlDefaultRepository>().WhenInjectedInto(typeof(WidgetController)).WithConstructorArgument("contentType", ContentType.Page);
Bind<IRepository>().To<XmlDefaultRepository>().WhenInjectedInto(typeof(SectionController)).WithConstructorArgument("contentType", ContentType.Section);
Bind<IRepository>().To<XmlDefaultRepository>().WhenInjectedInto(typeof(WidgetZoneController)).WithConstructorArgument("contentType", ContentType.WidgetZone);
XmlDefaultRepository implements IRepository and it contains a constructor that takes contentType parameter which I use for persistance path generation.
Now I have a ServicesController which does not have a default type by itself - it is just a controller that provides JSON data (JSON actions) to consumers from jQuery.
This is how it looks now:
public class ServicesController : ContentController
{
public ActionResult ContentSlugs(string contentType, string q, int limit)
{
IRepository _repository = new XmlDefaultRepository(contentType); // this instantiation depends on contentType provided by JSON GET request at runtime and I want to somehow replace it with DI
return Json(_repository.GetSlugsForContentType(limit, q), JsonRequestBehavior.AllowGet);
}
}
And this is how other controllers look like (DI is done thru constructor injection here):
public class SectionController : ContentController
{
private IRepository ContentRepository;
public SectionController(IRepository repository)
{
ContentRepository = repository;
}
}
How can I get rid of "new XmlDefaultRepository(contentType)" dependency in ServicesController?
I've solved this problem by implementing method
_repository.GetSlugsForContentType(contentType, limit, q)
and creating a parameterless default constructor on XmlDefaultRepository implementation of IRepository.
This is the DI way of solving this issue with a Repository factory.
// ServicesController.cs
// ninject factory example, see IRepositoryFactory interface and its XmlRepositoryFactory implementation
[Inject]
public IRepositoryFactory RepositoryFactory
{
set
{
factory = value;
}
}
private IRepositoryFactory factory;
public ActionResult ContentSlugsThruFactory(string contentType, string q, int limit)
{
IRepository _repository = factory.Create(contentType);
return Json(_repository.GetSlugsForContentType(limit, q), JsonRequestBehavior.AllowGet);
}
// IRepositoryFactory.cs
public interface IRepositoryFactory
{
IRepository Create(string contentType);
}
// XmlRepositoryFactory.cs
public class XmlRepositoryFactory : IRepositoryFactory
{
public IRepository Create(string contentType)
{
return XmlDefaultRepository.Create(contentType);
}
}
// XmlDefaultRepository.cs
public static XmlDefaultRepository Create(ContentType contentType)
{
return new XmlDefaultRepository(contentType);
}
public static XmlDefaultRepository Create(string contentType)
{
return new XmlDefaultRepository(contentType);
}
// global.asax.cs
Bind<IRepositoryFactory>().To<XmlRepositoryFactory>().InSingletonScope();

Ninject And Connection Strings

I am very new to Ninject and am trying Ninject 2 with MVC and Linq. I have a SqlProductRepository class and all I want to know is what's the best way of passing the connectionstring in the constructor if I am injecting the Repository object in the controller.
public class SqlProductRepository:IProductRepository
{
private Table<Product> productsTable;
public SqlProductRepository(string connectionString)
{
productsTable = (new DataContext(connectionString)).GetTable<Product>();
}
public IQueryable<Product> Products
{
get { return productsTable; }
}
}
This is my ProductController class where I am injecting the Repository:
public class ProductsController : Controller
{
private int pageSize = 4;
public int PageSize { get { return pageSize; } set { pageSize = value; } }
IProductRepository _productsRepository;
[Inject]
public ProductsController(IProductRepository productRepository)
{
_productsRepository = productRepository;
}
public ViewResult List(int page)
{
return View(_productsRepository.Products
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList()
);
}
}
Can somebody please guide me regarding this?
You can set it up in your binding
_kernel.Bind<IProductRepository>()
.To<SqlProductRepository>()
.WithConstructorArgument("connectionString",yourConnectionString );
You're doing:
new DataContext(connectionString)
in your code - this is the very newing and binding to classes you're trying to push out of your code by using a DI container. At the very least, consider adding an IConnectionStringSelector interface or something like that. You dont want to have 20 Bind calls for 20 repositories - you want a higher level abstraction than that.
I'd suggest the best solution is that you should be demanding either an IDataContext or an IDataContextFactory in the constructor instead and letting that worry about it.
You could supply the connection string as a constructor argument when binding the SqlProductRepository to the IProductRepository interface.
public class LinqToSqlModule : NinjectModule
{
public override void Load()
{
Bind<IProductRepository>().To<SqlProductRepository>()
.WithConstructorArgument(connectionString, "connectionstring");
}
}
I would suggest a slightly different approach. First of all, you might want to create a binding for the DataContext class in the kernel. You could do so by using a provider class to create your DataContext passing the connection string as an argument to its constructor. Then you bind the DataContext to the DataContextProvider.
public class DataContextProvider : Provider<DataContext>
{
protected override DataContext CreateInstance(IContext context)
{
string connectionString = "connectionstring";
return new DataContext(connectionString);
}
}
public class LinqToSqlModule : NinjectModule
{
public override void Load()
{
Bind<DataContext>().ToProvider<DataContextProvider>();
Bind<IProductRepository>().To<SqlProductRepository>();
}
}
Next modify the constructor of SqlProductRepository class to accept a DataContext object instead.
public class SqlProductRepository : IProductRepository
{
private readonly DataContext context;
public ProductRepository(DataContext context)
{
this.context = context;
}
public IQueryable<Product> Products
{
get { return context.GetTable<Product>(); }
}
}
By the way you don't have to decorate your constructor with the Inject attribute. Ninject will select the constructor with the most parameters by default.
Please refer below code snap:
//Bind the default connection string
public void BindDataContext()
{
ConstructorArgument parameter = new ConstructorArgument("connectionString", "[Config Value]");
Bind<DataContext>().ToSelf().InRequestScope().WithParameter(parameter);
}
//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);
}
For more information, please visit below link
MVC3, Ninject and Ninject.MVC3 problem

Resources