TransactionFlowAttribute attribute set to Mandatory but the channel's binding is not configured with a TransactionFlowBindingElement - wcf

I have a big problem. I am trying to create a web service that will work with a distributed transaction.
All the code below is on the server side of the web service(the web service that is called from a client).
I wrote this in my interface:
[ServiceContract]
public interface IClientOperations
{
[OperationContract]
[ServiceKnownType(typeof(TriggerExecInput))]
[ServiceKnownType(typeof(TriggerExecOutput))]
[TransactionFlow(TransactionFlowOption.Mandatory)]
TriggerExecOutput TriggeredProfileDataUpdate(TriggerExecInput triggerInputData, bool isST3StatusActive);
And this in the web.config file:
<services>
<service name="ClientOperationsService" behaviorConfiguration="ServiceBehavior">
<endpoint address="" binding="wsHttpBinding"
bindingConfiguration="wsHttpBinding_Common" contract="SL.STAdmin.Applications.WebAPI.IClientOperations"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding_Common" transactionFlow="true">
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- 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"/>
</behavior>
</serviceBehaviors>
</behaviors>
If I right click the .svc file and click on "View in browser" I get the following error
Exception Details: System.InvalidOperationException: At least one operation on the 'ClientOperations' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding 'BasicHttpBinding' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement.
I have other .svc files that don't use transactions.
They all work well.
I don't understand why it still tries to use the BasicHttpTransaction when I instruct it to use the other binding type.
DOes anyone have any idea what I am doing wrong?
Thank you in advance.

Add this inside your <system.serviceModel> element of your web.config:
<protocolMapping>
<add scheme="http" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_Common"/>
</protocolMapping>

You need to do a few things to get the transaction working.
Add the transactionflow to your operation
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void TransactionSupported(int id, string name);
After that you add an operationbehavior to your implementation
[OperationBehavior(TransactionScopeRequired = true)]
public void TransactionSupported(int id, string name)
{
...
}
In your config file you need to add the transaction flow to your host binding
<system.serviceModel>
...
<bindings>
<netNamedPipeBinding> --> Your binding (don't use basicHttpBinding)
<binding transactionFlow="true"/>
</netNamedPipeBinding>
</bindings>
</system.serviceModel>
And last but not least you need to set the transactionflow of your client to get it working. In my example I do this in my code in my unit test, I think you can also do this in your configuration of your client, in your config file.
var factory = new ChannelFactory<IService>(callback,
new NetNamedPipeBinding() { TransactionFlow = true },
new EndpointAddress("net.pipe://localhost/ping"));

Related

Invoking WCF service in browser similar to asmx

I have made a WCF service in NET 4.0 and it returns the XML, I have tested it using SoapUI and i can see the required response xml. But my WCF service would be called by 3rd party software and they want to use it through a URL like asmx. I have googled and found that i need to make using REST guidelines. However i have not found a proper link showing making web-service using REST and then accessing the method from the browwer similar to web services.
Below is the interface code which i have used to return the format in XML
public interface IService1
{
[WebGet(
UriTemplate = "/GetDocument/",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Xml)]
[OperationContract, XmlSerializerFormat]
XmlElement GetDocument();
// TODO: Add your service operations here
}
Below is my config settings.
<system.serviceModel>
<services>
<service name="BritishLandXML.BritishLandXML1" behaviorConfiguration="metadataBehavior">
<endpoint address="" binding="basicHttpBinding" contract="BritishLandXML.IService1" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<webHttpBinding>
<binding>
<security mode="None"></security>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="rest">
<webHttp helpEnabled="true" faultExceptionEnabled="true" automaticFormatSelectionEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<!-- 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" />
Please let me know how can i achieve this and what exact settings will be required.

WCF inter-service messaging

I am building a system with 2 WCF Services. Both are IIS Hosted. At the moment they both reside in a single VS2010 website app, running on my local IIS7 (Windows 7) using the Derfault Website. I have enabled net.tcp on both.
Service1
accepts HTTP posts using webHttpBinding
wraps the data in a serializable composite object
sends the composite object to Service2 (we hope) using netMsmqBinding
Service2
receives said message and does something with it
Service 1 works as expected, however instead of placing the message on the configured Private Queue, our code is creating a new Queue under "Outgoing Queues" with the handle
DIRECT=TCP:127.0.0.1\private$\Service2/Service2.svc
note the forward slash
Of course Service2 never sees the message - this is the first time I have attempted this structure so I am not certain that Service2 misses the message because of its location, but based on what I have read it would seem so - I have not come across anything mentioning this Queue-creation behaviour.
Questions:
Am I doing this correctly (is there something wrong in the structure, web.config or code)?
When done properly in VS Debug, should Service1's
proxy.ProcessForm(formMessage);
hit breakpoints in my Service2 code, or is there another way to hande Service2 debug (ala windows services for example)?
Service1 Web.Config
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpFormBinding" crossDomainScriptAccessEnabled="true"/>
</webHttpBinding>
<netMsmqBinding>
<binding name="MsmqFormMessageBindingClient" exactlyOnce="false" useActiveDirectory="false" >
<security mode="None">
<message clientCredentialType="None"/>
<transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
</security>
</binding>
</netMsmqBinding>
</bindings>
<client>
<endpoint
name="HttpServiceWebEndpoint"
address=""
binding="webHttpBinding"
bindingConfiguration="webHttpFormBinding"
contract="Service1.HttpService.IHttpServiceWeb" />
<endpoint name="MsmqFormMessageBindingClient"
address="net.msmq://127.0.0.1/private/Service2/Service2.svc"
binding="netMsmqBinding"
contract="MyInfrastructure.IService2" />
</client>
<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"/>
<!--
<serviceAuthenticationManager />
-->
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
On Receipt of an HTTP Post Service1 executes the following:
StreamReader sr = new StreamReader(formData);
string str = sr.ReadToEnd();
var t = HttpUtility.ParseQueryString(str);
Hashtable nvc = new Hashtable();
foreach (string n in t)
{
nvc.Add(n, (string)t[n]);
}
WcfFormMessage formMessage = new WcfFormMessage(nvc);
////create the Service binding
NetMsmqBinding msmq = new NetMsmqBinding("MsmqFormMessageBindingClient");
msmq.Security.Mode = (NetMsmqSecurityMode) MsmqAuthenticationMode.None;
EndpointAddress address = new EndpointAddress("net.msmq://127.0.0.1/private/Service2/Service2.svc");
ChannelFactory<IService2> factory = new ChannelFactory<IFormService>(msmq,address);
IService2 proxy = factory.CreateChannel();
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
proxy.ProcessForm(formMessage);
//do any 'sent to queue logging/updates here
}
I am ready to bet that your problem is related to 127.0.0.1 in your config. Type the machine name in there, even if it is local.

Setting binding in WCF service

This may seem like a really easy question but I can't seem to figure it out at all.
I'm trying to create a new WCF service, and I'm new to having to secure them. I'm using a custom username/password for authentication. The problem [right now anyways] that I seem to be running into is that I can't figure out how to define the service to use the WSHttpBinding (on the service side, not the client side).
Am I missing something incredibly simple? Any pointers and/or recommendations would be greatly appreciated!
EDIT
Here's my code so far:
IAccountService
[ServiceContract]
public interface IAccountService
{
[OperationContract]
bool IsCardValid(string cardNumber);
[OperationContract]
bool IsAccountActive(string cardNumber);
[OperationContract]
int GetPointBalance(string cardNumber);
}
Service web.config
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false 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"/>
<StructureMapServiceBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="StructureMapServiceBehavior" type="Marcus.Loyalty.WebServices.Setup.StructureMapServiceBehavior, Marcus.Loyalty.WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
<service name="Marcus.Loyalty.WebServices.Account.IAccountService">
<endpoint address=""
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_Config"
contract="Marcus.Loyalty.WebServices.Account.IAccountService"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_Config"/>
</wsHttpBinding>
</bindings>
</system.serviceModel>
Testing app (console app)
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter card number");
var number = Console.ReadLine();
var endPoint = new EndpointAddress("http://localhost:59492/Account/AccountService.svc");
var binding = new WSHttpBinding(SecurityMode.Message);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
var cf = new ChannelFactory<IAccountService>(binding, endPoint);
cf.Credentials.UserName.UserName = "testuser";
cf.Credentials.UserName.Password = "Password1!";
var service = cf.CreateChannel();
var balance = service.IsAccountActive(number);
Console.WriteLine("\nBALANCE: {0:#,#}", balance);
Console.Write("\n\nPress Enter to continue");
Console.Read();
}
}
Testing app app.config
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="BasicHttpBinding_IAccountService" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:59492/Account/AccountService.svc"
binding="wsHttpBinding" bindingConfiguration="BasicHttpBinding_IAccountService"
contract="ServiceReference1.IAccountService" name="BasicHttpBinding_IAccountService" />
</client>
</system.serviceModel>
</configuration>
You need to define the abc (address, binding, contract) configuration into de web.config file (you can also do it programmatically. the b part, the binding, you can specify the wsHttpBinding
<system.serviceModel>
<services>
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService"
binding = "wsHttpBinding"
contract = "MyNamespace.IMyContract" />
</service>
</services>
</system.serviceModel>
If you wish to enable security in a proper way, there is a lot of literature and options. You can use certificates, windows based, tokens, ... passing a username & password like a parameter could not be the best way to do it.
There is an extensive sample on MSDN (How to: Specify a Service Binding in code) - but basically, you need to have:
your service contract (IMyService)
an implementation of that service (MyService)
a code where you create your ServiceHost to host your service
You got all of that? Great!
In that case, just do something like this:
// Specify a base address for the service
string baseAddress = "http://YourServer/MyService";
// Create the binding to be used by the service.
WsHttpBinding binding1 = new WsHttpBinding();
using(ServiceHost host = new ServiceHost(typeof(MyService)))
{
host.AddServiceEndpoint(typeof(IMyService), binding1, baseAddress);
host.Open();
Console.ReadLine();
}
and now you should have your service host up and running, on your chosen base address and with the wsHttpBinding defined in code.

WCF Silverlight enabled service "Not Found" error

I'm struggling with the following scenario (here is the big picture):
I have a WCF Silverlight-enabled service (based on the DomainService class) into my Web project. The service is designed to be called by the Silverlight 5 clients and also by non-Silverlight consumers.
The service displays the WSDL info at the address
"http://localhost/mywebapproot/Services/MailService.svc" and therefore it can
be discovered and implemented by any client within the Web
project (which is fine).
Here are the symptoms:
The service can't be called by any
Silverlight client (here is the problem!) The error returned is "The remote server returned an exception: Not Found". If I change the name of the
service in Web.Config (let's say I change
MyCompany.Web.Services.MailService into MailService), the service can
now be called by any Silverlight client but at that time the service
is no longer discoverable.
I put includeExceptionDetailInFaults at True and tried to inspect the service with Fiddler/HTTPDebuggerPro but they didn't give me any detailed information about the exception. It looks to me that the Silverlight clients, in this configuration and for some reason, aren't able to create the .SVC file on the fly.
Here is the implementation:
MailService.svc implementation
<%# ServiceHost Language="C#" Debug="true" Service="MyCompany.Web.Services.MailService" CodeBehind="MailService.svc.cs" %>
MailService.svc.cs implementation
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public sealed partial class MailService : DomainService, IMailService
{
}
IMailService interface
[ServiceContract(ConfigurationName = "MyCompany.Web.Services.IMailService")]
public interface IMailService
{
//Some public methods flagged as [OperationContract] go here
}
Web.Config implementation
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="Secure_Behavior_Configuration">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="Public_MailService_BasicHttpBinding" transferMode="Streamed"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647"
maxBufferPoolSize="2147483647">
<readerQuotas maxArrayLength="21400000" maxStringContentLength="21400000" maxBytesPerRead="21400000"/>
<security mode="None"/>
</binding>
</basicHttpBinding>
<services>
<service name="MyCompany.Web.Services.MailService" behaviorConfiguration="Secure_Behavior_Configuration">
<endpoint
address=""
binding="basicHttpBinding"
bindingConfiguration="Public_MailService_BasicHttpBinding"
contract="MyCompany.Web.Services.IMailService" />
<endpoint
address=""
binding="basicHttpBinding"
bindingConfiguration="Secure_MailService_BasicHttpBinding"
contract="MyCompany.Web.Services.IMailService" />
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Thanks a lot for any help!
Chris.

WCF REST WebGet for user defined parameters

I have below operation contract with WebGet defined as follows.
[OperationContract]
[WebGet(UriTemplate = "UpdateUserDetails/?configdata={_userConfigData}&configresult={_configResult}&clientip={_clientIP}&adminname={AdminName}")]
public void UpdateUserDetails(UserConfigData _userConfigData, ConfigResult _configResult, string _clientIP, string AdminName)
When I run the service, I am getting below error. Any ideas how to fix this issue?
Operation 'UpdateUserDetails' in contract 'UserConfigService' has a query variable named '_userConfigData' of type Service1.WCF.UserConfig.UserConfigData', but type 'Service1.WCF.UserConfig.UserConfigData' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter'.
I will assume that you use Json object to request data.
it should be like this:
[OperationContract]
[WebInvoke(UriTemplate = "UpdateUserDetails?_clientIP={_clientIP}&AdminName={AdminName}", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
public void UpdateUserDetails(UserConfigData _userConfigData, ConfigResult _configResult, string _clientIP, string AdminName)
And JSON data seems to be like this:
{
"_userConfigData":{
"Property1":"value",
"Property2":"value",
"Property3":"value"
..and so on...
},
"_configResult":{
"Property1":"value",
"Property2":"value",
"Property3":"value"
..and so on...
}
}
There is a good application for testing Rest services, you can try to use:
Fiddler
Additional Info
In response to the result "getting Method not found"
You may not have defined the endpoint or the service address properly. Your webconfig file should have this kind of config.
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<bindings>
<basicHttpBinding>
<binding name="soapBinding">
<security mode="None"></security>
</binding>
</basicHttpBinding>
<webHttpBinding>
<binding name="webBinding"></binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="jsonBehavior">
<enableWebScript/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="defaultServiceBehavior">
<!-- 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>
<services>
<!-- USING SOAP-->
<service behaviorConfiguration="defaultServiceBehavior" name="MyProject.WCF.UserConfig.UserConfigService">
<endpoint address="soap" binding="basicHttpBinding" bindingConfiguration="soapBinding" contract="MyProject.WCF.UserConfig.IUserConfigService"></endpoint>
</service>
<!-- USING JSON-->
<service behaviorConfiguration="defaultServiceBehavior" name="MyProject.WCF.UserConfig.UserConfigService">
<endpoint address="json" binding="webHttpBinding" bindingConfiguration="webBinding" behaviorConfiguration="jsonBehavior" contract="MyProject.WCF.UserConfig.IUserConfigService"></endpoint>
</service>
</services>
</system.serviceModel>
The address seems like this:
SOAP
localhost:1706/soap/UserConfigService.svc
JSON
localhost:1706/json/UserConfigService.svc
For better reference you could try to watch here:
How to create simple REST Based WCF Service with JSON format
you have to use string, u cant use an object as query string prameters. it wont convert your query string to an object. those variables should be defined as string.
Here's a link on implementing a custom QueryStringConverter, which will do what you want it to. Note also (mentioned in that post) that it might be better to pass a (possibly) complicated object like UserConfigData or ConfigResult as POST data, rather than in the URL. Considering your method is called "UpdateUserDetails", it's probably best to use a POST (WebInvoke) instead of a GET (WebGet) anyway, in the spirit of REST.