Missing help description for task based WCF service - c#

I'm currently hosting a few web services from within a WPF application. I also have enabled automatic help pages to simplify the service documentation. Every OperationContract is decorated with a Description attribute, containing information about the method.
However, whenever I take a look at my help pages, I realize that only methods with a return type of void will correctly display their Description attribute here. Methods returning Task or Task<t> will only say "Service at localhost:XXXXX/ServiceEndpoint".
As this pattern is used for IPC, I rely a lot on async operation contracts, so most of them will return a Task or Task<t>. Is there any way to fix this issue so the help gets displayed correctly?
namespace App
{
[ServiceContract]
public interface IMainService
{
[OperationContract]
[WebGet(UriTemplate = "visibility")]
[Description("Gets the main window visibility.")]
Task<bool> GetVisibilityAsync();
[OperationContract]
[WebInvoke(UriTemplate = "visibility", Method = "PUT")]
[Description("Sets the main window visibility.")]
Task SetVisibilityAsync(bool isVisible);
[OperationContract]
[WebInvoke(UriTemplate = "menu", Method = "PUT")]
[Description("Navigates to the main menu.")]
void NavigateToMainMenu();
[OperationContract]
[WebInvoke(UriTemplate = "shutdown", Method = "PUT")]
[Description("Requests the application to shutdown.")]
void RequestApplicationShutdown();
}
}
Here is my app.config
<system.web>
<compilation debug="false" targetFramework="4.5" />
</system.web>
<system.serviceModel>
<services>
<service name="App.MainService" behaviorConfiguration="rpcServiceBehavior">
<endpoint binding="webHttpBinding"
contract="App.IMainService"
behaviorConfiguration="webHttpBehavior"
name="RpcEndpoint"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:25565/main"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="rpcServiceBehavior" >
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

Try to debug with another browser (or in incognito mode).
Explanation:
This is because you probably added a few descriptions, checked it on your browser. Then you added more description tags and checked it again on your browser which, instead of reloading the page, opened the one in cache. Browser will open page in browser in cache instead of reloading it when the page is static.

Related

What requisities must WCF service have to be used as service/web reference?

I am learning WCF these days and probably don't know where to start. I want to create WCF REST Service, which will be accesible through HTTP requests (GET, PUT...). And at same time I want to be able to add this service as service reference or web reference and use them in the Web Application client as ordinary method. This issue is quite wide so I will by grateful for any hint or direction.
At this time, I have functional services and run them on my hosting. I can add Service Reference and Web Reference. Service reference is better for new code, as I reckognized, because it use WCF communication and thus it contains all former communication channels. When I add these references, I can use reference to GetSimpleDataService, but non of its methods. When I try to add these methods as reference, problem with metadata is noted.
WCF interface:
[ServiceContract]
public interface IGetSimpleDataService
{
[OperationContract]
[WebGet(UriTemplate = "User/{ID}")]
User GetUser(string ID);
[OperationContract]
[ScriptMethod(UseHttpGet = true)]
User GetUserByMethod(string ID);
[OperationContract]
[WebGet]
string ActivationTest();
[OperationContract]
[WebMethod]
string WebMethodTest();
}
Web.config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2"/>
</system.web>
<system.serviceModel>
<services>
<service name="StoryHubWCFApp.TestStudentService" behaviorConfiguration="serviceBehavior">
<endpoint address=""
binding="webHttpBinding"
contract="StoryHubWCFApp.ITestStudentService"
behaviorConfiguration="web"
/>
</service>
<service name="StoryHubWCFApp.GetSimpleDataService" behaviorConfiguration="serviceBehavior">
<endpoint address=""
binding="webHttpBinding"
contract="StoryHubWCFApp.IGetSimpleDataService"
behaviorConfiguration="web"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Now I can get data via GET request, but I want to be able use service with Web/Service reference like this.:
string s = MyServices.ActivationTest();
I assume to use methods like this, which returns or takes values other than int and string I should have [DataContracts]? I understood too, I have to use [WebMethod] or [ScriptMethod], but I wasn't successful so far.
Thanks in regards for any correction.
You can so it almost as smoothly as your example shows...
Since your topic is quite broad I won't go in details just give pointers.
For your own classes, to be able to use them as parameters and/or return types. (in your case User) you have to define what and how to be serialized. You can read about that here:
https://msdn.microsoft.com/en-us/library/ms733127(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/ms733811(v=vs.110).aspx
Example:
[DataContract]
public class User
{
// This member is serialized.
[DataMember]
string FullName;
// This is not serialized because the DataMemberAttribute
// has not been applied.
private string MailingAddress;
}
Now you are able to use your class.
For calling the service:
You can add service reference: https://msdn.microsoft.com/en-us/library/bb386386.aspx (this would be a generated proxy to your service)
Or you can use ChannelFactory: https://msdn.microsoft.com/en-us/library/ms734681(v=vs.110).aspx
(with this you are in full control of the code, but might need to do more settings, i.e. endpoints.)

Adding WCF service reference in ASP.NET MVC app fails

I'm trying to add a WCF service to my ASP.NET MVC application. After setting it up, I get an error whenever I want to test it.
Here is my code:
BackgroundTask.svc
public class BackgroundTask : IBackgroundTask
{
public void ShutdownVm()
{
}
public void UpdateTable()
{
}
}
IBackgroundTask.cs
[ServiceContract]
public interface IBackgroundTask
{
[OperationContract]
void ShutdownVm();
[OperationContract]
void UpdateTable();
}
Web.config
<system.serviceModel>
<services>
<service behaviorConfiguration="bgtBehaviour" name="IsolutionsAzureManager.Controllers.BackgroundTask">
<endpoint address="BackgroundTask" binding="basicHttpBinding"
bindingConfiguration="" name="" contract="IsolutionsAzureManager.Controllers.IBackgroundTask" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:44304/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="bgtBehaviour">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
So, whenever I want to test my WFC service, I get this error:
Error: Cannot obtain Metadata from https://localhost:44304/Controllers/BackgroundTask.svc
Error: The HTML document does not contain Web service discovery information.
Can anyone see the mistake?
Because you are attempting to acquire the mex endpoint over Https, you'll also need to switch to mexHttpsBinding and
to enable the httpsGetEnabled setting:
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
Alternatively, obtain the endpoint via http (http://localhost:44304/Controllers/BackgroundTask.svc) and then just switch back to https once the client has built the service reference artifacts.

Ajax enabled WCF in MVC returns 404

I'm trying to add a WCF service to my MVC project to have a general place to put my ajax methods but when i try to call its' methods simply by browsing to the url (e.g. ../Service.svc/HelloWorld) or calling it with jquery ajax I get a 404 page instead.
If i do the same thing in a webforms project, it works without any problem. What am I doing wrong here? Is this not the best practice place to keep ajax methods? Like i said: I want a general place to keep my ajax methods that might be reused from several views so i dont want to put them in the controller of the view if possible
My code:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1:IService1
{
public string HelloWorld()
{
return "HelloWorld";
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet]
string HelloWorld();
}
My configuration:
<system.serviceModel>
<services>
<service name="WebApplication1.Service1">
<endpoint address=""
behaviorConfiguration="AJAXEndpoint"
binding="webHttpBinding"
contract="WebApplication1.IService1" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="AJAXEndpoint">
<webHttp />
<enableWebScript />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" minFreeMemoryPercentageToActivateService="1" />
</system.serviceModel>
You have to add a ServiceRoute in your Global.asax. You have a 404 simply because there are no routes for this request.
For example this could be
routes.Add(new ServiceRoute("ThePath", new WebServiceHostFactory(), typeof(YourService)));
+1 for asp.net Web Api : this is definitely the recommanded way of building a web api in asp.net, especially in an existing mvc project.

ServiceAuthorizationManager not called

I have a very simple ServiceAuthorizationManager (perhaps the simplest) and have followed various tutorials on the web but for some reason none of my breakpoints are hit and this leads to me thinking that its not being called.
Created a WCF Service Application named it WcfTest
Kept the default classes Service1.svc, IService.cs but changed method to return a string
Added a new class that inherits from ServiceAuthorizationManager
Overridden the method CheckAccessCore()
Adjusted web.config so that it uses this manager class
Run the WCF Service Application
Assembly is called WcfTest
All classes live in root of the project no folders or anything
Invoke the method and at this point I am expecting my ServiceAuthorizationManager to be called or am I wrong here? I thought the whole purpose of it was to hit the custom ServiceAuthorizationManager on every request received?
Thanks in advance, Onam.
Any more info required let me know, will be watching this like a hawk as I am very confused when this should apparently be very simple.
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(UriTemplate = "/getIt",ResponseFormat=WebMessageFormat.Json)]
string GetIt();
}
public class Service1 : IService1
{
public string GetIt()
{
return "boo!";
}
}
public class MyServiceMan : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
try
{
//Some stuff here breakpoint set on above line not hit
return false;
}
catch (Exception e)
{
return false;
}
}
}
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="WcfTest.Service1">
<endpoint address=""
contract="WcfTest.IService1"
binding="webHttpBinding">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- 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"/>
<serviceAuthorization serviceAuthorizationManagerType="WcfTest.MyServiceMan,WcfTest" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Are you missing endpoint behavior?
behaviorConfiguration="WebHttpEndpointBehavior"
<endpointBehaviors>
<behavior name="WebHttpEndpointBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
I had the same problem. I resolved it by setting breakpoint in my CustomAuthorizationManager CheckAccessCore method and start debugging my WCF project in Visual Studio. Then I run from my desktop WCF Client App and execute some method and it's done.

Combining WCF SOAP and REST

The REST project works fine, this can be accessed through this address:
http://localhost:8525/Device/Login?deviceID=testid&password=a&serialNum=testserial
I also have WCF SOAP project in my REST project, these two projects are separated in different folders, "SOAP" and "REST".
My problem is that, after I put this code:
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("Device", new WebServiceHostFactory(), typeof(Rest.DeviceComponent)));
}
I can't access now the SOAP service which I was able to access before through this address:
http://localhost:8525/DeviceComponent.svc (using WCFTest Client)
Here is the WebConfig
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- 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="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</modules>
<handlers>
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd"/>
</handlers>
</system.webServer>
</configuration>
And inside Global.asax.cs
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("Device", new WebServiceHostFactory(), typeof(Rest.DeviceComponent)));
}
SOAP sample contract
namespace TSDEVICE.SoapSVC.Interface
{
[ServiceContract]
public interface IDeviceComponent
{
[OperationContract]
Session Login(string deviceID, string password, string serialNum, string ip);
[OperationContract]
bool Logout(DeviceSession session);
[OperationContract]
bool IsLatestVersion(DeviceSession session, int version);
[OperationContract]
byte[] DownloadLatest(DeviceSession details);
[OperationContract]
DateTime GetServerTime(DeviceSession session, long branchID);
[OperationContract]
bool AddDevice(UserSession session, Device deviceitem);
[OperationContract]
bool RemoveDevice(UserSession session, long deviceID);
}
}
And the REST part:
namespace TSDEVICE.Rest
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class DeviceComponent
{
[WebInvoke(UriTemplate = "Login?deviceID={deviceID}&password={password}&serialNum={serialNum}", Method = "POST")]
[OperationContract]
public TMODELDEVICE.Entities.Session Login(string deviceID, string password, string serialNum)
{
string ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
TMODELDEVICE.Logic.DeviceComponent restDC = new TMODELDEVICE.Logic.DeviceComponent();
return restDC.Login(deviceID, password, serialNum, ip);
}
public string Sample()
{
return "Hello";
}
}
}
I have to access SOAP and REST, how can I do that? Thanks a lot!
EDIT
When I try to "Set as Start page" the .svc file, I get this error:
Failed to add a service. Service metadata may not be accessible. Make sure your service is running and exposing metadata.
EDIT 2
Now I found out the real problem.
When ASP.NET compatibility mode in the web.config == true, SOAP fail to work, while REST requires it. What should I do with this? Thanks
I have a REST project that as both REST and SOAP service being exposed. Now I placed an .svc file for the SOAP service to be accessed by some clients.
The below screenshot gives the folder structure of my project, the route configuration in global.asax, Output accessing the Rest Service and accessing the .svc file (SOAP service)
UPDATE:
Please find my web.Config (My application is hosted on IIS):
Please find my class that implements my interface ISampleService:
While I appreciate the solutions listed above - I have a found it is far easier to manage/deploy if you don't over think the problem and follow a KISS principle.
Service Contract: IService.cs
namespace DontTazeMe.Bro
{
[ServiceContract]
public interface IService
{
[OperationContract]
[WebGet]
List<GeoMapData> GetToTheChopper();
}
}
Implementation: Service.cs
namespace DontTazeMe.Bro
{
public class WSDLService : IService
{
public List<GeoMapData> GetToTheChopper()
{
return ItsNotEasyBeingChessy.Instance.GetToTheChopperGeoData();
}
}
public class RESTService : WSDLService
{
// Let's move along folks, nothing to see here...
// Seriously though - there is no need to duplicate the effort made in
// the WSDLService class as it can be inherited and by proxy implementing
// the appropriate contract
}
}
Configuration
<system.serviceModel>
<services>
<!-- SOAP Service -->
<service name="DontTazeMe.Bro.WSDLService">
<endpoint address="" binding="basicHttpBinding" contract="DontTazeMe.Bro.IService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8733/DontTazeMe.Bro/Service/" />
</baseAddresses>
</host>
</service>
<service name="DontTazeMe.Bro.RESTService">
<endpoint address="" binding="webHttpBinding" contract="DontTazeMe.Bro.IService" behaviorConfiguration="Restful" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8733/DontTazeMe.Bro/Rest/Service/" />
</baseAddresses>
</host>
</service>
<behaviors>
<endpointBehaviors>
<behavior name="Restful">
<webHttp defaultOutgoingResponseFormat="Json" defaultBodyStyle="Wrapped" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
This method works just fine without getting carried away with configuration

Categories