I'm new with using structuremap and I'm not sure if I configured IoC.cs properly.
I have an ASP.Net Project and used Unit Of Work. this is how my IoC.cs file looks like:
IoC.cs
namespace Portal.Web.DependencyResolution {
using Portal.Data.Context;
using Portal.Service;
using Portal.Service.Interface;
using StructureMap;
using StructureMap.Pipeline;
public static class IoC {
public static IContainer Initialize() {
return new Container(c => {
c.AddRegistry<DefaultRegistry>();
c.For<IUnitOfWork>().LifecycleIs(Lifecycles.Singleton).Use<PortalDbContext>();
c.For<IAccount>().Use<AccountService>();
c.For<IStandard>().Use<StandardService>();
});
}
}
}
with this configuration this error occures some times
System.Data.SqlClient.SqlException: New transaction is not allowed because there are other threads running in the session.
I already have read many other SO questions with same topic as error above and they all suggest to use .toList() and enumerate but I guess that is not my problem.
so for summarize I'd like to use Unit Of Work + Structure Map 3 DI in ASP MVC5, how I configure my IoC.cs
It is very bad idea to use singleton for dbcontext in web application.
You should consider to change this to PerHttpRequest lifecycle (if it exists in structuremap) or PerResolve lifecycle:
Per HttpRequest:
For<IUnitOfWork>().LifecycleIs(new HttpContextLifecycle()).Use<PortalDbContext>();
Update:
in this case, you should not use your IUnitOfWork in using statement and lay responsibility of disposing it on your DI container.
Per Resolve:
For<IUnitOfWork>().Use<PortalDbContext>();
Related
i'm building a small webapi to work in conjunction with additional functionalities running in the background.
In the specific case I have a class called TelegramBot:
public class TelegramBot
{
static ITelegramBotClient botClient;
private readonly BotManagerContext _botManagerContext;
public TelegramBot(BotManagerContext botManagerContext)
{
_botManagerContext = botManagerContext;
botClient = new TelegramBotClient("xxxx:yyyyy");
botClient.OnMessage += Bot_OnMessage;
botClient.StartReceiving();
}
That I'm trying to run together with the web api. BotManagerContext is a DbContext initialized in the web api, i'm trying to retrieve it using dependency injection - so i'm trying to add the TelegramBot class into the Startup.cs file so that it starts as a Singleton and can retrieve the dbcontext
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BotManagerContext>(opt =>
opt.UseSqlite("Data Source=BotManager.db"));
services.AddControllers();
services.AddSingleton<TelegramBot>();
}
Question is - how do I implement this? using an interface? I'm fairly new to this and I don't know how to implement it :)
Thanks
Implementing your own IHostedService would be the best way to go about this. For getting the dbcontext in the service you can use IserviceProvider as your dependency. Serviceprovider will give you the dbcontext.
You can configure your custom hosted service to be added as a singleton then. Check this documentation for details:
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/background-tasks-with-ihostedservice#implementing-ihostedservice-with-a-custom-hosted-service-class-deriving-from-the-backgroundservice-base-class
I do not have any problems for example here:
using FooContext context = new FooContext();
context.RemoveRange(context.FooTable);
context.SaveChanges();
But I DO get ObjectDisposedException here when I use 'using directive' either with block (with {}, as before C# 8.0) either by writing the new form, without {}.
DbSet<FooThing> allResults;
using (FooContext context = new FooContext())
{
allResults = context.FooTable;
}
return allResults;
And again, no problems with the following:
FooContext context = new FooContext();
return context.FooTable;
What is the problem with my code and how could I use using directive without exceptions?
EDIT:
Now I'm trying to use DI.
I created a scaffolded item by choosing "...using Entity Framwork" in the menu in VS.
In the class that was created by VS, I have an instance of the DbContext class at the top:
private readonly FooContext _context;
public FooController(FooContext context)
{
_context = context;
}
I can use this object, it seems there is no problem with it.
But now I get:
System.InvalidOperationException: Unable to resolve service for type '<Namespaces of my app here>.FooContext' while attempting to activate '<Namespaces of my app here>.FooController'.
I think that is because I did not injected the type to the appropriate container.
Where should I do this?
Should I do something here?
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
A DbSet<T>, which is what FooTable is an instance of, is a table accessor. You return this instance is (which then used by something we assume) but the associated DbContext instance on which this instance relies on was disposed by your using block.
If you are using asp.net you should look into using a Dependency Injection framework (like AutoFac). You can then register your DbContext type, have it injected in your Controller, and AutoFac will dispose the DbContext instance at the end of the request.
Your context is disposed when it exits the using statement. There would normally be no problem with your allResults, but I would guess that you are trying to use a navigation property or related item on the results. Your code will attempt to retrieve the data, but then it gets blocked because the context that was used to retrieve the data has been disposed.
So far it seems that this solves my current problem:
EF Core how to get a DBContext in a class instance?
Thank you, Igor for mentioning DI!
Finally I found this:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.0
Here's my scenario:
I Have a single app, but I need to switch the database connection by route.
Example:
switch(route)
{
case(URL/A):
{
USE DATABASE 1
}
case(URL/B):
{
USE DATABASE 2
}
DEFAULT:
USE DATABASE DEFAULT
}
Is it possible?
Since you're using ASP.NET MVC, your routes depends on your controllers. Then you can imagine having ControllerA using DatabaseA and ControllerB using DatabaseB.
To use multiple database connections, you need a connection string for each one of them.
I would use the following pieces of code to inject instances of DbContextOptionsBuilder inside of Startup.ConfigureServices()
var ContextAOptionsBuilder = new DbContextOptionsBuilder<ContextA>();
var ContextBOptionsBuilder = new DbContextOptionsBuilder<ContextB>();
Then you can configure your builders this way (depending on your parameters)
ContextAOptionsBuilder.UseSqlServer(Configuration.GetConnectionString("ContextAConnectionString"), builder =>
{
builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(30), null);
});
ContextAOptionsBuilder.EnableSensitiveDataLogging();
Then you can inject them as singletons this way :
services.AddSingleton(typeof(DbContextOptionsBuilder<ContextA>),ContextAOptionsBuilder);
You can use a BaseController, whose constructor parameters can access to services this way :
public BaseController(IConfiguration configuration, IMemoryCache memoryCache,
IHttpContextAccessor contextAccessor,
DbContextOptionsBuilder<ContextA> ContextAOptionsBuilder,
DbContextOptionsBuilder<ContextB> ContextBOptionsBuilder){}
Of course, ControllerA and ControllerB being heir classes of BaseController, you can access desired builder quite simply.
public ControllerA(IConfiguration configuration,
IMemoryCache cache,
IHttpContextAccessor contextAccessor,
DbContextOptionsBuilder<ContextA> ContextAOptionsBuilder,
DbContextOptionsBuilder<ContextB> ContextBOptionsBuilder)
:base(configuration, cache, contextAccessor, ContextAOptionsBuilder,ContextBOptionsBuilder)
{
//Create your DbContext using the builder
}
This way you can use one, the other, or both database to build your context
A simpler way would have been injecting your configuration file and building your context from it's content but ppumkin's comment suggested it's a bad idea to do this at a controller level.
This solution is working for me in an ASP.NET Core MVC application, I am still learning the framework but maybe my answer gave you precisions about multiple DbContexts.
You can create 3 connection string also 3 data access Classes. First of your class uses for example DropCreateDatabaseIfModelChanges others use CreateDatabaseIfNotExists. When you call first class your database creates when you need others there will no need recreate it.
Register your context (as scoped, per request) and use factory method for dynamically creating context with specified connection string based on current route (which should be available from HttpContext or something similar). If the databases schemas are same and just data is different this should work easily. I can't provide a snippet for you because it's mostly depends on what DI framework you have.
I have an ASP.NET MVC application using StructureMap.
I have created a service called SecurityContext which has a static Current property. A simplified version looks like this:
public class SecurityContext : ISecurityContext
{
public bool MyProperty { get; private set; }
public static SecurityContext Current
{
get
{
return new SecurityContext() { MyProperty = true };
}
}
}
I've hooked this up in my StructureMap registry as follows:
For<ISecurityContext>().Use(() => SecurityContext.Current);
My understanding of this Linq expression overload of the Use method is that the returned concrete object is the same for the entire HTTP request scope.
However, I've set up a test case where my context interface is injected in two places, once in the controller's constructor and again using the SetterProperty attribute in the base class my view inherits from.
When debugging I observe the Current static method being hit twice so clearly my assumptions are wrong. Can anyone correct what I'm doing here? The reason I want this request-scoped is because I'm loading certain data into my context class from the database so I don't want this to happen multiple times for a given page load.
Thanks in advance.
The default lifecycle for a configuration is Transient, thus each request for an ISecurityContext will create a new instance of SecurityContext. What I think you want is to use the legacy HttpContext lifecycle.
Include the StructureMap.Web nuget package. Then change your configuration to the following:
For<ISecurityContext>()
.Use(() => SecurityContext.Current)
.LifeCycleIs<HttpContextLifecycle>();
More information on lifecyles can be found here.
The HttpContextLifecycle is obsolete, however I do not know if or when it will be removed. The StructureMap team does recommend against using this older ASP.Net lifecycle. They state in the documentation that most modern web frameworks use a nested container per request to accomplish the same scoping. Information about nested containers can be found here.
I don't know if the version of ASP.Net MVC you are using is considered a modern web framework. I doubt it is because ASP.Net Core 1.0 is the really the first in the ASP.Net line to fully embrace the use of DI. However, I will defer to #jeremydmiller on this one.
In ServiceStack application, I have Funq configured to inject a session per request like this:
container.Register<NHibernate.ISessionFactory>(sessionFactoryForDB1);
container.Register<NHibernate.ISession>(c => c.Resolve<NHibernate.ISessionFactory>()
.OpenSession())
.ReusedWithin(Funq.ReuseScope.Request);
My service looks like this, and it works just fine:
public class MyNhAwareService : Service
{
public ISession Session { get; set; }
public object Any(DoSomething request)
{
...
}
}
Now, the problem comes in when I want to add a second NHibernate database into the mix with its own session factory:
container.Register<NHibernate.ISessionFactory>(sessionFactoryForDB1);
container.Register<NHibernate.ISession>(c => c.Resolve<NHibernate.ISessionFactory>()
.OpenSession())
.ReusedWithin(Funq.ReuseScope.Request);
// add a different session factory
container.Register<NHibernate.ISessionFactory>(sessionFactoryForDB2);
I've been experimenting with a variety of ways Funq can be used, and I thought I had found the way forward when I discovered the 'RegisterNamed()" method, but that still doesn't help, as I can't use anything except TryResolve() from within my service.
This seems like it should be possible, but I'm beating my head against the wall trying to work it out...Any advice would be greatly appreciated.
You have a couple ways of going about this.
Option 1: Unique Interfaces
This option is to create a distinct interface for each NHibernate database so that they can be uniquely resolved by Funq.
For example:
interface FactoryA : NHibernate.ISessionFactory
{
}
interface FactoryB : NHibernate.ISessionFactory
{
}
You could then proceed as you are now. The same applies for the session. See here for a little more detail about the process:
How to register multiple IDbConnectionFactory instances using Funq in ServiceStack.net
Option 2: Named Instance
This option I am less familiar with, but you can name your instances using Funq:
container.Register<NHibernate.ISessionFactory>("FactoryA",sessionFactoryForDB1);
And then in your service, resolve it thusly:
ServiceStackHost.Instance.Container.ResolveNamed<NHibernate.ISessionFactory>("FactoryA");
This option uses Service Location, which I personally find less attractive.