Hosting WCF Service as Windows Service - wcf

I have created WCF service project.
It has following content in SVC file.
<%# ServiceHost Service="Deepak.BusinessServices.Implementation.ApiImplementation"
Factory="Deepak.BusinessServices.Implementation.CustomServiceHostFactory"%>
SVC reference
http://localhost/DeepakGateway/Service.svc
Service is UP and WSDL generated. Now I want to host this service as Windows Service.
How can I do it?
I have created "Windows Service" Project ans have following code.
protected override void OnStart(string[] args)
{
if (m_Host != null)
{
m_Host.Close();
}
Uri httpUrl = new Uri("http://localhost/DeepakGateway/Service.svc");
m_Host = new ServiceHost
(typeof(?????? WHAT TO FILL HERE?), httpUrl);
//Add a service endpoint
m_Host.AddServiceEndpoint
(typeof(?????? WHAT TO FILL HERE?), ), new WSHttpBinding(), "");
//Enable metadata exchange
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
m_Host.Description.Behaviors.Add(smb);
//Start the Service
m_Host.Open();
}

You need to add the type of the class that implements your service contract in the ServiceHost constructor, and type of the service contract in your AddServiceEndpoint
Assuming your service implementation class looks something like this:
namespace Deepak.BusinessServices.Implementation
{
public class ApiImplementation : IApiImplementation
{
....
}
}
then you need:
m_Host = new ServiceHost(typeof(ApiImplementation), httpUrl);
m_Host.AddServiceEndpoint(typeof(IApiImplementation), new WSHttpBinding(), "");
the service host needs to know what (concrete) type of service class to host
the endpoint needs to know what service contract (interface) it exposes

Related

How to use Autofac with a CustomServiceHostFactory in an IIS hosted WCF service?

Lets say I have a simple service contract:
[ServiceContract(Namespace = Constants.MyNamespace)]
public interface IAccountService
{
[OperationContract]
Account GetByAccountNumber(string accountNumber);
}
Here is the service:
[ServiceBehavior(Namespace = Constants.MyNamespace)]
public class AccountService : IAccountService
{
private readonly IUnitOfWorkAsync _uow;
private readonly IRepositoryAsync<Account> _repository;
public AccountService(IDataContextAsync dataContext)
{
_uow = new UnitOfWork(dataContext);
_repository = new Repository<Account>(dataContext, _uow);
}
public Account GetByAccountNumber(string accountNumber)
{
return _repository.GetByAccountNumber(accountNumber);
}
}
Here is the CustomServiceHostFactory:
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDbContext>().As<IDataContextAsync>();
builder.Register(c => new AccountService(c.Resolve<IDataContextAsync>())).As<IAccountService>();
using (var container = builder.Build())
{
var host = new CustomServiceHost(serviceType, baseAddresses);
host.AddDependencyInjectionBehavior<IAccountService>(container);
return host;
}
}
}
..where CustomServiceHost creates all of the bindings/behaviors programmatically. I am using file-less activation so my .config file just has section like this:
<serviceHostingEnvironment>
<serviceActivations>
<add service="Company.Project.Business.Services.AccountService"
relativeAddress="Account/AccountService.svc"
factory="Company.Project.WebHost.CustomServiceHostFactory"/>
</serviceActivations>
</serviceHostingEnvironment>
I publish to IIS and can view the site in a browser. It says "you have created a service". However, any call I try to make to the service from my client application gives the following error:
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.
How do you use Autofac with WCF and a CustomServiceHostFactory?
I am able to use poor man's DI as a workaround for now but was hoping to get this working. I can't seem to find any good examples on the web. Thanks.
Don't dispose of the container. Instead of a using statement, keep the container alive. It needs to live as long as the host.
You'll notice in the default Autofac WCF stuff the container is a global static that lives for the app lifetime - that's why.

Castle Windsor - Resolving duplex wcf service

I'm hosting a duplex wcf service using windows service with castle windsor wcffacility using TCP binding.
There is no problem with hosting, I think, when I add a service reference to a console application.I'm able to access the duplex service without any issues.
Problem arises when I use castle windsor at the client side while resolving. Below is the code am using for adding the wcf services through code based on config file.
public static IWindsorContainer RegisterWcfClients(IocBuildSettings iocBuildSettings,
IWindsorContainer container)
{
//Register callback methods for duplex service first.
container.Register(Component.For<INotificationCallback>()
.ImplementedBy<NotificationCallbackCastle>()
.LifestyleTransient());
// get dictionary with key = service class, value = service interface
var servicesWithWcfInterfaces = Assembly.GetAssembly(typeof (IApplicationService))
.GetTypes()
.Where(x => (x.IsInterface || x.IsClass) && HasServiceContract(x))
.ToList();
var registrations = new List<IRegistration>();
//get the client section in System.ServiceModel from web.config file
var clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;
//get the endpointsCollection from childSection
var endpointCollection =
clientSection.ElementInformation.Properties[string.Empty].Value as ChannelEndpointElementCollection;
foreach (var serviceInterface in servicesWithWcfInterfaces)
{
//get the childEndpoint name from web.config file
var endpointName = GetClientEndpointName(endpointCollection, serviceInterface);
//register services which are declared in web.config file only.
if (string.IsNullOrEmpty(endpointName)) continue;
// attribute is either on the service class or the interface
var attribute =
(ServiceContractAttribute)
(Attribute.GetCustomAttribute(serviceInterface, typeof (ServiceContractAttribute)));
if (attribute != null)
{
WcfClientModelBase model = null;
// handle duplex differently
if (attribute.CallbackContract != null)
{
model = new DuplexClientModel
{
Endpoint =
WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
}.Callback(container.Resolve(attribute.CallbackContract));
registrations.Add(WcfClient.ForChannels(model).Configure(c => c.LifestyleSingleton()));
}
else
{
//regular attributes
model = new DefaultClientModel
{
Endpoint = WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
};
registrations.Add(WcfClient.ForChannels(model).Configure(c => c.LifestyleTransient()));
}
}
}
return container.Register(registrations.ToArray());
}
Am hosting only one duplex service and the below are the servicecontracts -
[ServiceContract(CallbackContract = typeof(INotificationCallback))]
public interface INotificationService
{
[OperationContract(IsOneWay = false)]
void Subscribe(Guid subscriptionId, string userName, string[] eventNames);
[OperationContract(IsOneWay = true)]
void EndSubscribe(Guid subscriptionId);
}
[ServiceContract]
public interface INotificationCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveNotification(NotificationResultDto notificationResult);
}
[DataContract]
public class NotificationResultDto
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public string NotificationMessage { get; set; }
}
When I try to resolve the duplex service using the below statement.
var temp = _container.Resolve();
I get error -
WcfClientActivator: could not proxy component c2a216c2-af61-4cb2-83ba-e4d9a5cc4e68
with inner exception - The Address property on ChannelFactory.Endpoint was null. The ChannelFactory's Endpoint must have a valid Address specified.
in the web.config file under client section -
<endpoint address="net.tcp://localhost:9877/NotificationService" binding="netTcpBinding"
bindingConfiguration="netTcpBindingConfiguration" contract="ServiceContracts.INotificationService"
name="INotificationService_Endpoint" />
After few hours of struggling, I found a work around for this problem.
I think this could a bug in Castle Windsor, while creating DuplexClientModel, endpoint cannot be created using "FromConfiguration". It fails while resolving during runtime. However samething works fine with "DefaultClientModel".
My workaround was to read the config file and get the address, binding and contract details and use them to create Endpoint in code.
model = new DuplexClientModel
{
//Endpoint = WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
//FromConfiguration method is failing for some reason,could be b.u.g in castle,
//so had to do this workaround by reading the web.config file and creating the Endpoint
//from there manually.
Endpoint = WcfEndpoint.ForContract(serviceInterface)
.BoundTo(CreateBindings(clientEndpoint.Binding))
.At(clientEndpoint.Address)
}.Callback(container.Resolve(attribute.CallbackContract));

Dynamically invoking WCF service

I've created ASP.NET application and added simple WCF service to it. The ASP.NET application is host for WCF service. The service is running.
The service looks as follows:
[ServiceContract]
public interface IService1
{
[OperationContract]
string DoWork(string text);
}
public class Service1 : IService1
{
public string DoWork(string text)
{
return text.ToUpper();
}
}
On the client side is console application that should invoke WCF service dynamically. I use following code:
WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);
IChannelFactory<IRequestChannel> factory = binding.BuildChannelFactory<IRequestChannel>(
new BindingParameterCollection());
factory.Open();
EndpointAddress address = new EndpointAddress("http://localhost:3929/Service1.svc");
IRequestChannel irc = factory.CreateChannel(address);
using (irc as IDisposable)
{
irc.Open();
XmlReader reader = XmlReader.Create(new StringReader(
#"<DoWork xmlns='http://tempuri.org/'>
<composite xmlns:a='http://www.w3.org/2005/08/addressing'
xmlns:i='http://www.w3.org/2001/XMLSchema-instance'>
<a:StringValue>aaaa</a:StringValue>
</composite>
</DoWork>"));
Message m = Message.CreateMessage(MessageVersion.Soap12,
"http://tempuri.org/IService1/DoWork", reader);
Message ret = irc.Request(m);
reader.Close();
Console.WriteLine(ret);
}
//close the factory
factory.Close();
But, it crashes at this line:
Message ret = irc.Request(m);
with following error:
The message version of the outgoing message (Soap12 (http://www.w3.org/2003/05/soap-envelope) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)) does not match that of the encoder (Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)). Make sure the binding is configured with the same version as the message.
Does anybody know what I'm doing wrong?
Thank you in advance.
Message.CreateMessage(MessageVersion.Soap12,
Instead of the MessageVersion enum value Soap12, you need to specify Soap12Addressing10 to match your binding.

Error while trying to access the method in the WCF service where the project type is "WCF Service application"

I have a WCF Service application project , a class library project(acts as a proxy betwen the service and the client) and a Asp.net web project.
Now in the WCF Service application project, I have the method GetData(int) [the default one]
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
I complied the WCF service project and found it working and henceforth added that as service reference to the Class library project. And written a method as under to fetch the value from the service
public string GetResult(int number)
{
string result = "";
try
{
Service1Client sc = new Service1Client();
result = sc.GetData(number);
}
catch (Exception ex)
{
var message = ex.Message;
}
return result;
}
Now this method is being invoked from the Web application. At runtime I am getting an exception
Could not find default endpoint element that references contract 'ServiceReference1.IService1' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.
The error is happening at Service1Client sc = new Service1Client();
All the config files are in place....Should I have to create the proxy using SVC util?
What am I missing?
Are you sure you have apropriate configuration for wcf placed in web.config? It seems you don't.

Can WCF self hosted applications create ServiceHosts automatically using the app.config?

When I create a self hosted wcf application, I create ServiceHost objects for each service I want to expose. It then looks in the app.config (matching up the server name), and then pulls the associated endpoint address and contract.
Is there a way to automatically create ServiceHosts for every service that is listed in the app.config. I would like to add new services to the app.config and have them loaded automatically without recompilng my program and using my manually coded process to create ServiceHost objects.
Is there a factory or a tutorial someone could link me that shows me how to do this?
Thanks
I'm not sure what do you mean by pulling associated addresses and contracts from config - this is done automatically. Service section in configuration file is automatically paired with type of service hosted in ServiceHost:
Service hosting:
using (var host = new ServiceHost(typeof(MyNamespace.Service))
{
// no endpoint setting needed if configuration is correctly paired by the type name
host.Open()
}
Service configuration:
<services>
<service name="MyNamespace.Service">
...
</service>
</service>
Now the only thing you need is to handle ServiceHost creation automatically. Here is my sample code to do it:
class Program
{
static void Main(string[] args)
{
List<ServiceHost> hosts = new List<ServiceHost>();
try
{
var section = ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection;
if (section != null)
{
foreach (ServiceElement element in section.Services)
{
var serviceType = Type.GetType(element.Name);
var host = new ServiceHost(serviceType);
hosts.Add(host);
host.Open();
}
}
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.ReadLine();
}
finally
{
foreach (ServiceHost host in hosts)
{
if (host.State == CommunicationState.Opened)
{
host.Close();
}
else
{
host.Abort();
}
}
}
}
}