My problem is that I want to use Func<> factory to resolve dependency. And in if I use ContainerBuilder Update() (I need it for mocking some services in integration tests), this factories still resolve outdated instances.
I created simple scenario to reproduce the problem:
class Program
{
static void Main(string[] args)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Test>().As<ITest>();
containerBuilder.RegisterType<Test1Factory>().As<ITestFactory>();
containerBuilder.RegisterType<TestConsumer>().AsSelf();
var container = containerBuilder.Build();
var tc1 = container.Resolve<TestConsumer>();
var cbupdater = new ContainerBuilder();
cbupdater.RegisterType<Test2>().As<ITest>();
cbupdater.RegisterType<Test2Factory>().As<ITestFactory>();
cbupdater.Update(container);
var tc2 = container.Resolve<TestConsumer>();
Console.ReadLine();
}
}
public interface ITest
{
int Id { get; set; }
}
public class Test : ITest
{
public Test()
{
Id = 1;
}
public int Id { get; set; }
}
public class Test2 : ITest
{
public Test2()
{
Id = 2;
}
public int Id { get; set; }
}
public interface ITestFactory
{
ITest Create();
}
public class Test1Factory : ITestFactory
{
public ITest Create()
{
return new Test();
}
}
public class Test2Factory : ITestFactory
{
public ITest Create()
{
return new Test2();
}
}
public class TestConsumer
{
public TestConsumer(Func<ITest> testFactory, ITest test, ITestFactory customFactory)
{
Console.WriteLine("factory: " + testFactory().Id);
Console.WriteLine("direct: " + test.Id);
Console.WriteLine("MyCustomFactory: " + customFactory.Create().Id);
Console.WriteLine("*************");
Console.WriteLine();
}
}
The output is:
factory: 1 direct: 1 MyCustomFactory: 1
factory: 1 direct: 2 MyCustomFactory: 2
Notice "factory: 1" in both cases.
Am I missing something or I have to create my cusom factory in this scenario?
P.S.
Autofac 3.5.2 or 4.0 beta 8-157
.net 4.5.1
That's by design unfortunately, the reasons, I don't know. Looking at the Autofac code gives you a better insight on how they register items with the same interface definition, in short, all registrations are maintained but the last registration wins (ref). Wait...that's not all, weirdly, for Fun<...>, you actually get them in order. You can easily test by changing the constructor of the TestConsumer class to:
public TestConsumer(Func<ITest> testFactory, IEnumerable<Func<ITest>> testFactories, IEnumerable<ITest> tests, ITest test, ITestFactory customFactory)
{
// ...
}
Note that you get all the Funcs and the ITest registration. You are simply lucky that resolving ITest directly resolves to Test2.
Now, having said all of the above, there is a way described here. You have to create a container without the registration you want to override, therefore:
/// <summary>
/// This has not been tested with all your requirements
/// </summary>
private static IContainer RemoveOldComponents(IContainer container)
{
var builder = new ContainerBuilder();
var components = container.ComponentRegistry.Registrations
.Where(cr => cr.Activator.LimitType != typeof(LifetimeScope))
.Where(cr => cr.Activator.LimitType != typeof(Func<ITest>));
foreach (var c in components)
{
builder.RegisterComponent(c);
}
foreach (var source in container.ComponentRegistry.Sources)
{
builder.RegisterSource(source);
}
return builder.Build();
}
And you can simply change your main method to the following:
static void Main(string[] args)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Test>().As<ITest>();
containerBuilder.RegisterType<Test1Factory>().As<ITestFactory>();
containerBuilder.RegisterType<TestConsumer>().AsSelf();
var container = containerBuilder.Build();
var tc1 = container.Resolve<TestConsumer>();
container = RemoveOldComponents(container);
var cbupdater = new ContainerBuilder();
cbupdater.RegisterType<Test2>().As<ITest>();
cbupdater.RegisterType<Test2Factory>().As<ITestFactory>();
cbupdater.Update(container);
var tc2 = container.Resolve<TestConsumer>();
Console.ReadLine();
}
PS: Wouldn't it be great to have a method which does the exact opposite of PreserveExistingDefaults()
Related
In the code below, I am trying to inject a ViewModel into a View, while the ViewModel requires a Model to wrap and another service that is in the container. The Model is not registered as it is not really a "service".
How do I:
a) not have to provide the IService instance as an argument (let the container resolve it),
b) not have to register a factory for my ViewModels (there will be many)
So what I'm really asking the container to do is treat my Model (that I pass as an argument) as if it were a registered "service" for the duration of this call to GetInstance.
If this is not possible with LightInject, are there any containers out there that have something like this?
public static class Program
{
public static void Main()
{
var container = new LightInject.ServiceContainer();
var service = new Service1();
container.RegisterInstance<IService>(service);
// Have to register the factory
container.Register<IService, PersonModel, PersonViewModel>(
(f, s, p) => new PersonViewModel(s, p));
container.Register<View>();
var person = new PersonModel(); // this is contextual -- not a service.
object view = CreateView(container, typeof(View), service, person);
// ultimate desired code:
//var view = container.GetInstance(typeof(View), new object[] { person });
}
private static object CreateView(ServiceContainer container, Type viewType, IService service, object model)
{
var ctor = viewType.GetConstructors()[0];
var parameters = new List<object>();
foreach (var param in ctor.GetParameters())
{
var attr = param.GetCustomAttributes(typeof(ModelAttribute), false).FirstOrDefault();
if (model != null && attr != null)
{
parameters.Add(model);
}
else
{
parameters.Add(container.GetInstance(param.ParameterType, new object[] { service, model }));
}
}
return Activator.CreateInstance(viewType, parameters.ToArray());
}
}
public interface IService
{
}
public class Service1 : IService
{
}
public class PersonModel
{
}
public class PersonViewModel
{
public PersonModel PersonModel { get; set; }
public PersonViewModel(IService service, [Model] PersonModel person)
{
PersonModel = person;
}
}
public class View
{
public PersonViewModel PersonViewModel { get; set; }
public View(PersonViewModel vm)
{
PersonViewModel = vm;
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class ModelAttribute : Attribute
{
}
I have solved the issues with a combination of techniques...
a) use a Scope and register the ViewModel and View with PerScopeLifetime.
b) use a "ModelTracker" registered with a factory to allow an instance not created by the container to be injected (since models will be created by client code or a DbContext).
This combination also allows me to not register a factory for every ViewModel type -- but instead use the built-in mass registration functions (like RegisterAssembly).
public static class Program
{
public static void Main()
{
var container = new LightInject.ServiceContainer();
container.RegisterInstance<IService>(new Service1());
container.Register<View>(new PerScopeLifetime());
container.Register<PersonViewModel>(new PerScopeLifetime());
container.Register<ModelTracker>(new PerScopeLifetime());
container.Register<PersonModel>((f) => (PersonModel)f.GetInstance<ModelTracker>().Instance);
using (var scope = container.BeginScope())
{
var tracker = scope.GetInstance<ModelTracker>();
tracker.Instance = new PersonModel() { Name = "person1" };
var view = scope.GetInstance<View>();
}
}
}
public class ModelTracker
{
public object Instance { get; set; }
}
public class PersonModel
{
public string Name { get; set; }
}
public class PersonViewModel
{
private readonly IService service;
private readonly PersonModel person;
public PersonViewModel(IService service, PersonModel person)
{
this.service = service;
this.person = person;
}
}
public class View
{
public PersonViewModel PersonViewModel { get; set; }
public View(PersonViewModel vm)
{
PersonViewModel = vm;
}
}
public interface IService { }
public class Service1 : IService { }
I want to use IOC with my service and I want to instead inject a class not an interface in the constructor as below in the services layer but I do not want to create a new object from the calling layer like var service = new InvoiceService(new ChangeInvoiceDueDateCommand()) instead I want to create something like this from my controller in MVC where the IInvoiceService is injected into the controller constructor but the problem I see is that
public InvoiceController(IInvoiceService invoiceService, IMapper mapper)
{
_invoiceService = invoiceService;
_mapper = mapper;
}
and then called like this
public ActionResult ChangeInvoiceDueDate(InvoiceChangeDueDateViewModel invoiceChangeDueDateViewModel )
{
var request = _mapper.Map<InvoiceChangeDueDateViewModel, ChangeInvoiceDuedateRequest>(invoiceChangeDueDateViewModel);
InvoiceChangeDueDateResponse response = _invoiceService.ChangeDueDate(request);
return View();
}
Service Layer
public class InvoiceService : IInvoiceService
{
private readonly ChangeInvoiceDueDateCommand _changeInvoiceDueDateCommand;
public InvoiceService(ChangeInvoiceDueDateCommand changeInvoiceDueDateCommand)
{
_changeInvoiceDueDateCommand = changeInvoiceDueDateCommand;
}
public InvoiceChangeDueDateResponse ChangeDueDate(ChangeInvoiceDuedateRequest invoiceChangeDueDateRequest)
{
_changeInvoiceDueDateCommand.Execute(invoiceChangeDueDateRequest);
return new InvoiceChangeDueDateResponse {Status = new Status()};
}
}
Command
public class ChangeInvoiceDueDateCommand : ICommand<ChangeInvoiceDuedateRequest>
{
private readonly IRepository<Invoice> _invoiceRepository;
readonly InvoiceDueDateChangeValidator _validator;
public ChangeInvoiceDueDateCommand(IRepository<Invoice> invoiceRepository)
{
_invoiceRepository = invoiceRepository;
_validator = new InvoiceDueDateChangeValidator();
}
public void Execute(ChangeInvoiceDuedateRequest request)
{
if (_validator.IsDuedateValid(request.NewDuedate))
{
Invoice invoice = _invoiceRepository.GetById(request.Id);
invoice.ChangedDueDate(request.NewDuedate);
_invoiceRepository.SaveOrUpdate(invoice);
}
else
{
throw new InvalidDueDateException();
}
}
}
ICommand
public interface ICommand<T> where T : IRequest
{
void Execute(T request);
}
IRequest
public interface IRequest
{
int Id { get; set; }
}
I worked it out. It was just a Windsor syntax issue. It ended up being as simple as registering the Command using the container.Register(Component.For<ChangeInvoiceDueDateCommand>());
I am trying to evaluate the scoping of Autofac and as I understand it, when an instance has been declared as InstancePerLifetimeScope, then within the using(container.BeginLifetimeScope()) block, we should get the same instance. But in another such block, we should get a different instance. But my code (in linqpad) gives me the same instance. Windsor's lifestylescope however works as I think it should.
Code:
static IContainer glkernel;
void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<Controller>();
builder.RegisterType<A>().As<IInterface>().InstancePerLifetimeScope();
glkernel = builder.Build();
using (glkernel.BeginLifetimeScope()){
Controller c1 = glkernel.Resolve<Controller>();
c1.GetA();//should get instance 1
c1.GetA();//should get instance 1
}
using (glkernel.BeginLifetimeScope()){
Controller d = glkernel.Resolve<Controller>();
d.GetA();//should get instance 2
d.GetA();//should get instance 2
}
}
public interface IInterface
{
void DoWork(string s);
}
public class A : IInterface
{
public A()
{
ID = "AAA-"+Guid.NewGuid().ToString().Substring(1,4);
}
public string ID { get; set; }
public string Name { get; set; }
public void DoWork(string s)
{
Display(ID,"working...."+s);
}
}
public static void Display(string id, string mesg)
{
mesg.Dump(id);
}
public class Controller
{
public Controller()
{
("controller ins").Dump();
}
public void GetA()
{
//IInterface a = _kernel.Resolve<IInterface>();
foreach(IInterface a in glkernel.Resolve<IEnumerable<IInterface>>())
{
a.DoWork("from A");
}
}
}
The output is:
controller ins
AAA-04a0
working....from A
AAA-04a0
working....from A
controller ins
AAA-04a0
working....from A
AAA-04a0
working....from A
Perhaps my understanding of scoping is wrong. If so, can you please explain.
What do I have to do to get a different instance in the second block?
The problem is you're resolving things out of the container - the glkernel instead of out of the lifetime scope. A container is a lifetime scope - the root lifetime scope.
Resolve out of the lifetime scope instead. That may mean you need to change up your controller to pass in the list of components rather than using service location.
public class Controller
{
private IEnumerable<IInterface> _interfaces;
public Controller(IEnumerable<IInterface> interfaces)
{
this._interfaces = interfaces;
("controller ins").Dump();
}
public void GetA()
{
foreach(IInterface a in this._interfaces)
{
a.DoWork("from A");
}
}
}
Then it's easy enough to switch your resolution code.
using (var scope1 = glkernel.BeginLifetimeScope()){
Controller c1 = scope1.Resolve<Controller>();
c1.GetA();
c1.GetA();
}
using (var scope2 = glkernel.BeginLifetimeScope()){
Controller c2 = scope2.Resolve<Controller>();
c2.GetA();
c2.GetA();
}
The Autofac wiki has some good information on lifetime scopes you might want to check out.
I am using unit testing first time.
I have created the class 'CAR' using entity framework.
I have following code in my project
namespace EntityFrameworkExample
{
class Program
{
static void Main(string[] args)
{
public static void AddCar()
{
SampleDBEntities db = new SampleDBEntities();
Car car = new Car();
car.Brand = "Ford";
car.Model = "Mastang";
db.Cars.AddObject(car);
db.SaveChanges();
Console.WriteLine("Record Saved");
Console.ReadLine();
}
}
}
}
Now I want to perform unit testing on this method.
I have added unit test project as well added ref of above project to it...
but I am confused abt how to call it ?
I have writen below code
namespace UnitTest
{
/// <summary>
/// Summary description for UnitTest1
/// </summary>
[TestClass]
public class UnitTest1
{
[TestMethod]
public void AddCar()
{
SampleDBEntities e = new SampleDBEntities();
//what to do next ? ////////////////////////////////
}
}
}
I am confused abt next step what should I write ?
You should put the AddCar-method outside the Main-method to be able to call it. I would make a new class for the Car-functionality, outside of the Program class. Then you can call that class without having to interact with the program in your test.
For unit testing, do "Arrange", "Act", "Assert" inside your test method.
Arrange:
var cars = new Cars();
Act:
cars.AddCar("Ford", "Mustang");
Assert:
//check that car is in the database
var myCar = cars.GetCar("Ford", "Mustang");
Assert.IsNotNull(myCar);
To be able to test it, you should wipe the database before each test run.
edit for clarification:
namespace EntityFrameworkExample
{
class Program
{
static void Main(string[] args)
{
var cars = new Cars();
cars.AddCar("Ford", "Mustang");
}
}
public class Cars
{
public static void AddCar(string brand, string model)
{
SampleDBEntities db = new SampleDBEntities();
Car car = new Car();
car.Brand = brand;
car.Model = model;
db.Cars.AddObject(car);
db.SaveChanges();
Console.WriteLine("Record Saved");
Console.ReadLine();
}
public static Car GetCar(brand, model)
{
using(var db = new SampleDBEntities())
{
var car = from car in db.Cars
where car.Brand == brand
&& car.Model == model
select car;
return car.FirstOrDefault();
}
}
}
}
This is what I've got:
public interface INamed
{
string Name { get; }
}
public interface IService
{
}
public class Service : IService
{
}
public class ServiceUser
{
public ServiceUser(IEnumerable<Lazy<IService, INamed>> services)
{
var cnt = services.Count(); // Always 0.
}
}
var builder = new ContainerBuilder();
builder.Register(c => new Service())
.As<IService>()
.WithMetadata<INamed>(m => m.For(n => n.Name, "Test"));
builder.RegisterType<ServiceUser>();
var container = builder.Build();
var su = container.Resolve<ServiceUser>();
The collection of Lazy<IService, INamed> services in the ServiceUser ctor is always empty. Can you explain what I'm doing wrong? One thing I immediately don't understand is where the concrete implementation of INamed is coming from. I guess Autofac generates that internally? Please help clear things up for me.
Upgrade to the latest Autofac.