I’ve inherited an MVC that currently does some setup work with the ApplicationStart method so that when the application comes back to life with an IIS Application pool this setup has already been carried out.
As pseudo-code:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Build Api autofac container
// Build MVC autofac container
// Resolve serviceOne from the MVC container
var serviceOne = (IServiceOne)DependencyResolver.Current.GetService(typeof(IServiceOne));
// Make setup call - includes external http calls and DbContext checking
serviceOne.syncToExternal();
// Resolve serviceTwo again from the MVC container
var serviceTwo = (IServiceTwo)DependencyResolver.Current.GetService(typeof(IServiceTwo));
// Make setup call - publishes application information to internal message queues so that we know it's running
serviceTwo.syncToInternalSystems();
}
}
In the ApplicationStart I run through the normal process of setting up Autofac containers; one for the MVC and one for the WebApi. In here the respective MVC or Api controllers, service classes and my DbContext are registered.
The setup work in the ApplicationStart needs a service and a DbContext which I resolve from the MvcContainer, as the WebApi one is not accessible at this point.
ServiceOne retrieves data from an external url and using this to seed / check the current contents of the database.
ServiceTwo reads back some of this data and publishes it to internal message queues within the company.
Once the Application_Start() has finished and the Home page has loaded: if I make a request which routes through the MVC then the DbCobntext's registered databaseInitialzer does not get called as it was run during the Application_Start, but if I make an /api request the databaseInitializer does get called.
I suspect that running the setup in ApplicationStart method is preventing a flag being set in the System.Data.Entity.Database which managed the Connection; hence when the DbContext is resolved from the Api Container it thinks the database hasn’t been initialised...?
Any help would be much appreciated.
My fallback will be to shift all of the setup into a seed method which runs when the databaseInitialiser/Migration is called; but it would be useful to know why the original version of the code was failing to execute as expected.
the ApplicationStart run every time the ApplicationPool starts, no matter what. You have to use another mechanism to populate your DB. Like Migrations.
Related
I register a service as Singleton in the program.cs file like this.
builder.Services.AddSingleton<ITest, Test>();
and if I request an instance of it in program.cs
var testService = builder.Services.BuildServiceProvider()
.GetRequiredService<ITest>();
It creates a new object which is the first object, but when I request it through constructor injection in some other service it creates a new object again. Shouldn't it return the first object that it created while startup in Program.cs?
Note : This behavior is only if I request service in the program.cs other than that it returns the same object, even if I request it using IServiceProvider.GetRequiredService();
i have tested the same scenario in dot net 5 web api as well in which i register the service in ConfigureServices method of Startu.cs file
services.AddSingleton<ITest, Test>();
and request the service in Configure method like this
var test = app.ApplicationServices.GetRequiredService<ITest>();
and when i request it in constructor in some other service it will return the same object.
Why?
In your starup file, you don't have a service provider yet. You only have the Services property that allows you to define your services.
Since you want to instantiate one, you call builder.Services.BuildServiceProvider() which builds a service provider for you, that you then use to get a service. Inside that service provider, your service lives as singleton.
Then, you proceed in your application and the framework, being happy that you defined your services, then builds it's own service provider. Which you can access via app.ApplicationServices. That one, too, has a single instance of your class, since it's a singleton.
And from now on, since all your services and controllers use the service provider created by the framework, not the one you created manually in startup, all of them will get the same instance.
I have a single page app with Angular.js front, web api2 back end, also using Castle Windsor and SignalR.
I am using a c# component on the server that maintains server state. So, on Application_Start() I register the main component with Castle Windsor as Singleton.
IoC.Container.Register(Component.For(typeof(MainManager)).ImplementedBy(typeof(MainManager)).LifeStyle.Singleton);
(however, I tested PerWebRequest and few other lifestyles, but the main problem remains the same, the started task does not quit)
Then in my Web Api, I can execute commands against that instance.
[HttpGet]
public void StartProcess(Params p) {
IoC.Resolve<MainManager>().StartOperation(p);
}
and this also gives me opportunity to stop it from the website by calling another controller method
[HttpGet]
public void Stop() {
IoC.Resolve<MainManager>().RequestStop();
}
This works great for the most part. However, sometimes my application gets in a bad state (for a multitude of reasons, It can get restarted in prod) I can emulate this problem by modifying web.config during running the operation, so a lot of things reset (such as Signal-R connection), but the main operation does not stop running.
(In fact, once my application is in bad state I can no longer call that Stop controller method because MainManager has been reset, so the only way to stop it as of now is to reset the whole IIS. This is certainly not desired)
I am trying to figure out how to detect this state, and terminate the running process.
As one possible solution, I am experimenting with using Bound lifestyle (new in Castle Windsor 3), trying to scope my manager to my web api httpapplication, but no luck yet.
update
I tried making the main task method static
[HttpGet]
public void Start(ForceData data) {
MainManager.Start(data);
}
I believe this should take out the singleton instance out of equation, but the code still runs un-interrupted after touching web.config
update
took the MainManager class out of equation. All my web api method does now is loop + sleep
[HttpGet]
public void Start(ForceData data) {
foreach (var e in data.Members)
{
_log.Info("processing member: {0}", e.Email);
Thread.Sleep(1000);
}
}
this loop is also not interrupted after touching web.config.
So, at this point I am reading about MVC request lifecycle to figure out at which point this request goes zombie rouge
This is by design, as HttpApplication instances are not freed immediately when application restarts,
http://msdn.microsoft.com/en-us/library/vstudio/ms178473%28v=vs.100%29.aspx
When an application restart is required, ASP.NET will serve all pending requests from the existing application domain and the old assemblies before restarting the application domain and loading the new assemblies.
In all your sample Web API method, you can see the thread is still busy, which means ASP.NET runtime considers this request is still being processed.
We've got a ServiceStack 3.9.x service that we're trying to unit test end-to-end (via an in-process service host and accessing it via C# native clients), and we're running into a snag where it seems that the session is not accessible via the means we typically use to access it when running this way.
We typically access the current session (using servicestack's built-in system and custom AuthSession and providers, which all works fine running in IIS against an AppHost derived from AppHostBase) using this:
EndpointHost.AppHost.TryResolve<ICacheClient>().SessionAs<SsoUserSession>();
However, when trying to access this in unit testing (against an AppHost derived from AppHostHttpListenerBase), we get an exception thrown trying to get at the session: "Only ASP.NET Requests accessible via Singletons are supported" which appears to be a hard-coded error in the SessionFeature.
So the question is this: can one access sessions via the cache provider when unit testing via a service host derived from AppHostHttpListenerBase? And if so, how?
I've run into this before too.
The way I handle it is create create something like an extension method to get the session from the cache client that just calls the base, but before calling the base..check the IoC container for the session first. If it's there, just use that instead, and I just inject the session I want to use when testing.
This is used somewhere in servicestack but I can't seem to find the method that does it...anyway an extension method could look something like this
public static MyTypedSession GetMyTypedSession(this ICacheClient cache)
{
var typedSession = ServiceStackHost.Instance.Container.TryResolve<MyTypedSession>();
if (typedSession != default(MyTypedSession))
return typedSession;
return cache.SessionAs<MyTypedSession>();
}
Then instead of calling SessionAs in your code to get the typed session, you would just call GetMyTypedSession, and it would work fine for testing, so long as you Register your fake MyTypedSession
Here's some c# psuedo test method
public void SomeTestMethod()
{
var session = new MyTypedSession { IsAuthenticated = true; };
//get your container and register the session
container.Register(session);
var someValue = TestCodeThatUsesASession();
Assert(someValue);
}
I'm unsure what kinda delay looking in the IoC container everytime you need a session is though.
Sorta strange to add that code just for testing but oh well, works for me and save me time :).
I'm trying to call a client method from within a .net Web API controller action.
Can I do this?
The only post I can find that comes close to what I am looking to do is this one:
SignalR + posting a message to a Hub via an action method
In there a message is sent from within an asp.net MVC controller action using GlobalHost.ConnectionManager.GetHubContext.
When I try that inside my Web API action no errors are thrown, but the method "methodInJavascript" is never invoked on the client side.
Public ActionResult MyControllerMethod()
{
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.All.methodInJavascript("hello world");
// or
context.Clients.Group("groupname").methodInJavascript("hello world");
}
When I set a break point inside that action, I see that the code is being reached and executed. Nothing happens on the javascript client side though.
Why? Is Web API so different under the hood that this won't work? Has anyone else tried it and had success?
When I call the "methodInJavascript" from "within" my hub, it works perfectly. Just won't work when called from within a .net Web API controller action.
UPDATE:
After researching this issue I have no solution. I can only assume there is something missing from examples like this Server to client messages not going through with SignalR in ASP.NET MVC 4 and this calling SignalR hub from WebAPI controller issues like maybe there is an additional configuration step to enable calling from a HubContext or something. The code I initially posted here is like that which appears in those examples has not been demonstrated to be flawed in any way. Can anyone see a flaw in the code? Calling from html works. I do it extensively in my apps and never experience an issue. I have never seen a call from the HubContext in an API controller work. No errors. Just no results on the client.
SOLVED (kind of):
Code above does indeed work as is when published. Does not work in Visual Studio dev environment via localhost though. No errors but no result on the client end. Publishing the code as is to a real server on the web does indeed work. I never thought there'd be a difference so I never tried. Figured if it didn't work locally it wouldn't work published. It's working live now but I'm wondering why it doesn't work via localhost in the dev environment. Can't test locally with breakpoints and such.
I have a feeling it's that signalr virtual directory. Something is different when run locally vs published. Not sure what but I see lots of posts like http://www.bitwisejourneys.com/signalr-hosting-in-iis-a-nasty-gotcha/. Reading now to see if there's a way to have it work both locally and published.
I came across with same issue couple days ago. That took my 2 days to find solution and resolve it. After some serious investigate the problems root cause was the signalr dependency resolver that I set customly.
At the end I found this link and that was saying this:
Replacing the DependencyResolver
You can change the DependencyResolver to use your DI container of
choice by setting GlobalHost.DependencyResolver.
NOTE: DO NOT override the global resolver in PreApplicationStart, it
will not work, or it'll work only sometimes. Do it in
PostApplicationStart (using WebActivator) or in Global.asax.
The important place here the NOTE. Of course after signalr 2.0 this documentation become deprecated. So I mixed some of here with the new SignalR API. In new SignalR API not using WebActivatorEx anymore. OwinStartup preferred instead of WebActivator.
[assembly: OwinStartupAttribute(typeof(YourNamespace.Startup))]
namespace YourNamespace
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
//IoC container registration process
UnityConfig.RegisterComponents();
UnityConfig.Container.RegisterType<AHub, AHub>();
HubConfiguration config = new HubConfiguration();
config.EnableJavaScriptProxies = true;
//You should remove your dependency resolver code from here to Global.asax Application_Start method. Before setting the MVC properties.
//config.Resolver = new SignalrDefaultDependencyResolver(UnityConfig.Container); // your dependency resolver
//
app.MapSignalR(config);
}
}
}
And in your global.asax
namespace YourNamespace
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//Here your SignalR dependency resolver
GlobalHost.DependencyResolver = new SignalrDefaultDependencyResolver(UnityConfig.Container);
//other settings goes on
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
I dont want to send all the code here, for showing up the real problem.
So for me everything works fine for now. Dependency injection also works too. But the bad part is everywhere that I searched David Fowler was saying "Its by design". I started to think is this design really a necessary or a mistake.
Hope it helps somebody else who makes research for same problem.
I had the same issue, and it is related to IoC (with whatever such as ninject or castle).
If you set the global dependency resolver to your IoC manager, it will also replace the SignalR inner pipeline resolution. This makes your SingleTon client hub, not work correctly.
I solved it by only having the Server Hubs being IoC-ed
The code below requires SignalHubActivator (you can find it on the internet)
Now, GlobalHost.ConnectionManager.GetHubContext will return the single instance AND client methods will be called correctly again!
//configuration.Resolver = signalrDependency ; dont, this will cause GlobalHost.ConnectionManager to be intercepted by Castle
configuration.Resolver.Register(typeof(IHubActivator),
() => new SignalHubActivator(container));
I want to create a web-service that run a specific method on startup.
this is the service's interface:
namespace MyClass
{
[ServiceContract]
public interface IService
{
[OperationContract]
string getData();
}
}
and on the service itself i want a specific method (not one of those) to run when the service loads (or deployed to IIS). is there a way to do so?
You need to be clear what really happens when a WCF service is hosting in IIS.
IIS provides a service host that loads on demand
when a request comes in, IIS instantiates the service host, which in turns instantiates an instance of your service class, passes it the parameters from the request, and then executes the appropriate method on the service class
As such, there is no point in time when the "service loads" and then just lingers around in memory. The "service" isn't just loaded when IIS starts up and then would be "present and ready" at all times...
So where do you want to plug in??
when the service host loads in IIS? In that case, you'd have to create your own custom service host and register it with IIS so that IIS would use your custom host instead of the WCF default service host
when the actual service class is instantiated to handle the request? THen put your logic into the constructor of your service class - it will be executed each time a service class is instantiated to handle a request
Though this might not be exactly what you want, you could use the class's constructor, perhaps:
public class Service : IService
{
public Service()
{
//code here will execute when an instance
//of this service class is instantiated
}
string getData() { ... }
}
It would be more clear if you could inform us of the method you wish to call, and any surrounding information about it, so that you don't get ill-advice. Specifics are nice.
Here's where I put some code in order to get (and cache) data on the webservice start (in VB). You do need to trigger the service by navigating to any valid or invalid
Public Module WebApiConfig
Public Sub Register(ByVal config As HttpConfiguration)
'Run this method on startup to cache the addresses
Address.GetAll()
config.Routes.MapHttpRoute(
name:="DefaultApi",
routeTemplate:="api/{controller}/{id}",
defaults:=New With {.id = RouteParameter.Optional}
)
End Sub
End Module