I have a WCF service hosted in IIS7. The service has a static class with a static list containing strings (sort of log). It periodically write the entries to a file or db.
However when the IIS decides the recyle the app or terminate for whatever reason, the entries in the static field are lost.
Is there any way I can handle the service shuttingdown kind event and persist the data from memory?
Thanks
Shreedhar
I've implemented several services via IIS with a custom service host (originally I did this so I could implement IErrorHandler for global error handling).
You'll need two things - an implementation of ServiceHost and an implementation of ServiceHostFactory, which will call your custom service host. For example (just the relevant parts of code shown):
public class MyCustomServiceHost : ServiceHost
{
protected override void OnClosing()
{
// logic to save off your static data
base.OnClosing();
}
}
public class MyCustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new MyCustomServiceHost(serviceType, baseAddresses);
}
}
In your .svc file, you'd have something like this:
<%# ServiceHost Service="MyCompany.MyServiceName" Factory="MyCompany.MyCustomServiceHostFactory" %>
<%# Assembly Name="MyCustomServiceHost" %>
This is one way to do this (and this dates back to .NET 3.5 days); there are quite likely other ways to accomplish this, but at least this should give you some direction.
Related
I have a WCF web service hosted on my Local IIS (not Express). I've included a Global.asax in its root directory, where it is supposed to be. Since I'm using Ninject with WCF extensions, the class Global extends NinjectHttpApplication instead of HttpApplication (as seen here).
Also, I'm using the AutoMapper library in order to circumvent writing boring boilerplate code.
The problem arises because a static method I defined for configuring AutoMapper isn't being called causing AutoMapper to throw exceptions when I call Mapper.Map(). That static method's call is defined in Global.asax's Application_Start() method since I want these mappings to be performed once per the web service's lifetime.
Ninject's CreateKernel() method gets called just fine, by the way.
Am I missing something here? I've tried debugging it, it doesn't hit the breakpoint even though I've attached the debugger to w3wp.exe and also tried putting an explicit Debugger.Break() call in its body.
This is how it looks like so far:
Global.asax
<%# Application Codebehind="Global.asax.cs" Inherits="MyApp.WebHost.Global" Language="C#" %>
Global.asax.cs
public class Global : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
IKernel kernel = new StandardKernel();
/* various bindings */
return kernel;
}
protected void Application_Start(object sender, EventArgs e)
{
AutoMapperConfig.RegisterMappings();
}
/* rest of Global.asax methods (Session_Start, Application_BeginRequest, etc.) with empty bodies */
RegisterMappings method
public static class AutoMapperConfig
{
public static void RegisterMappings()
{
/* multiple calls to Mapper.CreateMap() */
Mapper.AssertConfigurationIsValid();
}
}
Svc file markup
<%# ServiceHost Language="C#"
Debug="true"
Service="MyApp.Services.MyAppService"
Factory="Ninject.Extensions.Wcf.NinjectServiceHostFactory" %>
Everything else works, I've already created a test client (a simple console app) and added a service reference. Service methods get called just fine, it is just that these mappings are a bit problematic since AutoMapper keeps throwing AutoMapperMappingException exceptions ("Missing type map configuration or unsupported mapping.") for the obvious reasons.
The application's app pool is DefaultAppPool. Should I create a separate one?
I really don't understand the problem here. Thank you in advance.
Well, it required some additional searching but I found the answer here - https://groups.google.com/forum/#!topic/ninject/wRy3ELSV4bU
The problem was that NinjectHttpApplication class itself implements the Application_Startup method so it is impossible to implement it in your own derived class (Global class).
To simulate such behavior one needs to override the OnApplicationStarted Ninject's method.
This is how it looks like regarding my particular problem:
protected override void OnApplicationStarted()
{
AutoMapperConfig.RegisterMappings();
}
With .NET 4.5, my WCF creation using svcutil suddenly seems to break (I've been using only .NET 4.0 until very recently) ....
With the default settings I'm using to convert a pre-existing WSDL to my C# WCF proxy class:
c:> svcutil.exe /n:*,MyNamespace /out:WebService MyService.wsdl
I get this C# file created:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="MyService.IMyService1")]
public interface IMyService1
{
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMyService1/IsAlive", ReplyAction="http://tempuri.org/IMyService1/IsAliveResponse")]
[System.ServiceModel.FaultContractAttribute(typeof(MyService.MyFault), Action="http://tempuri.org/IMyService1/IsAliveErrorInfoFault", Name="MyServiceErrorInfo", Namespace="http://schemas.datacontract.org/2004/07/MyService.Types")]
string IsAlive();
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMyService1/IsAlive", ReplyAction="http://tempuri.org/IMyService1/IsAliveResponse")]
System.Threading.Tasks.Task<string> IsAliveAsync();
This doesn't work - if I try to instantiate an implemention of this interface in a ServiceHost
using (ServiceHost svcHost = new ServiceHost(typeof(MyService01Impl)))
{
svcHost.Open();
Console.WriteLine("Host running ...");
Console.ReadLine();
svcHost.Close();
}
I get this error:
Cannot have two operations in the same contract with the same name, methods 'IsAlive' and 'IsAliveAsync' in type 'MyService01Impl' violate this rule. You can change the name of one of the operations by changing the method name or by using the Name property of OperationContractAttribute.
Why does svcutil suddenly generate code that doesn't work??
If I use the /async keyword with svcutil, then I get the "old-style" async pattern with BeginIsAlive and EndIsAlive and things work again - but how can I tell WCF / svcutil to generate no async stuff at all?
svcutil by default generates a proxy class with both synchronous and Task-based methods, eg:
public interface IService1
{
...
string GetData(int value);
...
System.Threading.Tasks.Task<string> GetDataAsync(int value);
...
}
To generate a proxy with only synchronous methods, you need to use /syncOnly. This will omit the Task-based version:
public interface IService1
{
...
string GetData(int value);
...
}
In both cases, the proxy class itself inherits from ClientBase:
public partial class Service1Client : System.ServiceModel.ClientBase<IService1>, IService1
Finally, the /serviceContract switch generates only the interface and DTOs necessary for generating a dummy service
First of all, I have never seen an example of using ninject with wcf.
This is my .svc:
<%# ServiceHost Language="C#" Debug="true" Service="MyService.Services.NotifyService" %>
My Service:
[ServiceContract]
public interface INotifyService
{
[OperationContract]
void SendEmail(string to, string from, string message);
}
class NotifyService : INotifyService
{
private IEmailRepository emailRepo;
public NotifyService(IEmailRepository emailRepo)
{
if (emailRepo== null) throw new ArgumentNullException("emailRepo");
this.emailRepo= emailRepo;
}
public void SendEmail(string to, string from, string message)
{
//do stuff here
}
}
Using this information, how do I dependency inject MyEmailRepository in NotifyService?
If I do not have a default constructor, wcf throws an error asking for one. I also have experience using ninject with asp.net mvc3 if that helps.
See https://github.com/ninject/ninject.extensions.wcf/tree/master/src/Examples/WcfTimeService
Use a custom IInstanceProvider to resolve your service instance. Here is an example:
http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html
This answer at SO provides a full implementation to add NInject to a WCF project.
I won't copy and paste it here, but basically, after installing the Ninject, Ninject.Extensions.Wcf and Ninject.Web.Common extensions through Nuget, you'll have to create three classes:
public class NInjectInstanceProvider : IInstanceProvider, IContractBehavior
public class NInjectServiceHostFactory : ServiceHostFactory
public class NInjectServiceHost : ServiceHost
Then point the Factory attribute in your .svc (right click the file on Solution Explorer, then choose "View Markup") to the NInjectServiceHost class:
<%# ServiceHost ... Factory="SomeNamespace.NInjectServiceHostFactory" %>
I've used custom wcf applicaion as wcf server by using:
ServiceHost<AlertService> alertServiceHost = new ServiceHost<AlertService>();
configuredEndpoints = alertServiceHost.Description.Endpoints;
alertServiceHost.Open();
Now, I have a problem with deployment on production which is IIS7.5.
I have no idea how to deploy on IIS. Because I only know that I have to create svc file for hosting on IIS. Now, I only have console application working as wcf service.
How can I transform it to deploy on IIS?
Thank you.
If you want to use a custom service host in a IIS hosting scenario, you need to supply a custom ServiceHostFactory that will return that type of service host, and configure that service host factory in your SVC file.
Basically, your custom service host factory must descend from ServiceHostFactory and override one method that returns an instance of your custom service host - something along the lines of:
public class MyOwnServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type t, Uri[] baseAddresses)
{
return new MyOwnCustomServiceHost(t, baseAddresses);
}
public override ServiceHostBase CreateServiceHost(string service, Uri[] baseAddresses)
{
// The service parameter is ignored here because we know our service.
ServiceHost serviceHost = new ServiceHost(typeof(HelloService), baseAddresses);
return serviceHost;
}
}
And in your SVC file, you now need to have:
<%# ServiceHost Language="C#" Debug="true"
Service="YourNamespace.YourService"
Factory="YourNamespace.MyOwnServiceHostFactory" %>
Read more about:
ServiceHostFactory on MSDN
Extending hosting using ServiceHostFactory
When hosting in IIS you don't create ServiceHost by yourselves. It is reponsibility of IIS and reason why .svc file exists. Svc file instructs worker process how and which service should be hosted.
Svc file consists of simple directive (markup):
<%# ServiceHost Language="C#" Debug="true" Service="MyNamespace.MyService" CodeBehind="MyService.svc.cs" %>
This is example of .svc file hosting locally create service but you can omit CodeBehind attribute and use full type description in Service attribute to host service class from another assembly.
Also check How to: Host WCF service in IIS.
I have the following WCF code:
ServiceHost host = null;
if (host == null)
host = new ServiceHost(typeof(RadisService));
How can i get a pointer to my RadisService, to make calls with it?
Well it was really for testing purposes, but please allow me to ask the question anyway, for educational purposes. What happens if my service is running on a machine (using a GUI host), several clients from different remote machines connect to the service and through the GUI leave comments on my service.
The code on my service looks like this:
public class MyClass
{
[DataMember]
static Dictionary<String, Variable> m_Variables = new
Dictionary<String, Variable>();
....
}
[ServiceContract]
public interface IMyClassService
{
[OperationContract]
bool AddVariable(String name, Variable value);
[OperationContract]
bool RemoveVariable(String name);
[OperationContract]
bool GetVariable(string name, Variable variable);
[OperationContract] List<String> GetVariableDetails();
...
}
So from my service host GUI i would like to be able to access GetVariableDetails(), and preview all the comments added from all the different clients at this point. How would i achieve this?
If you make your service a singleton you can create an instance of the service and give it to the ServiceHost:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class CalculatorService: ICalculatorService
{
....
CalculatorService service = new CalculatorService();
ServiceHost serviceHost = new ServiceHost(service, baseAddress);
You cannot. The ServiceHost will host 1-n service class instances to handle incoming requests, but those are typically "per-call", e.g. a service class instance is created when a new request comes in, a method is called on the service class, and then it's disposed again.
So the ServiceHost doesn't really have any "service" class instance at hand that it can use and call methods on.
What exactly are you trying to achieve?
Update: the service host should really not do anything besides hosting the service - it should definitely not be calling into the service itself.
What you're trying to achieve is some kind of an administrative console - a GUI showing the current comments in your system. Do this either via a direct database query, or then just have a GUI console to call into your service and get those entries - but don't put that burden on the ServiceHost - that's the wrong place to put this functionality.