Using WCF REST services with Unity on IIS - c#

I am trying to use dependency injection with WCF REST (WebGet) and am having trouble understanding where I would use Unity to build my object to host the web service.
I have seen many examples all over the web, but they seem to all be hosting the service in a console app or Windows service.
Currently my config file looks like this:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service name="Services.MyRestService">
<endpoint address="http://localhost:8732/api" binding="webHttpBinding" contract="Shared.Services.IMyRestService"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior>
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
Currently the way it works is using the built in WCF service host in Visual Studio. However, I'd like to run this in IIS and use Unity to configure my container. Now I am using "bastard injection" to get the job done, but would like to use Unity to supply the data provider implementation, etc.
My question is, what configuration changes do I need to make to host this in IIS and also where do I configure the container and let IIS know about it.
There are no SVC files as this is a WCF Service Library.
Thanks in advance!

The "normal" way to do this would be to implement a custom IInstanceProvider WCF extension point, and add it through configuration. I've only done this with regular SOAP endpoints, but I think it would work the same for WebAPI.
Couple links that might help:
IInstanceProvider (MSDN)
Extending WCF Using IInstanceProvider
IInstanceProvider (MSDN Blog)
Integrating Unity with WCF
The 2nd link covers some of the XML configuration stuff for adding a custom IInstanceProvider to the behavior.

With WCF REST you would use WebServiceFactory and WebServiceHost as your base classes.

Related

My wcf client side shows exception but WcfTestClient Tool works fine

I have coded a WCF server side with netTcpBinding. Then I coded a client side. But it shows exception while excute "var sc = new CommondServiceClient();" at runtime. What should I do?
Below is the exception message:
System.InvalidOperationException
HResult=0x80131509
Message=Could not find default endpoint element that references contract 'ICommondService' 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.
Source=System.ServiceModel
StackTrace:
......
I have tried something:
I can consume the services with WcfTestClient.
The service reference is added by visual studio "add service reference...". I guess I get the service mex data. But I meet runtime exception like above
I also tried generate code with svcutil tool, but it still not work
Here is the wcf config file:
<?xml version="1.0" encoding="utf-8" ?>
<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>
<services>
<service name="ServiceContractor.CommondService">
<endpoint address="" binding="netTcpBinding" contract="ServiceContractor.ICommondService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information,
set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="False" httpsGetEnabled="False"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
It's a self host wcf service:
var baseAddress = new Uri($"net.tcp://localhost:{PORT}/Company/service");
_host = new ServiceHost(typeof(CommondService), baseAddress);
try
{
var smb = _host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null) _host.Description.Behaviors.Add(new ServiceMetadataBehavior());
_host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
_host.AddServiceEndpoint(typeof(ICommondService), new NetTcpBinding(), "");
_host.Open();
}
catch (CommunicationException ce)
{
_host.Abort();
}
I have no idea what is wrong. What document I should ask for? Can you help me?
But I have another question now. Is it possible to get rid of the configuration. So that the application just needs the DLL and knows nothing about the WCF service.
Generally speaking, there are two ways to call the WCF service (Soap service) in the web application.
Generate the client proxy class by adding service reference, this way
commonly need to configure the service settings in the configuration
file(web.config), most of service information need to be called are
stored in the configuration file. For class library project, we have
to migrate the configuration to the configuration file of the actual
project.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
Use the ChannelFactory to create the communication channel, we set
up the service configurations programmatically. From your
description, this way to call the web service might be the
appropriate solution. Customize the service configuration
programmatically in the code. One thing must be noted that all the
service configuration is hard-coded, such as binding type and service
endpoint information. You could refer to the following documents.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-the-channelfactory
Feel free to let me know if there is anything I can help with.

Adding IClientMessageInspector globally to any service reference

I Have a .net solution with two seperate projects,
one is a class library and other as a web application.
All of my Service References placed in my class library project. and all bindings and endpoint configurations done programmatically.
In my Web Application I Added a IClientMessageInspector and BehaviorExtensionElement by reading this link. How to: Inspect or Modify Messages on the Client
I Don't want to add dbcontext access from within my class library, and all my db operations done at web application project.
Now the problem is I want to log every outgoing service call that place in my class library and I Want to configure this globally. (log to db)
for old style web references I was able to add a Soap Extention like the image below, and all calls logged using TraceExtention class.
I need the same ability using Service References.
You can do the same with WCF by adding behavior extensions to web.config.
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="ClientBehavior">
<ClientEndpointBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="ClientEndpointBehavior" type="Custom.WCFMessageInspector, Custom" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
1- Custom.WCFMessageInspector is where your client inspection code reside.
2- Add behaviorConfiguration="ClientBehavior" to the client endpoint.

IIS hosted wcf returns me blank page

I have a wcf application. It has "Service1.svc" file. In the web.config file I specified
http://localhost:2005/EmployeeService.svc
as an endpoint. When clicking browse from Visual Studio there is no problem. But, when I hosted it on IIS server I get a blank page. The interesting thing is, If I remove the address from the web. config this time I can see the service at this address.
http://localhost:2005/EmployeeService.svc
web.config file is as below:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="EmployeeServiceBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="EmployeeServiceBehaviour" name="EmployeeConfiguration">
<endpoint address="http://localhost:2005/EmployeeService.svc" binding="basicHttpBinding"
bindingConfiguration="" contract="IEmployeeConfiguration" />
</service>
</services>
</system.serviceModel>
<system.web>
<compilation debug="true"/>
</system.web>
<system.webServer>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Could you please explain, why I get a blank page on IIS when I provide the adress.
This may happen in HTTP Activation is not installed in Programs and Features in your Windows installation. Check HTTP Activation for both .Net 3.5 and 4.6 to enable opening WCF service via HTTP request to .svc file hosted in IIS.
I think you can remove all text on the address before Service.svc. This might be an issue with different urls.
When you specify the address part, you tell WCF the service is only available on that address. When going through the endpoints, WCF will not find one that matches the given URL and bail out.
When you have multiple endpoints you need the address field. Otherwise you don't.
Read http://msdn.microsoft.com/en-us/library/ms733749(v=vs.110).aspx
And in particular this part:
When hosting with IIS, you do not manage the ServiceHost instance yourself. The base address is always the address specified in the .svc file for the service when hosting in IIS. So you must use relative endpoint addresses for IIS-hosted service endpoints. Supplying a fully-qualified endpoint address can lead to errors in the deployment of the service.
Check if Service attribute in SVC markup equals your implementation class.
Check endpoint contract name. It has to be exactly full name of your interface. Don't add assembly name, it won't work.
Check <serviceMetadata httpGetEnabled="true" />. If false, you get blank page too.
And 4th change which helped me (but I have no idea why): Remove behavior name <behavior name=""> and remove behaviorConfiguration attribute from service element. It becomes default so the behavior will be used anyway.
Be sure you made an Application Pool with active user like this: https://stackoverflow.com/a/19654633/2148387
Be sure you created Web Application where your WCF is hosted
Check if AppPool and WebApp are running
Enable Directory Browse in your Web.config like here: https://stackoverflow.com/a/19630263/2148387

Blank/Empty WSDL file

I'm struggling to have my WCF web service output a wsdl file with no luck so far (it's empty?).
the svc file:
<%# ServiceHost Language="C#" Debug="true" Service="xxx.WCF.SubsetMID" CodeBehind="SubsetMID.svc.cs"
Factory="System.ServiceModel.Activation.WebServiceHostFactory"
%>
the cs file :
namespace xxx.WCF
{
[ServiceContract(Namespace = "SubsetMID")]
public interface ISubsetMID
{
[OperationContract]
[WebInvoke(BodyStyle=WebMessageBodyStyle.Wrapped)]
long[] GetMIDs(Guid guid, int subsetID);
}
[DataContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SubsetMID : ISubsetMID
{
public long[] GetMIDs(Guid guid, int subsetID)
{
[...]
return returnValue;
}
}
}
My web config file :
<system.serviceModel>
<services>
<service name="xxx.WCF.SubsetMID">
<endpoint address=""
binding="wsHttpBinding"
contract="xxx.WCF.ISubsetMID" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment
multipleSiteBindingsEnabled="true"
aspNetCompatibilityEnabled="true"
/>
</system.serviceModel>
I don't get any error when I access the wsd (http://.../SubsetMID.svc?wsdl) but it's just a blank page.
The following is a best guess based on some research I did - I'm not that familiar with RESTful WCF services, so I might be wrong but this should give you a starting point at least.
You didn't specify, but it looks like you're trying to write a RESTful WCF service. I'm not entirely certain, because you use wsHttpBinding in your endpoint, but you also decorate the method in the service with [WebInvoke].
In any event, REST services do not have WSDLs - that's a SOAP thing. Additionally, I believe WCF supports REST with the webHttpBinding. As you are using WebServiceHostFactory in your .svc file, I think this is what is happening:
You do not have any webHttpBinding endpoints defined. WCF will create a default webHttpBinding endpoint, with the address based on the location of the .svc file. However, when default endpoints are used according to WebServiceHost Class:
...the WebServiceHost also disables the HTTP Help page and the Web Services Description Language (WSDL) GET functionality so the metadata endpoint does not interfere with the default HTTP endpoint.
If you're writing a REST service, you won't need a WSDL. If you're planning on having a SOAP service, then use ServiceHostFactory in your .svc file and remove the [WebInvoke] attribute from the method.
Again, this is a (relatively) educated guess, and it may be wrong, but it's a place to start.
Im guessing that you are using a wcf service website project which contains the svc file. if you dont, i strongly recommend you to do that and set it as the startup project in your solution, so you could debug it properly. for me, and good chances that for you too if you followed codeproject step by step instructions, the problem was that i didn't reference the wcf services from the website, thus the directive in the svc couldn't find the service. hope i helped

Minimum configuration to make "Add Service Reference > Discover" work

I have already a few WCF + Windows Service tested and working correctly as run as a Windows Service on remote dev machines. Except for one.
In order to debug, I tried to host the WCF (without the Windows Service) using the built-in "Add Service Reference" and then hosting it in visual studio (don't know what the host is called).
Anyways, I cannot get the Add Service Reference to Discover my services.
Since I am using Windoes Services, I am using TCP stuff. This are some things I have done, all in the winforms app that I am adding the service reference:
Add Project > Properties > Debug > Command line arguments: /client:"WcfTestClient.exe" but the exe doesn't run.
And my App.Config
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:32279/SYNC" binding="netTcpBinding"
bindingConfiguration="tcpSyncBindingConfig" contract="Company.Data.Sync.ILocalCacheSyncContract"
name="tcpSyncClientEP" />
</client>
<bindings>
<netTcpBinding>
<binding name="tcpSyncBindingConfig" maxReceivedMessageSize="6553600" />
</netTcpBinding>
<mexTcpBinding>
<binding name="tcpMexBindingConfig" />
</mexTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="svcBehavior" name="Company.Data.Sync.Services.LocalCacheSyncService">
<endpoint address="net.tcp://localhost:32279/Sync" binding="netTcpBinding"
bindingConfiguration="tcpSyncBindingConfig" name="tcpSyncListenEP"
contract="Company.Data.Sync.Services.ILocalCacheSyncContract" />
<endpoint address="net.tcp://localhost:32279/Sync/mex" binding="mexTcpBinding"
bindingConfiguration="tcpMexBindingConfig" name="tcpMexEP" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="svcBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Side note: the ServiceOperation are returning Microsoft.Synchronization.Data.xxx sutff for example SyncContext. I might have to add a reference in the Winforms? Or will the "Add Service Reference" add them for me? The service and contracts are generated by the Local Database Cache template. perhaps not related to the problem.
After posting, I continued to look for answers and this is what I experimented to be working:
The project file itself e.g. *.csproj for C# project must contain the ProjectTypeGuid for WCF for Visual Studio to even start checking the project for Services.
<ProjectTypeGuids>{3D9AD99F-2412-4246-B90B-4EAA41C64699};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
More GUIDs can be found at http://www.mztools.com/articles/2008/mz2008017.aspx. (Thanks mztools!)
The WCF project can have multiple services, and each of them needs to to have their config defined in the project's App.Config. Each service needs to have a different mex endpoint address. The services can also share a single servicebehavior which has the serviceMetadata extension defined. Mex endpoint does not need behaviors or binding config. But remember to set mex endpoint contract to IMetadataExhchange.
For TCP mex, the serviceMetadata > HttpGetEnabled must be set to false.
I believe this is the minimum settings.
Right click on the service you wish to host on your PC, then right click and go Debug > Start New Instance
Then go your other application (I assume same solution) and copy the URL it is running at.
Then you can start that one with the same method above and debug both projects on your local machine.
I assume this is what you are trying to do. Please let me know if it was something else.

Categories

Resources