I'm having a difficult time wrapping my head around Mef and how imports and exports work. My project structure is as follows.
Projects:
MefMVPApp (Main MVC 4 app)
MefMVCFramework.Common(Interfaces shared between the projects)
MefMVCDemo.Plugins.OrderStatus (pluggable area.)
MefMVCDemo.Plugins.Data (Repository for OrderStatus)
OrderStatus.Models(domain models shared between the projects)
The goal of the main Mvc App will be to host plug-able areas via mef.
The OrderStatus Area has a controller called OrderStatusController and is decorated with the Export Attribute and a ImportingConstructor.
[Export(typeof(IController))]
[ExportMetadata("controllerName", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private readonly IRepository<OrderStatusApp.OrderStatusResponse>_repository ;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetAll();
return View();
}
}
IRepository is a class in the MefMVCFramework.Common assembly and will be used for generic CRUD operations.
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(int id);
void Add(T entity);
int SaveOrUpdate(T entity);
bool Delete(T entity);
bool Delete(int id);
}
The MefMVCDemo.Plugins.Data assembly contains a Class called OrderManagementRepository that inherents for the generic repository and is marked with an Export Attribute.
[Export(typeof(IRepository<OrderStatusApp.OrderStatusResponse>))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderManagementRepository : IRepository<OrderStatusApp.OrderStatusResponse>
{
private readonly JsonServiceClient _client;
public OrderManagementRepository()
{
_client = new JsonServiceClient("http://localhost:52266");
}
public IEnumerable<OrderStatusApp.OrderStatusResponse> GetAll()
{
throw new NotImplementedException("Can not get all");
}
public OrderStatusApp.OrderStatusResponse GetById(int id)
{
throw new NotImplementedException();
}
public void Add(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public int SaveOrUpdate(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(int id)
{
throw new NotImplementedException();
}
}
Using Mefx tool I am able to see my parts and there are no rejection.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /parts
MefMVCDemo.Plugins.Data.OrderManagementRepository
mefMVCDemo.Plugins.OrderStatus.Controllers.OrderStatusController
MefMVCDemo.Plugins.OrderStatus.Verbs.OrderStatusVerb
I can see my import.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /imports
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
Now when browse my main mvc site with the /orderstatus uri I get the following error:
No parameterless constructor defined for this object.
Adding a default constructor to the OrderStatusController that takes no overloads doesn't seem to work.
I guess the question is what am I doing wrong? Why does my interface in the constructor all way end up being null and why is there an mvc error about the "No parameterless constructor defined for this object".
The default controller factory in MVC tries to create the controller using a parameterless constructor. If you want to change this behavior, then you need to create your own custom controller factory.
Here is an example of a ControllerFactory using imports/exports on controllers
I'm using MEF for importing some parts to my app but my controllers are not imported/exported, so I created the following controller factory
public class ControllerFactory : IControllerFactory
{
private readonly CompositionContainer _container;
private IControllerFactory _innerFactory;
/// <summary>
/// Constructor used to create the factory
/// </summary>
/// <param name="container">MEF Container that will be used for importing</param>
public ControllerFactory(CompositionContainer container)
{
_container = container;
_innerFactory = new DefaultControllerFactory();
}
/// <summary>
/// Method used for create the controller based on the provided name. It calls the
/// constructor of the controller passing the MEF container
/// </summary>
/// <param name="requestContext">Context of the request</param>
/// <param name="controllerName">Name of the controller provided in the route</param>
/// <returns>The controller instance</returns>
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
Type controllerType = FindControllerByName(controllerName);
var args = new object[] { this._container };
var controller = (IController)Activator.CreateInstance(controllerType, args);
return controller;
}
/// <summary>
/// This methods looks into the current Assembly for the Controller type
/// </summary>
/// <param name="name">The controller name provided in the route</param>
/// <returns>The controller type</returns>
private static Type FindControllerByName(string name){
var a = Assembly.GetAssembly(typeof(ControllerFactory));
var types = a.GetTypes();
Type type = types.Where(t => t.Name == String.Format("{0}Controller", name)).FirstOrDefault();
return type;
}
public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return System.Web.SessionState.SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
}
}
Thank you pollirrata for pointing me in the right direction.
I had to change a few things to get this to work.
1.) I added an interface called INameMetadata to my MefMVCFramework.Common project.
public interface INameMetadata
{
string Name { get; }
}
2.) Modified My ExportMetadata Tag on my controller export to be Name, OrderStatus.
[Export(typeof(IController))]
[ExportMetadata("Name", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private IRepository<OrderStatusApp.OrderStatusResponse> _repository;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetById(47985);
return View(model);
}
}
3.) Created the MefControllerFactory (based off what pollirrata posted but modified to look for the Metadata)
public class MefControllerFactory : IControllerFactory
{
private string _pluginPath;
private readonly DirectoryCatalog _catalog;
private readonly CompositionContainer _container;
private DefaultControllerFactory _defaultControllerFactory;
public MefControllerFactory(string pluginPath)
{
_pluginPath = pluginPath;
_catalog = new DirectoryCatalog(pluginPath);
_container = new CompositionContainer(_catalog);
_defaultControllerFactory = new DefaultControllerFactory();
}
public MefControllerFactory(CompositionContainer compositionContainer)
{
_container = compositionContainer;
_defaultControllerFactory = new DefaultControllerFactory();
}
#region IControllerFactory Members
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
//IController controller = null;
var controller = _container.GetExports<IController,INameMetadata>()
.Where(e=>e.Metadata.Name.Equals(controllerName))
.Select(e=>e.Value).FirstOrDefault();
if (controller == null)
{
throw new HttpException(404, "Not found");
}
return controller;
}
public void ReleaseController(IController controller)
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
#endregion
public SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
}
4.) I created a Class called MefConfig in Main MVC app and moved it to the App_Start Dir.
public static class MefConfig
{
public static void RegisterMef()
{
//var builder = new RegistrationBuilder();
//builder.ForTypesDerivedFrom<IRepository<OrderStatusApp.OrderStatusResponse>>().Export<IRepository<IRepository<OrderStatusApp.OrderStatusResponse>>>();
var directoryCatalog = new DirectoryCatalog(HostingEnvironment.MapPath("~/bin"), "*.dll");
var container = new CompositionContainer(directoryCatalog, true);
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
//Working
//ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(HostingEnvironment.MapPath("~/bin")));
// Install MEF dependency resolver for MVC
var resolver = new MefDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
// Install MEF dependency resolver for Web API
GlobalConfiguration.Configuration.DependencyResolver = resolver;
var d = container.GetExportedValues<IRepository<OrderStatusApp.OrderStatusResponse>>();
//Mefx.
try
{
//var ci = new CompositionInfo(aggregateCatalog, container);
var ci = new CompositionInfo(directoryCatalog, container);
var partDef = ci.GetPartDefinitionInfo(typeof(IRepository<OrderStatusApp.OrderStatusResponse>));
//var possibleCauses = partDef.FindPossibleRootCauses();
var stringWriter = new StringWriter();
CompositionInfoTextFormatter.Write(ci, stringWriter);
var compStatString = stringWriter.ToString();
}
catch
{
}
MvcApplication.ActionVerbs = container.GetExports<IActionVerb, IActionVerbMetadata>();
}
}
5.) Load the Mefconfig from the global.asax.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
//Register Mef
MefConfig.RegisterMef();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Related
I have a multi tier application using Entity Framework, MVC and Unity.
The basic setup is like this:
EF Data Access Layer
public class MyDataProvider : DbContext, IMyDataProvider
{
public MyDataProvider(SqlConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection,contextOwnsConnection)
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 60;
Configuration.LazyLoadingEnabled = true;
Configuration.ValidateOnSaveEnabled = true;
Configuration.ProxyCreationEnabled = true;
Configuration.AutoDetectChangesEnabled = true;
}
public new IDbSet<TModel> Set<TModel>() where TModel : class
{
return base.Set<TModel>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new EmployeeMapping());
base.OnModelCreating(modelBuilder);
}
public abstract class ServiceBase<TModel> : IDisposable, IService<TModel> where TModel : class, IModel
{
[Dependency]
public IMyDataProvider MyDataProvider { get; set; }
...
}
All services inherit from this class
I then inject specific services into the Business Logic Layer like so:
public class GetEmployees
{
[Dependency("EmployeeService")]
public IEmployeeService EmployeeService { get; set;
public IQueryable<Employee> GetAllEmployees()
{
return EmployeeService.GetTable();
}
...
}
In MVC I use a controller factory
public class MyControllerFactory : DefaultControllerFactory
{
private IUnityContainer _container;
public MyControllerFactory(IUnityContainer container)
{
_container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType != null)
{
return _container.Resolve(controllerType) as IController;
}
else
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
Global.asax
private static IUnityContainer InitContainer()
{
IUnityContainer unityContainer = new UnityContainer();
Bootstrapper bootstrapper = new Bootstrapper(unityContainer);
return unityContainer;
}
I pass the instance of UnityContainer into the Bootstrapper class. The Bootstrapper class self registers all assemblies.
In the MVC Controllers, I inject the Business Logic like so:
public class EmployeeController
{
[Dependency("GetEmployees")]
public IBusinessLogic GetEmployees_Operations { get; set; }
public ActionResult EmployeeMain()
{
var employees = GetEmployees_Operations.GetAllEmployees();
...
}
}
This all works great up to a point. Every so often I will get an exception thrown from MyDataProvider class: "EntityConnection can only be constructed with a closed DbConnection". This seems to happen during high use of the MVC site. The exception is simple enough to understand, but how should I go about fixing it?
I found that changing how I instantiate the business logic class from a field on the controller to inside the ActionResult method, I don't recieve the exception.
For example:
public class EmployeeController
{
//[Dependency("GetEmployees")]
//public IBusinessLogic GetEmployees_Operations { get; set; }
public ActionResult EmployeeMain()
{
IBusinessLogic GetEmployees_Operations = _ioc_Bootstrapper.Resolve(typeof(IBusinessLogic), "GetEmployees") as IBusinessLogic;
var employees = GetEmployees_Operations.GetAllEmployees();
...
}
}
Have I completely missed the boat on this and implemented Unity incorrectly?
Bootstrapper code
private void RegisterDAL(String assembly)
{
var currentAssembly = Assembly.LoadFrom(assembly);
var assemblyTypes = currentAssembly.GetTypes();
foreach (var assemblyType in assemblyTypes)
{
...
if (assemblyType.FullName.EndsWith("Provider"))
{
foreach (var requiredInterface in assemblyType.GetInterfaces())
{
if (requiredInterface.FullName.EndsWith("DataProvider"))
{
var typeFrom = assemblyType.GetInterface(requiredInterface.Name);
var typeTo = assemblyType;
var injector = GetInjectorConstructor(assemblyType.Module.Name);
RegisterType(typeFrom, typeTo, false, injector);
}
}
continue;
}
...
}
private InjectionConstructor GetInjectorConstructor(String moduleName)
{
...
connString = String.Concat("Data Source=MySqlServer, ";Initial Catalog=", catalogName, ";Application Name=", applicationName, ";Integrated Security=True; );
var conn = new SqlConnection(connString);
return new InjectionConstructor(conn, true);
}
UnitOfWorkManager
public class UnitOfWorkManager : IUnitOfWorkManager
{
private bool _isDisposed;
private readonly BEMContext _context;
public UnitOfWorkManager(IBEMContext context)
{
// http://stackoverflow.com/questions/3552000/entity-framework-code-only-error-the-model-backing-the-context-has-changed-sinc
Database.SetInitializer<BEMContext>(null);
_context = context as BEMContext;
}
/// <summary>
/// Provides an instance of a unit of work. This wrapping in the manager
/// class helps keep concerns separated
/// </summary>
/// <returns></returns>
public IUnitOfWork NewUnitOfWork()
{
return new UnitOfWork(_context);
}
/// <summary>
/// Make sure there are no open sessions.
/// In the web app this will be called when the injected UnitOfWork manager
/// is disposed at the end of a request.
/// </summary>
public void Dispose()
{
if (!_isDisposed)
{
_context.Dispose();
_isDisposed = true;
}
}
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly BEMContext _context;
private readonly IDbTransaction _transaction;
private readonly ObjectContext _objectContext;
/// <summary>
/// Constructor
/// </summary>
public UnitOfWork(BEMContext context)
{
_context = context;
// In order to make calls that are overidden in the caching ef-wrapper, we need to use
// transactions from the connection, rather than TransactionScope.
// This results in our call e.g. to commit() being intercepted
// by the wrapper so the cache can be adjusted.
// This won't work with the dbcontext because it handles the connection itself, so we must use the underlying ObjectContext.
// http://blogs.msdn.com/b/diego/archive/2012/01/26/exception-from-dbcontext-api-entityconnection-can-only-be-constructed-with-a-closed-dbconnection.aspx
_objectContext = ((IObjectContextAdapter)_context).ObjectContext;
if (_objectContext.Connection.State != ConnectionState.Open)
{
_objectContext.Connection.Open();
_transaction = _objectContext.Connection.BeginTransaction();
}
}
public void SaveChanges()
{
_context.SaveChanges();
}
public void Commit()
{
_context.SaveChanges();
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
// http://blog.oneunicorn.com/2011/04/03/rejecting-changes-to-entities-in-ef-4-1/
foreach (var entry in _context.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Deleted:
// Note - problem with deleted entities:
// When an entity is deleted its relationships to other entities are severed.
// This includes setting FKs to null for nullable FKs or marking the FKs as conceptually null (don’t ask!)
// if the FK property is not nullable. You’ll need to reset the FK property values to
// the values that they had previously in order to re-form the relationships.
// This may include FK properties in other entities for relationships where the
// deleted entity is the principal of the relationship–e.g. has the PK
// rather than the FK. I know this is a pain–it would be great if it could be made easier in the future, but for now it is what it is.
entry.State = EntityState.Unchanged;
break;
}
}
}
public void Dispose()
{
if (_objectContext.Connection.State == ConnectionState.Open)
{
_objectContext.Connection.Close();
}
}
}
LanguageRepository
public class LanguageRepository : ILanguageRepository
{
private readonly BEMContext _context;
public LanguageRepository(IBEMContext context)
{
_context = context as BEMContext;
}
public Language Insert(Language language)
{
_context.Language.Add(language);
return language;
}
}
LocalizationService
public class LocalizationService : ILocalizationService
{
private readonly ILanguageRepository _languageRepository;
public LocalizationService(ILanguageRepository languageRepository)
{
_languageRepository = languageRepository;
}
public void CreateLanguage(Language language)
{
_languageRepository.Insert(language);
}
}
BaseController
public class BaseController : Controller
{
protected readonly IUnitOfWorkManager _unitOfWorkManager;
public BaseController(IUnitOfWorkManager unitOfWorkManager)
{
_unitOfWorkManager = unitOfWorkManager;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
}
LocalizationController
public class LocalizationController : BaseController
{
private readonly ILocalizationService _localizationService;
public LocalizationController(ILocalizationService localizationService, IUnitOfWorkManager unitOfWorkManager)
: base(unitOfWorkManager)
{
_localizationService = localizationService;
}
public ActionResult AddLanguage()
{
return View();
}
[HttpPost]
public ActionResult AddLanguage(LanguageModel model)
{
try
{
if (ModelState.IsValid)
{
using (var UnitOfWork = _unitOfWorkManager.NewUnitOfWork())
{
try
{
var language = Mapper.Map<LanguageModel, Language>(model);
_localizationService.CreateLanguage(language);
UnitOfWork.Commit();
}
catch (Exception ex)
{
UnitOfWork.Rollback();
throw new Exception(ex.Message);
}
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return View(model);
}
}
I cant add language to DB. Because service and unitOfWork classes use different context. Service change the context, but UnitOfWork save changes on another context. I mean following:
context in _localizationService.CreateLanguage(language);
context in UnitOfWork
Of cource, change does not effect db(I cant add entity to db). Because there are two different DbContex. I hope, I can explain, but I dont know how can I ask this question. How Can I solve this problem.
EDIT
I use Unity.MVC4 for IOC like following
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IBEMContext, BEMContext>();
container.RegisterType<ILocalizationService, LocalizationService>();
container.RegisterType<ILanguageRepository, LanguageRepository>();
container.RegisterType<ILocaleResourceKeyRepository, LocaleResourceKeyRepository>();
container.RegisterType<ILocaleResourceValueRepository, LocaleResourceValueRepository>();
container.RegisterType<IUnitOfWorkManager, UnitOfWorkManager>();
}
}
Unity is creating new instances of the dependencies, which means that as both IUnitOfWorkManager and ILocalizationService are instantiated, their dependencies are instantiated separately. Find a way to share the dependency on the context; ideally only the unit of work should be really aware of it, and anything else should go through the unit of work to make changes to the context, I think.
My solution is:
/// <summary>
/// Bind the given interface in request scope
/// </summary>
public static class IOCExtensions
{
public static void BindInRequestScope<T1, T2>(this IUnityContainer container) where T2 : T1
{
container.RegisterType<T1, T2>(new HierarchicalLifetimeManager());
}
public static void BindInSingletonScope<T1, T2>(this IUnityContainer container) where T2 : T1
{
container.RegisterType<T1, T2>(new ContainerControlledLifetimeManager());
}
}
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
container.BindInRequestScope<IBEMContext, BEMContext>();
container.BindInRequestScope<ILocalizationService, LocalizationService>();
container.BindInRequestScope<ILanguageRepository, LanguageRepository>();
container.BindInRequestScope<ILocaleResourceKeyRepository, LocaleResourceKeyRepository>();
container.BindInRequestScope<ILocaleResourceValueRepository, LocaleResourceValueRepository>();
container.BindInRequestScope<IUnitOfWorkManager, UnitOfWorkManager>();
}
}
I have this configuration in my project MVC4 Unity and a generic configucion for drivers.
Bootstrapper.cs
namespace MyProyect.Web
{
public static class Bootstrapper
{
public static void Initialise()
{
var container = BuildUnityContainer();
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
container.RegisterType<MyProyect.Model.DataAccessContract.ICountryDao, MyProyect.DataAccess.CountryDao>();
container.RegisterType<MyProyect.Model.DataAccessContract.IContactDao, MyProyect.DataAccess.ContactDao>();
return container;
}
}
}
Global.asax:
protected void Application_Start()
{
...
Bootstrapper.Initialise();
...
}
GenericModelBinder.cs:
namespace MyProyect.Web.ModelBinder
{
public class GenericModelBinder : DefaultModelBinder//, IValueProvider
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var resolver = (Unity.Mvc4.UnityDependencyResolver)DependencyResolver.Current;
if (modelType == null)
{
return base.CreateModel(controllerContext, bindingContext, null);
}
return resolver.GetService(modelType);
}
}
}
Now that I need is to resolve a dependency in another project within the solution. My question is how I can do to that recognize the settings Unity in another project?. I currently I have this class but the current configuration does not bring in the MVC project.
namespace MyProyect.Model.ListData
{
public abstract class ListDataGenericResolver
{
protected T ResolverType<T>()
{
IUnityContainer container = new UnityContainer();
UnityServiceLocator locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);
//return unity.Resolve<T>();
return (T)container.Resolve(typeof(T));
}
}
}
this is a example how to use ListDataGenericResolver:
namespace MyProyect.Model.ListData.Types
{
public class CountryListData : ListDataGenericResolver, IGetListData
{
private readonly ICountryDao countryDao;
private string defaultValue;
public CountryListData(object defaultValue)
{
// Resolver
this.countryDao = this.ResolverType<ICountryDao>();
this.defaultValue = defaultValue == null ? string.Empty : defaultValue.ToString();
}
public IList<SelectData> GetData()
{
var data = this.countryDao.GetAllCountry(new Entity.Parameters.CountryGetAllParameters());
return data.Select(d => new SelectData
{
Value = d.CountryId.ToString(),
Text = d.Description,
Selected = this.defaultValue == d.CountryId.ToString()
}).ToList();
}
}
}
Thank you.
Here is how I do this.
In Application_Start, I create the unity container. I have a custom library that I use for all of my MVC projects that I import through NuGet, so I make the call to its configure method, then call the other project's Configure() methods. You could simply omit the custom library and add that code in here as well. All of that keeps my Application_Start nice and clean.
protected void Application_Start()
{
// Standard MVC setup
// <removed>
// Application configuration
var container = new UnityContainer();
new CompanyName.Mvc.UnityBootstrap().Configure(container);
new AppName.ProjectName1.UnityBootstrap().Configure(container);
new AppName.ProjectName2.UnityBootstrap().Configure(container);
// <removed>
}
This is the code for the custom MVC library's UnityBootstrap class
namespace CompanyName.Mvc
{
/// <summary>
/// Bootstraps <see cref="CompanyName.Mvc"/> into a Unity container.
/// </summary>
public class UnityBootstrap : IUnityBootstrap
{
/// <inheritdoc />
public IUnityContainer Configure(IUnityContainer container)
{
// Convenience registration for authentication
container.RegisterType<IPrincipal>(new InjectionFactory(c => HttpContext.Current.User));
// Integrate MVC with Unity
container.RegisterFilterProvider();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
}
}
Then, in the other projects, I have a UnityBootstrap there, that was called from Application_Start:
ProjectName1:
namespace AppName.ProjectName1
{
public class UnityBootstrap : IUnityBootstrap
{
public IUnityContainer Configure(IUnityContainer container)
{
return container.RegisterType<IDocumentRoutingConfiguration, DocumentRoutingConfiguration>();
}
}
}
ProjectName2: - and you can see in here, that this one depends on some other projects in another library and it is calling their Configure() methods to get them set up too...
namespace AppName.ProjectName2
{
public class UnityBootstrap : IUnityBootstrap
{
public IUnityContainer Configure(IUnityContainer container)
{
new CompanyName.Security.UnityBootstrap().Configure(container);
new CompanyName.Data.UnityBootstrap().Configure(container);
container.RegisterSecureServices<AuthorizationRulesEngine>(typeof(UnityBootstrap).Assembly);
return container
.RegisterType<IAuthorizationRulesEngine, AuthorizationRulesEngine>()
.RegisterType<IDateTimeFactory, DateTimeFactory>()
.RegisterType<IDirectoryInfoFactory, DirectoryInfoFactory>()
.RegisterType<IDirectoryWrapper, DirectoryWrapper>()
.RegisterType<IEmailService, EmailService>()
.RegisterType<IEntryPointService, EntryPointService>();
}
}
}
Here is the IUnityBootstrap interface that is used throughout the code above (for your reference)
/// <summary>
/// Defines a standard interface for bootstrapping an assembly into a Unity container.
/// </summary>
public interface IUnityBootstrap
{
/// <summary>
/// Registers all of the assembly's classes to their public interfaces and performs any other necessary configuration.
/// </summary>
/// <param name="container">The Unity container instance to configure.</param>
/// <returns>The same IUnityContainer object that this method was called on.</returns>
IUnityContainer Configure(IUnityContainer container);
}
I hope this helps you out.
I really dont understand what I'm doing wrong here - for some reason the IControllerFactory i register is not being used, and I end up with a System.ArgumentException:
Type 'Company.WebApi.Controllers.AController' does not have a default
constructor
at System.Linq.Expressions.Expression.New(Type type) at
System.Web.Http.Internal.TypeActivator.Create[TBase](Type
instanceType) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage
request, Type controllerType, Func`1& activator) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType)
In Global.asax.cs
protected void Application_Start()
{
//DI-setup
var container = new WindsorContainer().Install(new WebWindsorInstaller());
//set custom controller factory
var controllerFactory = container.Resolve<IControllerFactory>();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
//register cors
container.Resolve<ICorsConfig>().RegisterCors(GlobalConfiguration.Configuration);
//routes
RegisterRoutes(RouteTable.Routes);
}
My IControllerFactory is based on this article http://keyvan.io/custom-controller-factory-in-asp-net-mvc and is implemented as following
public class ControllerFactory : IControllerFactory
{
private readonly IWindsorContainer _container;
public ControllerFactory(IWindsorContainer container)
{
if(container == null)
throw new ArgumentNullException("container");
_container = container;
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
var componentName = GetComponentNameFromControllerName(controllerName);
return _container.Resolve<IController>(componentName);
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
throw new NotImplementedException();
}
public void ReleaseController(IController controller)
{
_container.Release(controller);
}
static string GetComponentNameFromControllerName(string controllerName)
{
var controllerNamespace = typeof (CrudController<>).Namespace;
return string.Format("{0}.{1}Controller", controllerNamespace, controllerName);
}
}
I've been staring at this for hours, and really can't see why this isn't working. When debugging the ControllerFactory is never hit in any method except the constructor. Anyone see whats wrong or missing here?
(Setting ASP.NET MVC ControllerFactory has no effect does not answer my question)
Edit 1 - Controller
public class AController : CrudController<AModel>
{
public AController(IAHandler aHandler) : base(aHandler)
{
...
}
[HttpGet]
public HttpResponseMessage GetByUser(string aId)
{
...
}
}
public abstract class CrudController<T> : ApiController
where T : IModel, new()
{
protected CrudController(ICrudHandler<T> handler)
{
...
}
[HttpGet]
public HttpResponseMessage Get(string id)
{
...
}
[HttpPost]
public HttpResponseMessage Post(T input)
{
...
}
[HttpPut]
public HttpResponseMessage Put(T input)
{
...
}
}
Edit 2 - Installer
public sealed class WebWindsorInstaller : IWindsorInstaller
{
private bool _installComplete;
public void Install(IWindsorContainer container, IConfigurationStore store)
{
if(_installComplete)
return;
//register self for reuse
container.Register(Component.For<IWindsorInstaller>().Instance(this));
//controller factory
container.Register(
Component.For<IControllerFactory>().ImplementedBy<ControllerFactory>().LifeStyle.Singleton);
//Handlers
container.Register(
Classes.FromAssemblyContaining<AHandler>().InSameNamespaceAs<AHandler>().WithService.
DefaultInterfaces());
//Models
container.Register(
Classes.FromAssemblyContaining<AModel>().InSameNamespaceAs<AModel>().WithService.Self());
//DI
container.Register(Component.For<IWindsorContainer>().Instance(container));
container.Register(Component.For<IDependencyResolver>().ImplementedBy<WindsorDependencyResolver>());
//Controllers
container.Register(Classes
.FromAssemblyContaining<AController>()
.BasedOn<IHttpController>()
.LifestyleScoped());
//repositories
container.Register(
Classes.FromAssemblyContaining<ARepository>().InSameNamespaceAs<ARepository>().WithService.
DefaultInterfaces());
//cors
container.Register(Component.For<ICorsConfig>().ImplementedBy<CorsConfig>());
_installComplete = true;
}
}
From the error messages in your question it looks like you actually want to register WebAPI controllers. It's important to note that these are not the same as MVC controllers, and do not use the same controller factory (which you are trying to use).
See Mark Seeman's post Dependency Injection in ASP.NET Web API with Castle Windsor for details of how to do this for Web API controllers.
I am testing Account Controller in asp.net mvc project. I tested all methods and I looked the code coverage results, I noticed the Initialize method is not coveraged.
How can I test this method?
public class AccountController : Controller
{
public IFormsAuthenticationService FormsService { get; set; }
public IMembershipService MembershipService { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (FormsService == null) { FormsService = new FormsAuthenticationService(); }
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
base.Initialize(requestContext);
}
I tried this test method but error occured.
[TestMethod]
public void Constructor_ReturnsController()
{
RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
AccountController controller = new AccountController
{
FormsService = null,
MembershipService = null,
Url = new UrlHelper(requestContext),
};
IController cont = controller;
cont.Execute(requestContext);
}
Error Message:
Test method MVC3Project.Tests.Controllers.AccountControllerTests+AccountControllerTest.Constructor_ReturnsController threw exception:
System.NotImplementedException: The method or operation is not implemented.
You will need to invoke it explicitly in your unit test:
[TestMethod]
public void AccountController_Initialize_Method_Should_WireUp_Dependencies()
{
// arrange
AccountController controller = new AccountController();
// act
controller.Initialize(null);
// assert
Assert.IsNotNull(controller.FormsService);
Assert.IsNotNull(controller.MembershipService);
}
Note that using the Initialize method to setup your dependencies is bad practice. I would recommend you using dependency injection:
public class AccountController : Controller
{
public IFormsAuthenticationService FormsService { get; private set; }
public IMembershipService MembershipService { get; private set; }
public AccountController(IFormsAuthenticationService formsService, IMembershipService membershipService)
{
FormsService = formsService;
MembershipService = membershipService;
}
... actions
}
Now you could setup your favorite dependency injection framework to inject those dependencies into your controller. For example with Ninject that's pretty easy. Install the Ninject.MVC3 NuGet and then inside the generated ~/App_Start/NinjectWebCommon.cs file configure the kernel:
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IFormsAuthenticationService>().To<FormsAuthenticationService>();
kernel.Bind<IMembershipService>().To<AccountMembershipService>();
}
and finally you could mock those dependencies into your unit test and then define expectations on them.