First time using MS Unity. I have a controller with the following constructor:
protected IAdministrationService AdministrationService { get; set; }
public GenerateCacheController(IAdministrationService administrationService)
{
AdministrationService = administrationService;
}
I get the following error when trying to run the project:
Make sure that the controller has a parameterless public constructor.
In my Bootstrpper.cs file I have the following in the RegisterTypes method:
container.RegisterType<GenerateCacheController>();
I still get the error. Am I missing anything else? I'm using ASP.NET MVC 5 and Unity 3.
Here's my Boostrapper.cs file:
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();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterInstance(container);
var im = new InjectionMember[0];
container.RegisterType<IAdministrationService, AdministrationService>("AdministrationService", im);
container.RegisterType<ILookupMapper, LookupMapper>("LookupMapper", im);
container.RegisterType<IEmailService, EmailService>("EmailService", im);
container.RegisterType<GenerateCacheController>();
var provider = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => provider);
}
}
Abbreviated version of the AdministrationService class:
public class AdministrationService : IAdministrationService
{
protected ILookupMapper LookupMapper { get; set; }
protected IEmailService EmailService { get; set; }
public AdministrationService(ILookupMapper lookupMapper, IEmailService emailService)
{
LookupMapper = lookupMapper;
EmailService = emailService;
}
}
Found the issue.
I commented out the line:
var im = new InjectionMember[0];
container.RegisterType<IAdministrationService, AdministrationService>("AdministrationService", im);
and added:
container.RegisterType<IAdministrationService, AdministrationService>();
And that worked because the previous developers were doing something like this:
private IUnityContainer Container { get; set; }
public AdministrationService()
{
Container = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IUnityContainer>();
}
instead of
protected ILookupMapper LookupMapper { get; set; }
protected IEmailService EmailService { get; set; }
public AdministrationService(ILookupMapper lookupMapper, IEmailService emailService)
{
LookupMapper = lookupMapper;
EmailService = emailService;
}
I have to go back to their way to not break existing code. I'll get around to refactoring one day.
Related
Hi hope you guys can help me.
I Inherited a Project and needed to Update the Packages and there was a Change in the Initialization.
public abstract class ModuleBase : Prism.Modularity.IModule
{
public IUnityContainer UnityContainer { get; private set; }
public IRegionManager RegionManager { get; private set; }
public ModuleBase(IUnityContainer unityContainer, IRegionManager regionManager)
{
if (unityContainer == null)
{
throw new ArgumentNullException("unityContainer");
}
UnityContainer = unityContainer;
if (regionManager == null)
{
throw new ArgumentNullException("regionManager");
}
RegionManager = regionManager;
}
public virtual void Initialize() { }//old
public abstract void RegisterTypes(IContainerRegistry containerRegistry);//new
public abstract void OnInitialized(IContainerProvider containerProvider);//new
}
I have this Base and from there Extended are the different Modules.
public class UserModule : ModuleBase
{
public UserModule(IUnityContainer unityContainer, IRegionManager regionManager)
: base(unityContainer, regionManager)
{
}
public override void Initialize()//old
{
UnityContainer.RegisterType<UserView>();
UnityContainer.RegisterType<UserKernelSettingsView>();
UnityContainer.RegisterType<UserNavigationItemView>();
UnityContainer.RegisterTypeForNavigation<UserView>();
UnityContainer.RegisterTypeForNavigation<UserKernelSettingsView>();
RegionManager.RegisterViewWithRegion(RegionNames.NavigationRegion, typeof(UserNavigationItemView));
}
public override void OnInitialized(IContainerProvider containerProvider)//new
{
}
public override void RegisterTypes(IContainerRegistry containerRegistry)//new
{
}
}
But now i am not able to Register the Views correct.
BR MAX
Try this:
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion(RegionNames.NavigationRegion, typeof(UserNavigationItemView));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<UserView>();
containerRegistry.Register<UserKernelSettingsView>();
containerRegistry.Register<UserNavigationItemView>();
containerRegistry.RegisterForNavigation<UserView>();
containerRegistry.RegisterForNavigation<UserKernelSettingsView>();
}
I have a fairly simple ASP.NET MVC app that I am trying to resolve some dependencies in my controller. I have casting problems with List and I am not sure what to do at this moment. I have read about the Resolve() method with Autofac, but again I am not sure if this will resolve my particular issue.
Here is my controller code:
public class NumbersController : Controller
{
private INumbersModel _model;
private INumbersBusinessLayer _numbersBusinessLayer;
private IEnumerable<INumbersModel> _modelList;
public NumbersController(INumbersModel model, IEnumerable<INumbersModel> modelList, INumbersBusinessLayer numbersBusinessLayer)
{
_model = model;
_numbersBusinessLayer = numbersBusinessLayer;
_modelList = new List<INumbersModel>(modelList);
}
public ActionResult Index()
{
_modelList = _numbersBusinessLayer.AllNumbers.ToList();
return View(_modelList);
}
[HttpGet]
public ActionResult Edit(int id)
{
_model = _numbersBusinessLayer.AllNumbers.Single(n => n.ID == id);
return View(_model);
}
}
Here are my two interfaces:
public interface INumbersBusinessLayer
{
IEnumerable<NumbersModel> AllNumbers { get; }
void AddNumbers(NumbersModel model);
void DeleteNumbers(int id);
void UpdateNumbers(NumbersModel model);
}
public interface INumbersModel
{
int ID { get; set; }
bool IsValid { get; set; }
string Numbers { get; set; }
string Order { get; set; }
string Time { get; set; }
}
Here is my container config:
public static void ConfigureDependencyInjection()
{
var builder = new ContainerBuilder();
// ...or you can register individual controlllers manually.
builder.RegisterType<NumbersController>().InstancePerRequest();
// register models
builder.RegisterType<NumbersModel>().As<INumbersModel>();
builder.RegisterType<List<NumbersModel>>().As<List<INumbersModel>>();
builder.RegisterType<NumbersBusinessLayer>().As<INumbersBusinessLayer>();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
In the browser I get this error:
The type 'System.Collections.Generic.List1[BusinessLayer.NumbersModel]' is not assignable to service 'System.Collections.Generic.List1[[BusinessLayer.INumbersModel, BusinessLayer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'.
You should only need to register your INumbersBusinessLayer, since that is the only thing you actually need to inject into your controller.
Furthermore, change INumbersModel to a class, so you can use it.
So your controller then looks like this:
public class NumbersController : Controller
{
private INumbersBusinessLayer _numbersBusinessLayer;
public NumbersController(INumbersBusinessLayer numbersBusinessLayer)
{
_numbersBusinessLayer = numbersBusinessLayer;
}
public ActionResult Index()
{
var modelList = _numbersBusinessLayer.AllNumbers.ToList();
return View(modelList);
}
[HttpGet]
public ActionResult Edit(int id)
{
var model = _numbersBusinessLayer.AllNumbers.Single(n => n.ID == id);
return View(model);
}
}
Then you can simplify your AutoFac config:
public static void ConfigureDependencyInjection()
{
var builder = new ContainerBuilder();
// ...or you can register individual controlllers manually.
builder.RegisterType<NumbersController>().InstancePerRequest();
builder.RegisterType<NumbersBusinessLayer>().As<INumbersBusinessLayer>();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
and change your interface to a class:
public class NumbersModel
{
int ID { get; set; }
bool IsValid { get; set; }
string Numbers { get; set; }
string Order { get; set; }
string Time { get; set; }
}
Since your businesslayer interface already expects Numbersmodel, but not INumbersmodel, no changes are needed there.
Only two parameters require to be injected
private INumbersModel _model;
private INumbersBusinessLayer _numbersBusinessLayer;
public NumbersController(INumbersModel model,INumbersBusinessLayer numbersBusinessLayer)
{
_model = model;
_numbersBusinessLayer = numbersBusinessLayer
}
and no need to register a list of NumbersModel, comment this line and try again
builder.RegisterType<List<NumbersModel>>().As<List<INumbersModel>>();
I have a problem with Autofac injection or registration.
This is my code
Repository
namespace ClientConfiguration.Data.Repository
{
public class MappingBaseRepository : RepositoryBase<MappingBase>, IMappingBaseRepository
{
public MappingBaseRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{
}
}
public interface IMappingBaseRepository : IRepository<MappingBase>
{
}
}
Service
namespace ClientConfiguration.Service {
public interface IMappingBaseService
{
IEnumerable<MappingBase> GetElements();
void SaveElement();
}
public class MappingBaseService : IMappingBaseService
{
private readonly IMappingBaseRepository MappingBaseRepository;
private readonly IUnitOfWork unitOfWork;
public MappingBaseService(IMappingBaseRepository MappingBaseRepository, IUnitOfWork unitOfWork)
{
this.MappingBaseRepository = MappingBaseRepository;
this.unitOfWork = unitOfWork;
}
#region Members
public IEnumerable<MappingBase> GetElements()
{
var Elements = MappingBaseRepository.GetAll();
return Elements;
}
public void SaveElement()
{
unitOfWork.Commit();
}
#endregion
} }
Autofac init
private static void SetAutofacContainer() {
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
builder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>().InstancePerRequest();
// Repositories
builder.RegisterAssemblyTypes(typeof(ClientElementRepository).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().InstancePerRequest();
// Services
builder.RegisterAssemblyTypes(typeof(ClientElementService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
Now if I'm inside a controller I have an instance of the service objects without problem. But I have to access my service IMappingBaseService to get data from DB inside this class:
namespace ClientConfiguration.Mappings {
public class CustomDisplayNameAttribute : DisplayNameAttribute {
private static IMappingBaseService mappingBaseService { get; set; }
public CustomDisplayNameAttribute(string value)
: base(GetMessageFromResource(value)) {
}
private static string GetMessageFromResource(string value) {
var els = mappingBaseService.GetElements().ToList();
//get value from DB
//mappingBaseService is always null
return "";
}
}
}
Any help would be greatly appreciated! Thanks in advance.
Code demo such as:
namespace ClientConfiguration.Mappings {
public class CustomDisplayNameAttribute : DisplayNameAttribute {
private static IMappingBaseService _mappingBaseService { get; set; }
public CustomDisplayNameAttribute(string value, IMappingBaseService mappingBaseService)
: base(GetMessageFromResource(value, mappingBaseService)) {
}
private static string GetMessageFromResource(string value, IMappingBaseService mappingBaseService) {
_mappingBaseService = mappingBaseService;
var els = _mappingBaseService .GetElements().ToList();
//OR var els = mappingBaseService.GetElements().ToList();
//get value from DB
//mappingBaseService is always null
return "";
}
}
}
Maybe you can fix code register autofac, Because autofac only register for interface such as:
builder.RegisterAssemblyTypes(typeof(IClientElementRepository).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().InstancePerRequest();
// Services
builder.RegisterAssemblyTypes(typeof(IClientElementService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
builder.RegisterAssemblyTypes(typeof(IMappingBaseService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
The solution was to use Property injection (instanciate the class inside the autofac init)
We have to add this line
builder.Register(c => new CustomDisplayNameAttribute {
_mappingBaseService = c.Resolve<IMappingBaseService>() });
and in CustomDisplayNameAttribute we add empty constructor
public CustomDisplayNameAttribute() {}
and
public IMappingBaseService _mappingBaseService { get; set; }
and for getting the object we use
var _mappingBaseService = DependencyResolver.Current.GetService<IMappingBaseService>();
The problem is that is i surcharge CustomDisplayName from DisplayNameAttribute (ASP.NET MVC)
public class ClientElementsViewModel {
private static IMappingBaseService _mappingBaseService;
public ClientElementsViewModel(IMappingBaseService mappingBaseService) {
_mappingBaseService = mappingBaseService;
}
[Key]
[Display(Name = "Id")]
public long ClientElementId { get; set; }
[CustomDisplayName("", _mappingBaseService)]
public string CompanyCode { get; set; }
//[CustomDisplayName("")]
public string WebAppBaseUrl { get; set; }
//[CustomDisplayName("")]
public string GuestTraveller { get; set; }
}
I have this error
Error 3 An attribute argument must be a constant expression, typeof
expression or array creation expression of an attribute parameter
type D:\CDS_ADMIN\ClientConfiguration.Web\ViewModel\ClientElementsViewModel.cs 22 32 ClientConfiguration.Web
I am new to dependency injection and Microsoft Unity IoC. My scenario is the follwing:
Employee-Class:
class Employee
{
private readonly ISalaryCalculation _salaryCalculationCalculator;
public Employee(ISalaryCalculation salaryCalculationCalculator)
{
_salaryCalculationCalculator = salaryCalculationCalculator;
BaseSalary = 42;
}
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
public int BaseSalary { get; set; }
public int CalcSalary()
{
return _salaryCalculationCalculator.CalcSalary(BaseSalary);
}
}
I have two different implementations of my SalaryCalculation-Interface
interface ISalaryCalculation
{
int CalcSalary(int baseSalary);
}
My Unity setup is:
static void Main(string[] args)
{
UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<Employee>();
unityContainer.RegisterType<ISalaryCalculation, BossSalaryCalculation>("Boss");
unityContainer.RegisterType<ISalaryCalculation, NormalSalaryCalculation>("Normal");
var employee = unityContainer.Resolve<Employee>(new DependencyOverride(typeof(ISalaryCalculation),unityContainer.Resolve<ISalaryCalculation>("Boss")));
Console.WriteLine(employee.CalcSalary());
}
Is there a smarter way to achiev this by using Unity? Because whenever I instantiate a new Employee, I have to pass its salaryCalculation logic.
You can register only one implementation depending on some condition:
static void Main(string[] args)
{
UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<Employee>();
if (someCondition)
{
unityContainer.RegisterType<ISalaryCalculation, BossSalaryCalculation>("Boss");
}
else
{
unityContainer.RegisterType<ISalaryCalculation, NormalSalaryCalculation>("Normal");
}
var employee = unityContainer.Resolve<Employee>();
Console.WriteLine(employee.CalcSalary());
}
Another way - to use ResolvedParameter:
static void Main(string[] args)
{
UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<Employee>();
unityContainer.RegisterType<ISalaryCalculation, BossSalaryCalculation>("Boss");
unityContainer.RegisterType<ISalaryCalculation, NormalSalaryCalculation>("Normal");
var employee = unityContainer.Resolve<Employee>(new ParameterOverride("salaryCalculationCalculator", new ResolvedParameter<ISalaryCalculation>("Boss")));
Console.WriteLine(employee.CalcSalary());
}
Original Source Code
I've got a simple business object in my BusinessObjects.dll file:
namespace BusinessObjects
{
public class MyClass
{
public MyClass()
{
DateTime = DateTime.Now;
}
public DateTime DateTime { get; set; }
}
}
In my SharedUI.dll I've got this "Context-provider" class, that I use to hold a referece to the currently selected MyClass - remember this is a simplyfied example :)...
namespace SharedUI
{
public class AppContext
{
[Export]
public MyClass SelectedMyClass { get; private set; }
public void SetupContext(MyClass myClass)
{
SelectedMyClass = myClass;
}
public static AppContext Context
{
get
{
if (context == null)
{
context = new AppContext();
}
return context;
}
}
private static AppContext context;
}
}
My MefTest.exe has this class:
namespace MefTest
{
public class Program
{
[Import]
public MyClass MyClass { get; set; }
private void Compose()
{
var ventSystem = new MyClass();
AppContext.Context.SetupContext(ventSystem);
var executingAssembly = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var contextAssembly = new AssemblyCatalog(Assembly.LoadFile(string.Format(#"{0}\SharedUI.dll", Environment.CurrentDirectory)));
var catalog = new AggregateCatalog(executingAssembly, contextAssembly);
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
private void Run()
{
Compose();
// MyClass is always null in the next line?
Console.WriteLine(MyClass.DateTime.ToString());
Console.ReadKey();
}
private static void Main(string[] args)
{
var p = new Program();
p.Run();
}
}
}
I'm a MEF rookie so please bear with me :)
UPDATED Source Code with suggestions from Daniel Plaisted
MyClass source is the same...
SharedUI.dll now looks like this:
namespace SharedUI
{
[Export]
public class AppContext
{
[Export(typeof(MyClass))]
public MyClass SelectedMyClass { get; private set; }
public void SetupContext(MyClass myClass)
{
SelectedMyClass = myClass;
}
}
}
MefTest.exe now looks like this:
namespace MefTest
{
public class Program
{
[Import]
public MyClass MyClass { get; set; }
[Import]
public AppContext AppContext { get; set; }
private void Compose()
{
var executingAssembly = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var contextAssembly = new AssemblyCatalog(Assembly.LoadFile(string.Format(#"{0}\SharedUI.dll", Environment.CurrentDirectory)));
var catalog = new AggregateCatalog(executingAssembly, contextAssembly);
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
var myClass = new MyClass();
AppContext.SetupContext(myClass);
}
private void Run()
{
Compose();
// AppContext.SelectedMyClass is NOT null in the next line... which is good I guess :)
Console.WriteLine(AppContext.SelectedMyClass.DateTime.ToString());
// MyClass is always null in the next line?
Console.WriteLine(MyClass.DateTime.ToString());
Console.ReadKey();
}
private static void Main(string[] args)
{
var p = new Program();
p.Run();
}
}
}
What am I doing wrong since I can't get it working?
When MEF needs to get an Export which is on a property of a class, it will create an instance of the class and call the property getter. So MEF is creating a new instance of your AppContext, different than the static AppContext.Context instance. The instance MEF creates doesn't have the SelectedMyClass property set on it, which is why your import ends up being null.
The problem is:
[Import] public MyClass MyClass { get; set; }
There are no [Export]s defined for MyClass. MEF will compose this appplication based on stuff it "knows", and since it does not know "MyClass"...
I noticed this one:
[Export] public MyClass SelectedMyClass { get; private set; }
This means you are trying to trick MEF into updating one of its parts from time to time? The solution to this would be to create a custom Catalog which contains "runtime" objects, in which you can updated the exported value for MyClass whenever you want. The current implementation will never resolve MyClass...
[edited:]
You can decorate a member as well, but you'll have to add the class type there. So this will work:
[Export(typeof(MyClass)] public MyClass SelectedMyClass { get; private set; }
You put your Export attribute in the wrong place.
You should put it on the definition of MyClass like so:
namespace BusinessObjects
{
[Export]
public class MyClass
{
public MyClass()
{
DateTime = DateTime.Now;
}
public DateTime DateTime { get; set; }
}
}
And then use the [Import] attribute wherever you want an instance of this class.
Remark: You cannot use MEF to move a specific instance of a class (not like this).
MEF is used to create instances of a requested type and inject them at indicated places.
To learn more about MEF check out the project's page at CodePlex.