Pass an object as parameter to the ServiceHost - wcf

I'm trying to start up a project which takes care of stuff. I call this class the CentralLogic class. When this is started, I'd like to start a host. The host has some settings in the App.Config file like the baseAddress and endpoints.
This host can interact with this CentralLogic class. I'd like to pass on the instantiated object to this host.
Currently, I'm trying something like this:
centralLogic = new CentralLogic();
ServiceHost host = new ServiceHost(centralLogic, typeof(KLAService));
using (host)
{
host.Open();
//Start a WPF UI. Also makes sure the host stays open
//as long as the UI stays open.
Application app = new Application();
app.Run(new ConfigurationWPF.MainWindow(centralLogic));
host.Close();
}
The KLAService is defined like this:
public class KLAService : IKLAService
{
CentralLogic centralLogic;
public KLAService(CentralLogic theCentralLogic)
{
centralLogic= theCentralLogic;
}
.....
}
This doesn't work (the creation of the ServiceHost is wrong, both in number of arguments and the second parameter). I can get it started without parameter by doing:
ServiceHost host = new ServiceHost(typeof(KLAService));
So, the problem is that I don't know how to pass on the object on to the server. How do I do this?
EDIT: I tried the following:
centralLogic = new CentralLogic();
KLAService klaService = new KLAService(centralLogic);
using (ServiceHost host = new ServiceHost(klaService))
{
host.Open();
Application app = new Application();
app.Run(new ConfigurationWPF.MainWindow(centralLogic));
host.Close();
}
This popped an InvalidOperationException:
In order to use one of the ServiceHost constructors that takes a
service instance, the InstanceContextMode of the service must be set
to InstanceContextMode.Single. This can be configured via the
ServiceBehaviorAttribute. Otherwise, please consider using the
ServiceHost constructors that take a Type argument.

Related

Resolving WCF endpoint address dynamically with autofac

I have a WCF client used in MVC application which can get data from multiple WCF services, the services are configured the same way and Implement the same Interface the only difference is the address of the exposed endpoint.
This is what I tried:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://service.com/Service")))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
The thing here is that IService will always get data from http://service.com/Service since the address is hardcoded somewhere in the Application_Start method of the MVC application.
Then i tried using metadata:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://foo.com/Service")))
.SingleInstance().WithMetadata("name", "fooservice");
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://bar.com/Service")))
.SingleInstance().WithMetadata("name", "barservice");
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
But this way I will have to edit the code every time I want to add the same WCF service
implemented on a different server.Instead I want to get the address from the database.
Is there any way I can change the address per service call or at least when the instance of the client is created.
Additional explanation:
Lets say I have five exact copies of a website each with it's own domain name and database I want to be able to do the following:
foreach(Provider provider in providers)
{
SetServiceAddress(provider.Address);//how can i do that
_service.GetData()
}
Under the assumptions that:
The binding doesn't change when the address changes (e.g., it doesn't switch from HTTP to HTTPS)
The address might change on a per-request basis
Then I'd probably solve it with a combination of lambdas and a small interface.
First, you'd want something that retrieves the address from your data store:
public interface IAddressReader
{
Uri GetAddress();
}
The implementation of that would read from the database (or environment, or XML config, or whatever).
Then I'd use that in my registrations:
builder
.RegisterType<MyDatabaseAddressReader>()
.As<IAddressReader>();
builder
.Register(c => new ChannelFactory<IService>(new BasicHttpBinding()))
.SingleInstance();
builder
.Register(c =>
{
var reader = c.Resolve<IAddressReader>();
var factory = c.Resolve<ChannelFactory<IService>();
var endpoint = new EndpointAddress(reader.GetAddress());
return factory.CreateChannel(endpoint);
})
.As<IService>()
.UseWcfSafeRelease();
That way you can just take in an IService (or Func<IService>) as a constructor parameter and your calling class won't know about Autofac, service location, or endpoints.
If the binding also changes, it gets a little more complicated. You probably don't want a brand new channel factory spun up for every channel, so you'd want to have some sort of caching mechanism where you:
Get the settings from the configuration source.
Compares those settings against the settings currently in use.
If the settings don't match...
Dispose of the previous channel factory.
Create a new channel factory with the new settings.
Cache the channel factory for later reuse.
Return the current channel factory.
If you can use cache dependencies on the settings, all the better, but not every configuration source supports that, so YMMV. I'd probably implement a custom module for that to encapsulate the logic, but I won't write all that out here.
If you want to set the endpoint just before the call each time, you can do this:
containerBuilder
.Register(c => new ChannelFactory<IService>(new BasicHttpBinding()))
.SingleInstance();
containerBuilder.Register((c, p) =>
{
var factory = c.Resolve<ChannelFactory<IService>>();
var endpointAddress = p.TypedAs<string>();
return factory.CreateChannel(new EndpointAddress(endpointAddress));
})
.As<IService>()
.UseWcfSafeRelease();
Then you inject this:
Func<string, IService> getService
Then call it like this:
string endpoint = getDataDependentEndpointFromSomewhere();
var service = getService(endpoint);
I have a service that is running on multiple sites, and at start-up the app needs to determine at which site it is running. It does so using a start-up parameter, and based on this the endpoint address can be set dynamically in a property or method, like GetEndPointAddressForService().
In your case it seems that you need to call n services at different sites consecutively. You could definitely configure these in a database or a simple configuration file on disk, load the service definitions including their endpoint addresses at start-up, keep them in a list and do a foreach when collecting data from all existing servers.
The key part of your logic is in the following part of your code:
new EndpointAddress("http://bar.com/Service")
Do a
foreach (ServiceDefinition sd in ServiceDefinitions)
{
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress(sd.EndPointAddress)))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
GoGetTheData();
}
At the end I have used the following implementation:
On application start I register the ChannelFactory type without the endpoint address.
And I use named parameter to register the client so i can be able to assign the address later when I actually call the service.
builder.RegisterType<ChannelFactory<IService>>(new BasicHttpBinding())
.SingleInstance();
builder.Register((c, p) => c.Resolve<ChannelFactory<IService>>().CreateChannel(p.Named<EndpointAddress>("address")))
.UseWcfSafeRelease();
and then I use the service client at runtime like this:
public Data GetData(string url)
{
EndpointAddress address = new EndpointAddress(url);
NamedParameter parameter = new NamedParameter("address", address);
var service = _autofacContainer.Resolve<IService>(parameter);//this is what I have been looking for
Response response = service.GetData();
return CreateDataFromResponse(response);
}
this way I can call the GetData method for each address in the database. And I'm going to able to add more addresses at runtime without code or configuration editing.

Debugging a WCF project

I constructed a little solution containing 4 projects:
Contract: contains my (t4 generated) entities and interface to my service
Service: contains my (t4 generated) context and implementation of my service
Host: contains the bare minimum to host a service
ServiceHost host = new ServiceHost(typeof(InleerAppService));
try
{
host.Open();
Console.WriteLine("The service is ready!");
Console.ReadKey();
host.Close();
}
catch (CommunicationException cex)
{
Console.WriteLine(cex.Message);
}
Client:
var factory = new ChannelFactory("InleerAppService");
IInleerAppService service = factory.CreateChannel();
var result = service.ReturnInput("test string"); // just returns the input string, this works!
Console.WriteLine(result);
var result2 = service.GetAllCompanies(); // this doesn't and crashes the client
foreach (Company c in result2)
{
Console.WriteLine(c.Name);
}
Console.ReadKey();
You understand I would like to figure out what is going. But I don't really understand how I can debug this. First I start the host with ctrl+F5, then the client. But this doesn't allow me to debug. How should I go to that, using this setup? I know there are more ways to work with services, but for this part I'd just want to focus on this setup.
You can setup the solution to start multiple projects and just hit F5. To set this up, right click on the solution and go to properties. Select start up project under common properties. And choose both your service and client projects for startup.
Another way to debug is to select the service project, right click and go to debug -> start new instance. Next, do the same thing for client project. Now you should have both service and client projects running under debug mode.

How do I supply a specific instance of a class, to expose as my WCF service

I have a class that implements a plugin for an existing application.
I also have exposed that class as a WCF service. That part is working so far. The problem I am running into is that the application I am plugging into creates the instance of my class that I want to use.
Is there a way to pass an existing class instance to the WCF service host, to expose as a service endpoint?
I know (or can figure out) how to make a singleton instance of a WCF service, but that still won't help me. From what I can tell, the singleton instance will still be created and provided by WCF.
I have thought of other approaches, but I'd rather take this one if it is available to me.
Some code. This is in the constructor of my plugin:
// Setup the service host
var baseAddress = new Uri("http://localhost:8080/MyService/");
this.serviceHost = new ServiceHost(this.GetType(), baseAddress);
// Add our service endpoint
// Todo: Is there somewhere around here that I can provide an instance?
// Maybe in behavior somewhere?
this.serviceHost.AddServiceEndpoint(
typeof(ITheInterfaceMyClassDerivesFrom),
new BasicHttpBinding(),
""
);
// Add metadata exchange (so we see something when we go to that URL)
var serviceMetadataBehavior = this.serviceHost.Description.Behaviors
.Find<ServiceMetadataBehavior>();
if (serviceMetadataBehavior == null)
this.serviceHost.Description.Behaviors.Add(new ServiceMetadataBehavior());
this.serviceHost.AddServiceEndpoint(
typeof(IMetadataExchange),
new CustomBinding(new HttpTransportBindingElement()),
"MEX"
);
This is in the plugin's OnStartedUp method (called by the application I am plugging into):
serviceHost.Open();
You need to use the other constructor for ServiceHost if you want to do this - check out the MSDN docs at http://msdn.microsoft.com/en-us/library/ms585487.aspx
public ServiceHost(
Object singletonInstance,
params Uri[] baseAddresses
)

pointer to service from ServiceHost

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.

WCF Service - runtime not seeing the ServiceContract on Interface

I'm new to WCF and trying to get my first service running. I'm close but stuck on this problem.
In my interface definition file, I have this:
[ServiceContract(Namespace="http://mysite.com/wcfservices/2009/02")]
public interface IInventoryService
{
[OperationContract]
string GetInventoryName(int InventoryID);
}
Then I have my class file (for the service) that inherits it:
public class InventoryService : IInventoryService
{
// This method is exposed to the wcf service
public string GetInventoryName(int InventoryID)
{
return "White Paper";
}
Finally, in my Host project I have this:
ServiceHost host = new ServiceHost(typeof(Inventory.InventoryService));
host.AddServiceEndpoint(typeof(Inventory.InventoryService), new NetTcpBinding(),
"net.tcp://localhost:9000/GetInventory");
host.Open();
Everything compiles fine, and when the host goes to add the service endpoint, it bombs with this: "The contract type Inventory.InventoryService is not attributed with ServiceContractAttribute. In order to define a valid contract, the specified type (either contract interface or service class) must be attributed with ServiceContractAttribute."
I know I'm missing something simple here. I have the interface clearly marked as a service contract and there's a reference to that project in the Host project.
ServiceHost host = new ServiceHost(typeof(Inventory.InventoryService));
host.AddServiceEndpoint(typeof(Inventory.InventoryService), new NetTcpBinding(),
"net.tcp://localhost:9000/GetInventory");
host.Open();
If your ServiceContract attribute is on the Interface not the concrete class, try the following:
ServiceHost host = new ServiceHost(typeof(Inventory.InventoryService));
host.AddServiceEndpoint(typeof(Inventory.IInventoryService), new NetTcpBinding(),
"net.tcp://localhost:9000/GetInventory");
host.Open();