How to inject a new object every time it's needed? - structuremap

I have this constructor:
public GroupController(IUserService userService, IGroupService groupService)
{
_groupService = groupService;
_userService = userService;
}
userService and groupService has the same object in constructor:
public UserService(IDb db)
{
_db = db;
}
public GroupService(IDb db)
{
_db = db;
}
and my container is this:
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
x.For<IDb>().Use<Db>();
x.For<IUserService>().Use<UserService>();
x.For<IGroupService>().Use<GroupService>();
});
So, How can I inject an IDb for UserService and an another IDb for GroupService because I can't use the same instance.

The default lifecycle for StructureMap objects is PerRequestLifecycle, which means that multiple requests for the same interface within the same build session will resolve to a single instance. If you want it to resolve each interface request to a unique instance, you can use the UniquePerRequestLifecycle.
TL;DR: Configure your IDb like this:
x.For<IDb>.AlwaysUnique().Use<Db>();

Related

Update autofac container from IHostedService asynchronously on app startup

I am having an ASP.net core 3.0 app and I want to see if I can register some of my Orleans Cluster Clients asynchronously on app startup, due to the fact the creation and making the connections to Orleans Cluster are heavy. According to this article I created my own IHostedService, but when I implemented startAsync method I am not sure how to get the autofac container which I am using in Startup.cs and update it with my clients registrations. I have read this but see my below code, still I don't see the clients are getting registered. Is it doable or am I missing anything here? thanks!
Startup.cs
...
public static IServiceProvider ConfigureServices(IServiceCollection services)
{
var coreBuilder = new ContainerBuilder();
// other autofac registrations...
services.AddHostedService<MyService>();
coreBuilder.populate(services);
var container = coreBuilder.Build();
var serviceProvider = new AutofacServiceProvider(container);
return serviceProvider;
}
MyService.cs
public MyService : IHostedService
{
private readonly IServiceProvider _serviceProvider;
public MyService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// get the autofac container from Startup.cs and update with cluster client registrations?
using(var scope = this._serviceProvider.GetRequiredService<ILifeTimeScope>()
.BeginLifeTimeScope(builder => do registration here...)) {}
}
// noop
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
You cannot update the DI container on-the-fly like that. Once it's built, it's built.
You have another option: make a factory class that caches the clients, initialize them in the background, then retrieve them from the factory.
class MyService
{
// ...
}
class MyServiceFactory
{
private ConcurrentDictionary<string, MyService> _instances = new ConcurrentDictionary<string, MyService>();
public async Task<MyService> CreateAsync(string key)
{
if (_instances.TryGetValue(key, out var service))
{
return service;
}
// perform expensive initialization
// ...
service = new MyService();
_instances[key] = service;
return service;
}
}
class MyServiceInitializer: BackgroundService
{
private MyServiceFactory _serviceFactory;
public MyServiceInitializer(MyServiceFactory serviceFactory)
{
_serviceFactory = serviceFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await _serviceFactory.CreateAsync("first instance");
await _serviceFactory.CreateAsync("second instance");
}
}
Register the factory as singleton, (or make Instances a static property).
services.AddSingleton<MyServiceFactory>();
services.AddHostedService<MyServiceInitializer>();
Then resolve an instance you need. It will resolve instantly, because it's been initialized in the background.
class MyController
{
private MyServiceFactory _serviceFactory;
public MyController(MyServiceFactory serviceFactory)
{
_serviceFactory = serviceFactory;
}
[HttpGet]
public async Task<ActionResult> Index()
{
var service = await _serviceFactory.CreateAsync("first instance");
// use the service
}
}

Using Unity DI with HostBuilder based .NET Core console application

With ASP.NET MVC Core it is possible to use the Unity DI packages by loading the appropriate NuGet packages (Unity.Container and Unity.Microsoft.DependencyInjection) and then calling the UseUnityServiceProvider() extension method when building the web host in Program.cs. This extension method is based off of the IWebHostBuilder interface. After initializing the host it is possible to access the Unity DI functionality via the .NET Core GetService interface and any constructor injection.
I'm working on a console based application that will use HostBuilder and the IHostBuilder interface. I've tried reimplementing the registration logic from the UseUnityServiceProvider() extension (available here: https://github.com/unitycontainer/microsoft-dependency-injection/blob/master/src/HostingExtension.cs) as part of the ConfigureServices() method call against IHostBuilder, but the change in service provider does not appear to be visible to downstream DI calls.
Has anyone been successful in getting Unity to work with the Microsoft DI methods in an application created using HostBuilder?
Update As requested, here is an example of what I was trying (taken from the Unity source). Of course, this does not work. TestService has a constructor which should be injected with an object defined in MyUnityExtension. This does not happen.
private static async Task MainTest()
{
var container = new UnityContainer().AddNewExtension<MyUnityExtension>();
var factory = new ServiceProviderFactory(container);
var hostBuilder = new HostBuilder()
.ConfigureServices((hostBuilderContext, services) =>
{
services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IUnityContainer>>(factory));
services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(factory));
services.AddHostedService<TestService>();
});
await hostBuilder.RunConsoleAsync();
}
Not sure what you want to achieve with new UnityContainer().AddNewExtension<MyUnityExtension>().
If you just want some registered service to be injected into TestService, why not just use IUnityContainer.RegisterType<TInterface, TImplementation>() ?
Here is a working IHost setup in Program.cs (.NET Core 3.1, Microsoft.Extensions.Hosting v3.10, Unity.Microsoft.DependencyInjection v5.11.5):
public static async Task Main(string[] args)
{
var container = new UnityContainer();
container.RegisterType<IService, MyService>();
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<TestService>();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConsole();
})
.UseUnityServiceProvider(container);
await builder.RunConsoleAsync();
}
The interface:
public interface IService
{
string Name { get; }
}
The implementation:
public class MyService : IService
{
public string Name => "My name";
}
The TestService:
public class TestService : IHostedService
{
private readonly IService service;
private readonly ILogger logger;
public TestService(IService service, ILogger<TestService> logger)
{
this.service = service;
this.logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
this.logger.LogInformation("Hello {n}", this.service.Name);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

DbContext instance not changing with dependency injection

In my application, I have an API and a worker, that both need to use my database. I have a repository layer, that is accessed by both of these.
I use DI in my application, and I inject my dbContext in my repositories.
While I expect my repositories to use a new dbContext on every request, the instance seems to always be the same.
Since I have a background worker, which is a singleton, I was not able to use the default scoped lifetime of my dbContext. Therefore I have added my dbContext as transient in both my worker and my API. I have added an instanceId to my dbContext, which is set in my constructor.
Constructor of dbcontext:
public CatAPIDbContext()
{
InstanceId = Guid.NewGuid();
Database.EnsureCreated();
}
Worker configureservices:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Transient);
services.AddTransient(typeof(IFeedingProfileRepository), typeof(FeedingProfileRepository));
services.AddTransient(typeof(IFeedingTimesRepository), typeof(FeedingTimesRepository));
services.AddTransient(typeof(IFeedHistoryRepository), typeof(FeedHistoryRepository));
services.AddTransient(typeof(IMotorController), typeof(MotorController));
services.AddTransient(typeof(IFoodDispenser), typeof(FoodDispenser));
services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddTransient(typeof(IFeedingTimeChecker), typeof(FeedingTimeChecker));
services.AddHostedService<Worker>();
});
API ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddDbContext<CatAPIDbContext>();
services.AddTransient(typeof(IFeedingProfileRepository), typeof(FeedingProfileRepository));
services.AddTransient(typeof(IFeedingTimesRepository), typeof(FeedingTimesRepository));
services.AddTransient(typeof(IFeedHistoryRepository), typeof(FeedHistoryRepository));
services.AddTransient(typeof(IMotorController), typeof(MotorController));
services.AddTransient(typeof(IFoodDispenser), typeof(FoodDispenser));
services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
}
GenericRepo example:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
public CatAPIDbContext _dbContext { get; set; }
public GenericRepository(CatAPIDbContext dbContext)
{
_dbContext = dbContext;
}
public T GetById(object id)
{
return _dbContext.Set<T>().Find(id);
}
}
Repo that I use and does not get latest state example:
public class FeedingProfileRepository :
GenericRepository<FeedingProfile>, IFeedingProfileRepository
{
public FeedingProfileRepository(CatAPIDbContext dbContext) :
base(dbContext)
{
}
public FeedingProfile GetCurrentFeedingProfile()
{
var profile = _dbContext.FeedingProfiles
.Include(x => x.TimesToFeedOn)
.Where(x => x.CurrentlyActive == true).FirstOrDefault();
if (profile == null)
{
return null;
}
if (profile.TimesToFeedOn != null)
{
profile.TimesToFeedOn = profile.TimesToFeedOn.OrderByDescending(x => x.Time).ToList();
}
return profile;
}
}
When thw orker calls FeedingProfileRepository.GetCurrentFeedingProfile() I check the instanceId of the dbContext, and it is always the same during the entire lifetime of my application. As a result, the data I retrieve from the dbContext is outdated, and does not match the current state of the database, since the dbContext never gets disposed. Am I doing something wrong?
As I see in your code you've made dbContext Transient and it means that it is created a new instance every time they are injected or requested:
services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Transient);
if you want to have a single instance for all of the requests, make it singleton try to change your code like below:
services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Singleton);
But if you want to have a single instance for every request to application try to use scoped lifetime:
services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Scoped);

SaveChanges() and adding to DB not working

I am working on adding Entity Framework to our web app, asp.net MVC 5, but I am having a hardtime saving changes and adding to the database. I set up UnitOfWork with a generic BaseRepository, and I have tried a few things attempting to get this to work. first, I thought I could inject, with AutoFac, my repo in UnitOfWork like so
public UnitOfWork(IServiceItem serviceItem
, ITechServiceItem techServiceItem
, ITechnicianTime technicianTime
, ISproc sproc
, IRepairOrder repairOrder
, ICustomer customer
, IRepairOrderStatus repairOrderStatus
, IRepairOrderUnit repairOrderUnit
, IFiles files
, IPartInventory partInventory
, IRepairOrderItems repairOrderItems
)
{
RepairOrderItems = repairOrderItems;
PartInventory = partInventory;
Files = files;
RepairOrderUnit = repairOrderUnit;
RepairOrderStatus = repairOrderStatus;
RepairOrder = repairOrder;
Customer = customer;
Sproc = sproc;
ServiceItem = serviceItem;
TechServiceItem = techServiceItem;
TechnicianTime = technicianTime;
}
and my BaseRepo is like
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DataDbContext _db;
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DataDbContext _db;
internal void GetData()
{
if (_db == null)
{
string accountNumber = HttpContext.Current.User.Identity.GetCompanyAccountNumber();
var connectionToken = ConfigurationManager.AppSettings["LoginSplitToken"];
_db = new DataDbContext(ConfigurationManager.ConnectionStrings["NameOfConnString"].ConnectionString.Replace(connectionToken, accountNumber));
}
}
public TEntity Get(int id)
{
return _db.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return _db.Set<TEntity>().ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _db.Set<TEntity>().Where(predicate);
}
public void Add(TEntity entity)
{
_db.Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
_db.Set<TEntity>().AddRange(entities);
}
public void Remove(TEntity entity)
{
_db.Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
_db.Set<TEntity>().RemoveRange(entities);
}
public int CompleteData()
{
return _db.SaveChanges();
}
public TEntity Get(int id)
{
return _db.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return _db.Set<TEntity>().ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _db.Set<TEntity>().Where(predicate);
}
public void Add(TEntity entity)
{
_db.Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
_db.Set<TEntity>().AddRange(entities);
}
public void Remove(TEntity entity)
{
_db.Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
_db.Set<TEntity>().RemoveRange(entities);
}
public int CompleteData()
{
return _db.SaveChanges();
}
}
and my StartUp.Configuration
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
HttpConfiguration config = GlobalConfiguration.Configuration;
// REGISTER DEPENDENCIES
builder.RegisterType<EverLogicDbContext>().AsSelf().InstancePerRequest();
builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest();
builder.RegisterType<ApplicationSignInManager>().AsSelf().InstancePerRequest();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).InstancePerRequest();
builder.Register(c => HttpContext.Current.User).InstancePerRequest();
builder.Register(c => app.GetDataProtectionProvider()).InstancePerRequest();
builder.RegisterType<ApplicationUserStore>().As<IUserStore<EverLogicMamber, int>>()
.WithParameter(new TypedParameter(typeof(ISecurityOfWork), new SecurityOfWork(new SecurityDbContext())))
.InstancePerRequest();
//Database
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
builder.RegisterType<SecurityOfWork>().As<ISecurityOfWork>().InstancePerRequest();
//Service
builder.RegisterType<TechnicianTimeService>().As<ITechnicianTimeService>().InstancePerRequest();
builder.RegisterType<PartService>().As<IPartService>().InstancePerRequest();
builder.RegisterType<TechServiceItemService>().As<ITechServiceItemService>().InstancePerRequest();
//Repo
builder.RegisterType<Company>().As<ICompany>().InstancePerRequest();
builder.RegisterType<Views>().As<IViews>().InstancePerRequest();
builder.RegisterType<RepairOrderItems>().As<IRepairOrderItems>().InstancePerRequest();
builder.RegisterType<PartInventory>().As<IPartInventory>().InstancePerRequest();
builder.RegisterType<Files>().As<IFiles>().InstancePerRequest();
builder.RegisterType<TechDashboardService>().As<ITechDashboardService>().InstancePerRequest();
builder.RegisterType<RepairOrderUnit>().As<IRepairOrderUnit>().InstancePerRequest();
builder.RegisterType<RepairOrderStatus>().As<IRepairOrderStatus>().InstancePerRequest();
builder.RegisterType<Customer>().As<ICustomer>().InstancePerRequest();
builder.RegisterType<ServiceItem>().As<IServiceItem>().InstancePerRequest();
builder.RegisterType<RepairOrder>().As<IRepairOrder>().InstancePerRequest();
builder.RegisterType<Sproc>().As<ISproc>().InstancePerRequest();
builder.RegisterType<TechServiceItem>().As<ITechServiceItem>().InstancePerRequest();
builder.RegisterType<TechnicianTime>().As<ITechnicianTime>().InstancePerRequest();
// REGISTER CONTROLLERS SO DEPENDENCIES ARE CONSTRUCTOR INJECTED
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(config);
builder.RegisterWebApiModelBinderProvider();
var container = builder.Build();
// REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
ConfigureAuth(app);
}
But with this set up, the database does not update or add new entitys.
Then i tryed removing Dependcy injection from UnitOfWork and set UnitOfWork up like
protected DataDbContext _db;
public UnitOfWork(DataDbContext context)
{
GetData();
RepairOrderItems = new RepairOrderItems(_db);
PartInventory = new PartInventory(_db);
Files = new Files(_db);
RepairOrderUnit = new RepairOrderUnit(_db);
RepairOrderStatus = new RepairOrderStatus(_db);
RepairOrder = new RepairOrder(_db);
Customer = new Customer(_db);
Sproc = new Sproc(_db);
ServiceItem = new ServiceItem(_db);
TechServiceItem = new TechServiceItem(_db);
TechnicianTime = new TechnicianTime(_db);
}
internal void GetData()
{
if (_db == null)
{
string accountNumber = HttpContext.Current.User.Identity.GetCompanyAccountNumber();
var connectionToken = ConfigurationManager.AppSettings["LoginSplitToken"];
_db = new DataDbContext(ConfigurationManager.ConnectionStrings["NameOfConnString"].ConnectionString.Replace(connectionToken, accountNumber));
}
}
and moving SaveChanges from the BaseRepo to UnitOfWork, but still nothing is saving or adding to the database.
What am i missing????
TL;DR the problem is that all your repositories are using separate, independent DbContexts, so the DbContext injected into your UnitOfWork has no pending changes when you call SaveChanges on it, so that's why you aren't seeing any change to the database.
In order for the Unit of Work to function correctly, your UnitOfWork class, and all the repository classes which your code needs to perform data persistence, must all share the same DbContext instance. In your code, it's clear that each repository has a factory method to create it's own, independent DbContext instance.
Remove the GetData() factory method from your BaseRepository class, and instead, require an instance of your EverLogicDbContext instance to injected to the constructor of BaseRepository by AutoFac. This will require that all your Repository subclasses also need to have a constructor accepting this same EverLogicDbContext.
As per your last edit, the UnitOfWork class must accept the same, shared EverLogicDbContext that the repositories use. Since you've tagged with asp.net-mvc then RequestPerInstance lifetime scope is correct for your scenario.
Your UnitOfWork class needs to control the SaveChanges(Async) method, so remove the CompleteData method from the BaseRepository class.
As you already seem to have done, the DbContext needs to be registered InstancePerRequest:
builder.RegisterType<EverLogicDbContext>().AsSelf().InstancePerRequest();
If all this is tied together correctly:
AutoFac will create an instance of your concrete DbContext the first time it is needed during processing of each Request.
All Repositories will then share the same DbContext instance for the lifetime of the Request, and the DbContext will track interim changes made by your services.
The UnitOfWork injected into your main "business logic" (e.g. Controller, or Orchestrator / Handler) will then be able to Commit the actions taken by simply calling SaveChangesAsync on the shared DbContext. This will all happen under a single database connection, so will be a lightweight transaction.
As per other comments above, IMO Entity Framework is a already high level framework with transactional support built-in, so there's little point in over-engineering a "UnitOfWork" pattern if all the ACID activity will be conducted against the same Database (and can be wrapped into the same DbContext).

Use Unity to resolve current NHibernate session for injection into Repositories

I have a multi-project MVC 5 solution, where NHibernate repositories are declared in a Core.Data class library, but my session management is in the Wen API Core.Api project. It creates and destroys a session per request:
public override void OnActionExecuting(HttpActionContext actionContext)
{
// start a session
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// commit the current session
var session = SessionFactory.GetCurrentSession();
var transaction = session.Transaction;
if (transaction != null && transaction.IsActive)
{
transaction.Commit();
}
session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
}
Now when I instantiate a repository in a controller action,I would like this particular session to be injected into the repository. How can I achieve this? I can do a BaseRepository<T>: IRepository<T>, with a constructor that finds the session, but I would really much rather like it injected. How can I achieve this?
This is a snippet from how we do it.
public class UnityConfig
{
private static readonly Lazy<IUnityContainer> _container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
//easy access to the container from anywhere in the application
public static T Resolve<T>()
{
//uses the Resolve<T> extension method
return GetConfiguredContainer().Resolve<T>();
}
private static void Register(IUnityContainer container)
{
container.RegisterType<IRepository, Repository>(
new InjectionConstructor(new ResolvedParameter<ISession>()));
container.RegisterType<ISession>(new PerRequestLifetimeManager(),
new InjectionFactory(c =>
c.Resolve<ISessionFactory>().OpenSession()
));
container
.RegisterType<ISessionFactory>(
new ContainerControlledLifetimeManager(),
new InjectionFactory(c =>
{
var v =
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(d => d.FromConnectionStringWithKey("web.config.connection.string.key"))
.ShowSql()
.Dialect<CustomOcMsSqlDialect>())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<IRepository>()
.Conventions.AddFromAssemblyOf<IRepository>())
.BuildSessionFactory();
return v;
})
);
}
}
public interface IRepository
{
}
public class Repository : IRepository
{
private readonly ISession _session;
public Repository(ISession session)
{
_session = session;
}
}
public class SomeController : Controller
{
public ActionResult SomeAction()
{
var repo = UnityConfig.Resolve<IRepository>();
var dbEntity = repo.Load(123);
return View("SomeView");
}
}
We wire up the repository, the session and the session-factory through unity. The factory is set to ContainerControlled(unity singleton). The session is set to PerRequest, so we get a new session for each request. And the repository uses the regular lifetime-manager so we get a new one for each resolve.
This way you can ask unity for a repository and get the same session throughout the entire request. It will also dispose the session automatically at the end of the request. But I'm sure you could hook into the ApplicationEndRequest event and do some housecleaning as well if you wanted.
Hope this helps!

Resources