I have two classes implementing a contract at a service which are consumed from a factory on the client like shown below.
[ServiceContract]
public interface MyInterface {
void DoSomething()
}
public class A : MyInterface {
public void DoSomething(){
"Hi I'm A"
}
}
public class B : MyInterface {
public void DoSomething(){
"Hi I'm B"
}
}
public class MyFactory <TMyInterface> {
void DoSomething(){
TMyInterface.DoSomething()
}
}
The client must remain the same. My question is how can I choose at the server side which implementation of MyInterface to use, by passing the type parameter using .config file in WCF
I read other post but I don't understand yet :(
It is possible to do it, and there are a few ways to do it.
One possibility is to create a "routing" service which will contain the "public" address which the client always talks to. This routing service can then, based on some configuration, redirect the call to the appropriate "real" service.
Another way is to actually have a process which starts both services, but their addresses are defined in config. If you use the same binding and the same contract (which is the case), then you can "flip-flop" the service address when you want to change the service which will receive the calls from the client. For example, this configuration directs the requests to the endpoint at "http://machine-name:8000/Service" to service A. Notice that, since you define service hosts for both services, you actually need to have a base address for service B as well - in this case I used named pipes, which cannot be accessed via different machines.
<system.serviceModel>
<services>
<service name="A">
<host>
<baseAddresses>
<add baseAddress="http://machine-name:8000/Service"/>
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding" contract="MyInterface" />
</service>
<service name="B">
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/ServiceBackup"/>
</baseAddresses>
</host>
<endpoint address="" binding="netNamedPipeBinding" contract="MyInterface" />
</service>
</services>
</system.serviceModel>
when you want to change the address for B, you'd then swap the addresses.
<system.serviceModel>
<services>
<service name="A">
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/ServiceBackup"/>
</baseAddresses>
</host>
<endpoint address="" binding="netNamedPipeBinding" contract="MyInterface" />
</service>
<service name="B">
<host>
<baseAddresses>
<add baseAddress="http://machine-name:8000/Service"/>
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding" contract="MyInterface" />
</service>
</services>
</system.serviceModel>
The hosting program would look like this:
public static void HostServices()
{
ServiceHost hostA = new ServiceHost(typeof(A));
ServiceHost hostB = new ServiceHost(typeof(B));
hostA.Open();
hostB.Open();
Console.WriteLine("Press ENTER to close");
Console.ReadLine();
hostA.Close();
hostB.Close();
}
Now, if your services are hosted in IIS (webhost), then it's a little harder. Since the "normal" activation requires a .svc file to be part of the endpoint address, and each .svc file is associated with a single class, the address for A would be something like http://machine-name/services/a.svc while the address for B would be something like http://machine-name/services/b.svc. So what you'd need to do in this case is to create a custom ServiceHostFactory, and use the ASP.NET Routes integration to create a .svc-less URL for your service. Then you'd use something similar to the previous example to decide which service will be activated.
Related
I just spend 4 hours googling around trying to find why i cant consume (add service reference) WCF hosted in Windows service. Error i am getting is this
The URI prefix is not recognized.
Metadata contains a reference that cannot be resolved: 'net.tcp://localhost:8523/Service1'.
Could not connect to net.tcp://localhost:8523/Service1. The connection attempt lasted for a time span of 00:00:02.0158574. TCP error code 10061: No connection could be made because the target machine actively refused it 127.0.0.1:8523.
No connection could be made because the target machine actively refused it 127.0.0.1:8523
If the service is defined in the current solution, try building the solution and adding the service reference again.
I created a small service following this tutorial https://msdn.microsoft.com/en-us/library/ff649818.aspx to the letter. I'm stuck on step 8 where i get error i posted above.
My App.config (WCF configuration)
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<bindings />
<services>
<service name="WcfServiceLibrary1.Service1">
<endpoint address="" binding="netTcpBinding" bindingConfiguration=""
name="netTcpEndpoint" contract="WcfServiceLibrary1.IService1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="" binding="mexTcpBinding" bindingConfiguration=""
name="mexTCPendpoint" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8523/Service1" />
</baseAddresses>
<timeouts closeTimeout="00:01:00" openTimeout="00:02:00" />
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
<serviceDebug httpHelpPageEnabled="false" httpsHelpPageEnabled="false"
includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Service.1.cs
public partial class Service1 : ServiceBase
{
internal static ServiceHost MyServiceHost = null;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (MyServiceHost != null)
{
MyServiceHost.Close();
}
MyServiceHost = new ServiceHost(typeof(Service1));
MyServiceHost.Open();
}
protected override void OnStop()
{
if (MyServiceHost != null)
{
MyServiceHost.Close();
MyServiceHost = null;
}
}
}
Things I've tried so far:
Turning off windows firewall
Opening port TCP 8523 (in and outbound rules)
Changing port numbers
Checking netstat -atn (TCP port 8523 is not listnening)
Double checking that serviceProcessInstaller1 has account option set to Network service
Double checking that serviceInstaller1 has StartType option set to Automatic
Double checking that Service1 is Started in services.msc
Enabled all Net.XXX Services in services.msc
Remove then Added TCP Activation and TCP Port Sharing in "Turn windows features on or off"
Ran out of ideas
Any help as to why i can't consume WCF is highly appreciated.
I've recently implemented a CustomServiceHostFactory and am wondering how to debug it by hitting breakpoints in code. Here is the factory:
public class CustomHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
ServiceHost host = new ServiceHost(serviceType, baseAddresses);
//configure WsHttpBinding
ConfigureServiceThrottling(host);
return host;
}
private void ConfigureWshttpBinding(ServiceHost host)
{
//Do something here....
}
private void ConfigureServiceThrottling(ServiceHost host)
{
ServiceThrottlingBehavior throttle = host.Description.Behaviors.Find<ServiceThrottlingBehavior>();
if (throttle == null)
{
throttle = new ServiceThrottlingBehavior
{
MaxConcurrentCalls = 100,
MaxConcurrentSessions = 100,
MaxConcurrentInstances = 100
};
host.Description.Behaviors.Add(throttle);
}
}
}
I create this in an empty web project and here are the pertinent Web.config contents.
<service name="Company.Project.Business.Services.AccountService" behaviorConfiguration="MyServiceTypeBehaviors">
<endpoint address=""
binding="basicHttpBinding"
contract="Company.Project.Business.Contracts.Service.IAccountService"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
<service name="Company.Project.Business.Services.AccountClassService" behaviorConfiguration="MyServiceTypeBehaviors">
<endpoint address=""
binding="basicHttpBinding"
contract="Company.Project.Business.Contracts.Service.IAccountClassService"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
<serviceHostingEnvironment>
<!-- where virtual .svc files are defined -->
<serviceActivations>
<add service="Company.Project.Business.Services.AccountService"
relativeAddress="AccountService.svc"
factory="Company.Project.WebHost.CustomHostFactory"/>
<add service="Company.Project.Business.Services.AccountClassService"
relativeAddress="AccountClassService.svc"
factory="Company.Project.WebHost.CustomHostFactory"/>
</serviceActivations>
</serviceHostingEnvironment>
I publish this to IIS and can successfully browse to and consume the services. Here is a path to one for example.
http://company.server.local/Project/Account/AccountService.svc
I am now trying to programmatically apply WsHttpBinding with open/close/send timeouts, readerQuotas, etc. I am trying to do this all in code and it would be helpful if I could step into the CustomeHostFactory to debug but have no idea how to do that. Any help is much appreciated. Thanks.
Ok, I was totally confused here. Instead of trying to attach to the w3wp process, I just set the project with the CustomHostFactory as the startup project in Visual Studio. I put a breakpoint in the protected override ServiceHost CreateServiceHost method.
Then, when I run the project http://localhost:58326/ comes up in a browser. I then had to actually browse to an endpoint like so: http://localhost:58326/Account/AccountService.svc in order to hit the breakpoint.
Now I can debug my programmatic configuration of the service. Hopefully this helps someone else.
I have a WCF service hosted under IIS.
I have the following configuration:
<services>
<service name="BillboardServices.LoginService" behaviorConfiguration="LoginServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://myip/LoginService/" />
</baseAddresses>
</host>
<endpoint address="" name="LoginService" binding="basicHttpBinding" contract="BillboardServices.ILoginService" />
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
If I enter http://myip/LoginService/, I get a 404.
If I enter http://myip/Service1.svc, I get the service metadata.
What changes to the configuration do I need in order for the service to be accessible through the nice url?
Thank you.
In order to have and extensionless service, you need to use WCF 4 and init the routing engine in the global.asax file like so:
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
// Edit the base address of Service1 by replacing the "Service1" string below
RouteTable.Routes.Add(new ServiceRoute("Service1", new WebServiceHostFactory(), typeof(Service1)));
}
There have been a few questions similar to this, and I am VERY new to MSMQ.
I have been trying to link a ServiceContract and associated DataContract to MSMQ and have set up endpoints so that the DataContact message ends up in MSMQ.
I have verified that the message is being correctly generated by the WCF service and I can also see that messages are in the Journal of the queue I am sending to, but not in the actual Queued Message area where I'd expect them.
I am not using transactions at the moment, and I have set security to none. I attach the relevant code, though my feeling is that I am missing something fundamental through ignorance of MSMQ. Any pointers would be appreciated.
Service & Data Contracts
[DataContract]
public class RegistrationMessage : IRegistrationMessage
{
[DataMember]
public string EMailAddress { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
[ServiceContract]
public interface IRegistration
{
[OperationContract(IsOneWay = true)]
void Register(RegistrationMessage message);
}
app.config of the WCF host
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MetaDataBehaviour">
<serviceMetadata httpGetEnabled="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netMsmqBinding>
<binding name="msmq"
deadLetterQueue="System" durable="true"
exactlyOnce="false"
receiveContextEnabled="false"
useMsmqTracing="true">
<security mode="None" />
</binding>
</netMsmqBinding>
</bindings>
<services>
<service behaviorConfiguration="MetaDataBehaviour" name="Client.AuthenticationService.RegistrationService">
<endpoint address="net.msmq://localhost/private/AuthenticationQueue"
binding="netMsmqBinding"
bindingConfiguration="msmq" name="msmq"
contract="Global.DomainModel.IRegistration" />
<endpoint address="mex" binding="mexHttpBinding" name="mex" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/Registration/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
I don't know WCF but I can comment on the MSMQ side.
"I have verified that the message is
being correctly generated by the WCF
service and I can also see that
messages are in the Journal of the
queue I am sending to, but not in the
actual Queued Message area where I'd
expect them."
This means that the message was delivered AND processed.
Journal messages associated with a queue are only created when the message has been delivered AND then read/received by an application.
If a message was just delivered and not read/received then there would not be a journal message created yet and the original would remain in the destination queue.
If a message was delivered but then purged/deleted, there would not be a journal message created as the original message was not read/received successfully by an application.
Cheers
John Breakwell
Well, since no-one seems to have an answer to why the messages are being consumed, I have written a workaround in the service implementation which uses the native System.Messaging classes. This is a shame because according to the documentation, one should be able to send a message to a queue without code (as long as the endpoints are described correctly).
Here is the code I have used, for anyone in this predicament.
In the host console project, I modified the App.Config endpoint by commenting out the net.msmq and adding a wsHttpBinding to make it a regular WCF service.
<services>
<service behaviorConfiguration="MetaDataBehaviour" name="Client.AuthenticationService.RegistrationService">
<!-- <endpoint address="net.msmq://localhost/private/authenticationqueue"
binding="netMsmqBinding"
bindingConfiguration="msmq"
name="msmq"
contract="Global.DomainModel.IRegistration" /> -->
<endpoint address="http://localhost:8080/Registration"
binding="wsHttpBinding"
bindingConfiguration=""
contract="Global.DomainModel.IRegistration" />
<endpoint address="mex"
binding="mexHttpBinding"
name="mex"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/Registration/" />
</baseAddresses>
</host>
</service>
In the service implementation, I added the following (leaving the original Console.WriteLine statements in for testing purposes:
public void Register(RegistrationMessage message)
{
MessageQueue queue = new MessageQueue(#".\private$\authenticationqueue");
Message msg = new Message();
msg.ResponseQueue = queue;
msg.Label = "AuthenticationMessage";
msg.Body = message;
queue.Send(msg);
Console.WriteLine("e-mail: " + message.EMailAddress);
Console.WriteLine("First Name: " + message.FirstName);
Console.WriteLine("Last Name: " + message.LastName);
}
This works perfectly and works as expected, hydrating the queue. Now I can write a workflow foundation service to consume it.
Thanks to John for confirming the fact that messages were, in fact, being consumed. I would give you a vote but my level is too low :)
I have a WCF service that is hosted in IIS. I want to use my own IAuthorizationPolicy, and have it configured in the web.config file on the server. I have my auth policy:
namespace MyLib.WCF
{
public class CustomAuthorizationPolicy : IAuthorizationPolicy
{
public CustomAuthorizationPolicy()
{
this.Id = Guid.NewGuid().ToString();
}
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
throw new ApplicationException("Testing custom auth");
}
...
}
}
And in my web.config:
<service behaviorConfiguration="Behavior" name="MyService">
<endpoint address="" binding="wsHttpBinding" contract="IMyService"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
<serviceBehaviors>
<behavior name="Behavior">
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="MyLib.WCF.CustomAuthorizationPolicy, MyLib.WCF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
But my CustomAuthorizationPolicy.Evaluate() method never fires. What am I missing?
Well, the obvious (silly) question is: in your <service>, do you actually reference your behavior configuration??
I.e. do you have:
<system.serviceModel>
....
<service name="YourService" behaviorConfiguration="Behavior">
....
</service>
....
</system.serviceModel>
Just defining all your stuff is nice and well - but unless you've actually referenced it, it won't do you any good (been there, done that myself, too! :-) )
Second (almost as silly) question would be: what binding and security config do you use?? Have you even turned on security at all? If you have <security mode="None">, then your service authorization will obviously never be used, either (since no credentials are being passed to the service at all).
Marc