I have created a project that uses Entity Framework 6. This project is a data layer that is used in multiple other projects, for example an MVC website and a Web API.
When a user changes something in the MVC website, it is stored through the data layer in the database. But the Web API project does not detect these changes.
My DbContext is injected in the controllers with Ninject. I tried Disposing the DbContext in the controller's Dispose method, which didn't work. Should I Refresh() the database context after it has been injected in the controller's constructor? Or is there another way to keep the DbContext in sync with the database?
Fwiw, here is the OWIN Startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
config.DependencyResolver = new NinjectResolver(NinjectWebCommon.CreateKernel());
app.UseWebApi(config);
}
}
And here is the Ninject code:
public static class NinjectWebCommon
{
public static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
GlobalConfiguration.Configuration.DependencyResolver = new WebApiContrib.IoC.Ninject.NinjectResolver(kernel);
// Register Services:
kernel.Bind<MyDbContext>().To<MyDbContext>().InRequestScope().WithConstructorArgument("disableOrgFilter", true);
return kernel;
}
}
Related
How can I use one DbContext with multiple application?
I have a WCF application (Net TCP binding) interface and implementation works fine with the DbContext. There is a need for API from the same application and I don't want to enable Http Binding on the WCF because of configuration and I have so many contracts. so I decided to import the service into asp.net core 2 via DI it works fine but works connect to Db via DbContext always returning null.
DB Context:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options){}
public AppDbContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(#"Server=.\;Database=Database;Trusted_Connection=True;MultipleActiveResultSets=true");
}
}
}
Service implementation
public partial class GeneralService : IGeneralService, IDisposable
{
protected readonly AppDbContext Db = new AppDbContext();
public void Dispose()
{
Db.Dispose();
}
}
Asp.net core Start Up
public void ConfigureServices(IServiceCollection services)
{
const string connection = #"Server=.\;Database=Database;Trusted_Connection=True;MultipleActiveResultSets=true";
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connection));
services.AddSingleton<IGeneralService,GeneralService>();
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver());
}
what am I doing wrong, what can I do I really don't want to use Proxy
connect to Db via DbContext always returning null.
I think that might be down to the fact that you're creating the DB context directly in the service class. You can/should inject your DbContext into your service instead. Something like:
public partial class GeneralService : IGeneralService, IDisposable
{
protected readonly AppDbContext Db;
public GeneralService(AppDbContext db)
{
Db = db;
}
// ... etc...
}
Further, since you're providing a connection string to the db in your Startup.cs you don't need the OnConfiguring method in your db context.
Finally, services shouldn't be singletons if they're using EF. See this answer which recommends the Request scope.
I've following projects:
Poco (listing all the potential objects/dtos)
BusinessServices (contains all the logic and communicates with database)
WcfServices (calls into Business Services and return Pocos)
MVC5 (calls into WcfServices)
Now, I'm trying to invoke the WcfServices without using the Proxy, but using the ChannelFactory and initializing it via Unity, I'm getting the error: "No parameterless constructor defined for this object". Please help.
UnityConfig.cs
public static void RegisterComponents()
{
var container = new UnityContainer();
// Register WCF
container.RegisterType<IPersonService>(
new ContainerControlledLifetimeManager(),
new InjectionFactory((c) => new ChannelFactory<IPersonService>("WSHttpBinding_IPersonService").CreateChannel()));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
PersonController.cs
public class PersonController : Controller
{
private readonly IPersonService _personService;
public PersonController(IPersonService personService)
{
_personService = personService;
}
...
...
...
}
I'm developing a multi tenant application. For now what I want to achieve is obtaining a unique instance of HttpContext per tenant.
Each tenant has its own database.
All tenants share same functionality (There aren't any tenant X specific controllers)
There is a master database for querying all tenants (So before accessing tenant's settings (eg:connectionString) there must be at least one hit to master database).
Tenant is identified from RequestContext RouteData.Values["tenant"]
Here is some simplified code so please do not focus on the architecture:
Route url pattern:{tenant}/{controller}/{action}/{id}
Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
// Delegates for BuildSessionFactory and GetSession are declared under Application_Start()
// Registers SessionFactory to be Singletone per Tenant
builder.Register(BuildSessionFactory).As<ISessionFactory>().InstancePerTenant();
builder.Register(GetSession).As<ISession>().InstancePerRequest();
// This is the module responsible for mapping HttpContextBase to HttpContextWrapper and other mvc specific abastractions
builder.RegisterModule(new AutofacWebTypesModule());
var container = builder.Build();
// Build multitenant container
var mtc = new MultitenantContainer(new RouteDataTenantIdentificationStrategy("tenant"), container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc));
}
private ISessionFactory BuildSessionFactory(IComponentContext context)
{
return new NHibernateConfiguration().BuildConfiguration().BuildSessionFactory();
}
private ISession GetSession(IComponentContext context)
{
return context.Resolve<ISessionFactory>().OpenSession();
}
HomeController sample:
public class HomeController : Controller
{
private readonly ISession _session;
private readonly HttpContextBase _context;
public HomeController(ISession session, HttpContextBase context)
{
_session = session;
_context = context;
}
public ActionResult Index()
{
// Setting up HttpContext.Session["testSessionKey"] only for tenant1
if (_context.Request.RequestContext.RouteData.Values["tenant"] as string == "tenant1")
{
_context.Session.Add("testSessionKey","Some value specific to tenant1 only");
}
using (var transaction = _session.BeginTransaction(IsolationLevel.ReadCommitted))
{
var tenants = _session.Query<Tenant>().ToList();
transaction.Commit();
return View(tenants);
}
}
}
The Workflow:
Remark: The code above ignores the true existance of the tenant. It just satisfies the dependencies according to the RouteDataTenantIdentificationStrategy("tenant") class definition (identification by RouteData nothing exceptional).
Now when I'm accessing the url: /tenant1/Home/Index a HttpContext.Session['testSessionKey'] is added.
When I try to acces something like /otherTenant/Home/Index the HttpContext.Session['testSessionKey'] is still there.
Question 1:
How to achieve Unique HttpContext per tenant using Dependency Injection with Autofac?
Question 2:
How to achieve something more than a HttpContext? Lets say a WorkContext which includes the tenant's HttpContext
If something is unclear please ask and I'll provide necessary clarifications. Thank you!
You can do this if you push the customization of the context values to the registration of HttpContextBase. Instead of using the AutofacWebTypesModule, use your own registration:
builder.Register(c =>
{
var httpContext = new HttpContextWrapper(HttpContext.Current);
var strategy = c.Resolve<ITenantIdentificationStrategy>();
// Update the context here
return httpContext;
}).As<HttpContextBase>()
.InstancePerRequest();
That means you should also have your tenant ID strategy registered in the container, but that's pretty easy.
(Sorry for the short snippet; I'm on my phone traveling.)
I have trouble using MembershipReboot with the new ASP MVC5 template and Autofac. I have used the default MVC5 template to set up the site and then tried to wire up the MembershipReboot framework as a replacement for the ASP Identity framework that ships with the template.
This issue I am having is trying to resolve an IOwinContext from the Autofac container. Here is my wiring in the Startup class (cut down to basics). This is the wiring used in the samples for the MembershipReboot Owin application (except there he uses Nancy).
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.Register(c => new DefaultUserAccountRepository())
.As<IUserAccountRepository>()
.As<IUserAccountQuery>()
.InstancePerLifetimeScope();
builder.RegisterType<UserAccountService>()
.AsSelf()
.InstancePerLifetimeScope();
builder.Register(ctx =>
{
**var owin = ctx.Resolve<IOwinContext>();** //fails here
return new OwinAuthenticationService(
MembershipRebootOwinConstants.AuthenticationType,
ctx.Resolve<UserAccountService>(),
owin.Environment);
})
.As<AuthenticationService>()
.InstancePerLifetimeScope();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
ConfigureAuth(app);
app.Use(async (ctx, next) =>
{
using (var scope = container.BeginLifetimeScope(b =>
{
b.RegisterInstance(ctx).As<IOwinContext>();
}))
{
ctx.Environment.SetUserAccountService(() => scope.Resolve<UserAccountService>());
ctx.Environment.SetAuthenticationService(() => scope.Resolve<AuthenticationService>());
await next();
}
});
}
And here is my controller with the dependency specified in the controller constructor.
public class HomeController : Controller
{
private readonly AuthenticationService service;
public HomeController(AuthenticationService service)
{
this.service = service;
}
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
It seems I need to wrap my Autofac container in an AutofacDependencyResolver in order for the MVC framework to use the container to resolve components. This is the only major difference from the Nancy Owin sample and my use in MVC5.
When I do this, it then seems (from my Tracing) as if the dependency is being resolved without first going through the OWIN middleware stack so the IOwinContext is never registered.
What am I doing wrong here?
UPDATE:
Brock, your new sample works perfectly when I migrate the configuration over to my project. Just for my understanding, it seems that this line in your new sample registers the current OwinContext with the container and that is what was missing previously.
builder.Register(ctx=>HttpContext.Current.GetOwinContext()).As<IOwinContext>();
Is that
There's a newer sample that does DI with AutoFac for MVC:
https://github.com/brockallen/BrockAllen.MembershipReboot/blob/master/samples/SingleTenantOwinSystemWeb/SingleTenantOwinSystemWeb/Startup.cs
See if this helps.
If you don't want to use HttpContext.Current you could do something like this:
app.Use(async (ctx, next) =>
{
// this creates a per-request, disposable scope
using (var scope = container.BeginLifetimeScope(b =>
{
// this makes owin context resolvable in the scope
b.RegisterInstance(ctx).As<IOwinContext>();
}))
{
// this makes scope available for downstream frameworks
ctx.Set<ILifetimeScope>("idsrv:AutofacScope", scope);
await next();
}
});
This is what we're doing internally for some of our apps. You'd need to wire up your Web API service resolver to look for "idsrv:AutofacScope". Tugberk has a post on this:
http://www.tugberkugurlu.com/archive/owin-dependencies--an-ioc-container-adapter-into-owin-pipeline
I am trying to use the CookieTempDataProvider in MVC 3 futures assembly. I believe I have "wired" it up successfully using ninject. Below is the code from my app_start.cs file:
[assembly: WebActivator.PreApplicationStartMethod(typeof(Web.AppStart), "Start")]
namespace Web {
public static class AppStart {
public static void RegisterServices(IKernel kernel) {
kernel.Bind<ITempDataProvider>().To<CookieTempDataProvider>();
}
public static void Start() {
// Create Ninject DI Kernel
// IKernel kernel = new StandardKernel();
IKernel kernel = Container;
// Register services with our Ninject DI Container
RegisterServices(kernel);
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new NinjectServiceLocator(kernel));
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
_container = new StandardKernel();
return _container;
}
}
However, when I access my page that uses TempData, I get the this error indicating that it is still trying to use the SessionTempDataProvider:
Server Error in '/' Application.
The SessionStateTempDataProvider class requires session state to be enabled.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The SessionStateTempDataProvider class requires session state to be enabled.
I must be missing something and I can't figure out what it is. Any help would be most appreciated.
Many Thanks
I've only been able to get this to work with the BaseController approach. The controller creation processes does't ask Ninject for a ITempDataProvider.
public class BaseController : Controller
{
protected override ITempDataProvider CreateTempDataProvider()
{
return new CookieTempDataProvider(HttpContext);
}
}
Extend the controller class
public static void SetMessage(this Controller controller, String message)
{
controller.TempData["Messag"] = message;
}
Then you can use it like this:
public ActionResult Save()
{
//Validation...
Save(Foo);
this.SetMessage("Item saved successfully");
return Redirect("/Site");
}
No number three :)