Simple way to create a WCF Binding from an EndpointAddress - wcf

Is there a shortcut for creating the most basic WCF Binding based on the address of a given Endpoint?
Endpoint: net.tcp://localhost:7879/Service.svc
Instead of a big block of if statements...
Binding binding = null;
if (endpoint.StartsWith("net.tcp"))
{
binding = new NetTcpBinding();
}
else if (endpoint.StartWith("http"))
{
binding = new WsHttpBinding();
}
.
.
.
Is there a shortcut in the Framework library that will do this for me that I just can't find or can I not find it because it doesn't publicly exist?

WCF in .NET 4 does that automatically for you - the feature is called default endpoints.
Read about all of WCF 4's new features here: A Developer's Introduction to WCF 4
Default endpoints is about the second or so paragraph into the article.

While WCF 4 supports default service endpoints, it does not support default client endpoints. Unfortunately the methods used by the framework to create default bindings are internal, but the logic behind it is simple, so I have reimplemented it to use on the client side (skipping original caching and tracing logic):
private static Binding GetBinding(string scheme)
{
// replace with ConfigurationManager if not running in ASP.NET
var configuration = WebConfigurationManager.OpenWebConfiguration(null);
var sectionGroup = ServiceModelSectionGroup.GetSectionGroup(configuration);
Debug.Assert(sectionGroup != null, "system.serviceModel configuration section is missing.");
var mapping = sectionGroup.ProtocolMapping.ProtocolMappingCollection
.OfType<ProtocolMappingElement>()
.SingleOrDefault(e => e.Scheme == scheme);
if (mapping == null)
throw new NotSupportedException(string.Format("The URI scheme {0} is not supported.", scheme));
var bindingElement = sectionGroup.Bindings.BindingCollections.Single(e => e.BindingName == mapping.Binding);
var binding = (Binding) Activator.CreateInstance(bindingElement.BindingType);
var bindingConfiguration = bindingElement.ConfiguredBindings.SingleOrDefault(e => e.Name == mapping.BindingConfiguration);
if (bindingConfiguration != null)
bindingConfiguration.ApplyConfiguration(binding);
return binding;
}
Without any configuration this code is equivalent to the code in the question, but you can select and configure your bindings inside the system.serviceModel/protocolMapping section.

After looking at the issue deeper I don't really need to read the configuration in manually. Instead I need to send the binding information along with the address and contract.
http://www.codeproject.com/KB/WCF/WCFDiscovery.aspx?display=PrintAll
I have built a simple component that serializes the binding information.
http://nardax.codeplex.com/

Related

Using WS2007FederationHttpBinding with SOAP 1.1

Is there a way to use WS2007FederationHttpBinding binding, but generate SOAP 1.1 request envelop? I need to use WS2007FederationHttpBinding to authenticate using a bearer token acquired from an STS service. Here is my bindings:
private static Binding GetWS2007FederationHttpBinding()
{
var binding = new WS2007FederationHttpBinding(
WSFederationHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.NegotiateServiceCredential = false;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
//binding.MessageVersion.Addressing = AddressingVersion.WSAddressingAugust2004;
//binding.MessageVersion.Envelope = EnvelopeVersion.Soap11;
// or
//binding.MessageVersion = MessageVersion.Soap11WSAddressingAugust2004;
return binding;
}
But I can't change binding.MessageVersion because it's a read-only property?
You would need a custom binding for that. One way would be to statically declare a custom binding equivalent to WS2007FederationHttpBinding - could take a while to fine tune it. Or you could create WS2007FederationHttpBinding in code (like you do), clone it into a custom binding:
CustomBinding outputBinding = new CustomBinding(federationBinding.CreateBindingElements());
and then find the text message encoding channel and change its soap version.

How to implement an async wcf client for a synchronous service

Is there a way to create an async client for a synchronous WCF service without adding a service reference? This is for a .NET 4 client.
A service reference in Visual Studio is nothing else than a code generator that creates a proxy class with corresponding data elements necessary to call your web service. Of course you can hand build a proxy if you really want to go over tedious and boring work.
Maybe start by decompiling System.ServiceModel.ClientBase using .net reflector?
Do some research on ChannelFactory: http://msdn.microsoft.com/en-us/library/system.servicemodel.channelfactory.aspx
Even when implementing my own client by wrapping a ChannelFactory, I am still using the Add Service reference in another project to create the class definitions and move them into the real project. That's a good compromise.
Here's a simple async service interface:
[ServiceContract(Name = "IService")]
public interface IServiceAsync
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetStuff(string someData, AsyncCallback callback, object state);
IEnumerable<Stuff> EndGetStuff(IAsyncResult result);
}
The .NET contract might look like this:
[ServiceContract]
public interface IService
{
[OperationContract]
IEnumerable<Stuff> GetStuff(string someData);
}
Then in code, assuming you use HTTP, No security and binary message encoding, something like this (Sorry I haven't compiled any of this, just typed it using some of the code I have written for projects):
//Create a binding for the proxy to use
HttpTransportBindingElement httpTransportBindingElement;
httpTransportBindingElement = new HttpTransportBindingElement();
absoluteServiceUri = new Uri(absoluteServiceUri.OriginalString + BinaryEndpointUri, UriKind.Absolute);
}
//Create the message encoding binding element - we'll specify binary encoding
var binaryMessageEncoding = new BinaryMessageEncodingBindingElement();
//Add the binding elements into a Custom Binding
var customBinding = new CustomBinding(binaryMessageEncoding, httpTransportBindingElement);
// Set send timeout
customBinding.SendTimeout = this.SendTimeout;
var factory = new ChannelFactory<IServiceAsync>(customBinding, new EndpointAddress(absoluteServiceUri, new AddressHeader[0]));
var channel = factory.CreateChannel();
channel.BeginGetStuff(Bla, results => { // Do something }, null);

Configuring WCF client and service for use with protobuf-net

I decided to open a new question about this matter, maybe expanding this question, not having found a precise answer about the issue anywhere on the Internet.
I want to use protobuf-net to serialize/deserialize messages exchanged between my WCF client and service. The service is self-hosted in a Windows Service. Both client and service are configured programmatically, using a custom binding very similar to wsHttpBinding. Service reference code is generated using "Add Service Reference" option in Visual Studio. The ORM used on the WCF service is EntityFramework 4 and it's code is generated using EF 4.x POCO Generator. More info about my service configuration can be found in a question I started here (that's where I described that my current serializer is DataContractSerialzizer).
I have only tested protobuf-net with one service operation which returns a list of custom DTOs.
Here is the operation (be advised that I just did a copy-paste of my code to here, there might be some fields named in my domestic language, not English):
public static List<OsobaView> GetListOsobas()
{
Database DB = new Database(); // EF object context
var retValue = DB.Baza.Osoba
.Select(x => new OsobaView
{
ID = x.ID,
Prezime = x.Prezime,
Ime = x.Ime,
Adresa = x.Adresa,
DatumRodjenja = x.DatumRodjenja,
JMBG = x.JMBG
});
return retValue.ToList();
}
Here is the definition of OsobaView class:
[ProtoContract]
public class OsobaView
{
[ProtoMember(1)]
public int ID;
[ProtoMember(2)]
public string Prezime;
[ProtoMember(3)]
public string Ime;
[ProtoMember(4)]
public string Adresa;
[ProtoMember(5)]
public DateTime DatumRodjenja;
[ProtoMember(6)]
public string JMBG;
}
As I am using "Add Service Reference" to generate the reference code, I had to use one of the two work-arounds in order to have my client recognize ProtoContracts and members:
using a shared assembly for DTOs (which is not an ideal solution in my case except for custom DTOs, due to the fact that I pass EF-generated POCOs to the client)
using ProtoPartialMember approach
I used both of them and I used both v1 and v2 of protobuf-net, all solutions yielded similar results which led me to believe my client is not deserializing at all. Read on.
Let's consider cases where I used the ProtoPartialMember approach. At first I used v2. I love the way ProtoOperationBehavior can be used. Here is the service operation to be invoked:
[ProtoBuf.ServiceModel.ProtoBehavior]
public List<OsobaView> GetListOsobas()
{
return OsobaQueries.GetListOsobas();
}
Here is how I replaced DataContractSerializerOperationBehavior with ProtoOperationBehavior for the needed service operation on client side:
OperationDescription op = Service.Proxy.Endpoint.Contract.Operations.Find("GetListOsobas");
if (op != null)
{
DataContractSerializerOperationBehavior dcsBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsBehavior != null)
op.Behaviors.Remove(dcsBehavior);
op.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoOperationBehavior(op));
}
And of course, here is the above mentioned work-around implementation for DTO:
[ProtoPartialMember(1, "ID")]
[ProtoPartialMember(2, "Prezime")]
[ProtoPartialMember(3, "Ime")]
[ProtoPartialMember(4, "Adresa")]
[ProtoPartialMember(5, "DatumRodjenja")]
[ProtoPartialMember(6, "JMBG")]
[ProtoContract]
public partial class OsobaView
{
}
Now when I call this service operation from my client, I get null. But Fiddler disagrees. It clearly says, in response header:
Content-Length: 1301963
Content-Type: application/soap+xml; charset=utf-8
...and in the message body:
<s:Body>
<GetListOsobasResponse xmlns="http://tempuri.org/">
<proto>CkMIpHES .../* REALLY LONG RESPONSE */... IyMDAxOA==</proto>
</GetListOsobasResponse>
</s:Body>
Then I thought, let's try with v1. On the service side, I haven't changed much. I just removed the reference to v2 .DLL and replaced it with a reference to v1 .DLL. On the client side, I had to remove the code to add ProtoOperationBehavior to my service operation behaviors and added the following line instead:
Service.Proxy.Endpoint.Behaviors
.Add(new ProtoBuf.ServiceModel.ProtoEndpointBehavior());
I fired it up, invoked the operation, and this time the result is not null. This time it is a list of blank fields. Again, Fiddler couldn't agree because it again said the same what it said before. The same content length and the same message body.
What's going on here?
P.S. If it's worth anything, here is the WCF configuration:
CustomBinding customBinding = new CustomBinding();
customBinding.CloseTimeout = TimeSpan.FromMinutes(10);
customBinding.OpenTimeout = TimeSpan.FromMinutes(10);
customBinding.ReceiveTimeout = TimeSpan.FromMinutes(10);
customBinding.SendTimeout = TimeSpan.FromMinutes(10);
HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
httpsBindingElement.AllowCookies = false;
httpsBindingElement.BypassProxyOnLocal = false;
httpsBindingElement.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
httpsBindingElement.MaxBufferPoolSize = 20480000;
httpsBindingElement.MaxBufferSize = 20480000;
httpsBindingElement.MaxReceivedMessageSize = 20480000;
httpsBindingElement.RequireClientCertificate = true;
httpsBindingElement.UseDefaultWebProxy = true;
TransportSecurityBindingElement transportSecurityElement = new TransportSecurityBindingElement();
transportSecurityElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(new UserNameSecurityTokenParameters());
transportSecurityElement.EndpointSupportingTokenParameters.SetKeyDerivation(false);
TransactionFlowBindingElement transactionFlowElement = new TransactionFlowBindingElement();
TextMessageEncodingBindingElement textMessageEncoding = new TextMessageEncodingBindingElement();
textMessageEncoding.MaxReadPoolSize = 20480000;
textMessageEncoding.MaxWritePoolSize = 20480000;
textMessageEncoding.ReaderQuotas = XmlDictionaryReaderQuotas.Max;
ReliableSessionBindingElement reliableSessionElement = new ReliableSessionBindingElement();
reliableSessionElement.ReliableMessagingVersion = ReliableMessagingVersion.WSReliableMessagingFebruary2005;
customBinding.Elements.Add(transportSecurityElement);
customBinding.Elements.Add(transactionFlowElement);
customBinding.Elements.Add(textMessageEncoding);
customBinding.Elements.Add(reliableSessionElement);
customBinding.Elements.Add(httpsBindingElement);
EndpointAddress endpoint = new EndpointAddress(new Uri(ServiceAddress));
Service.Proxy = new BazaService.BazaClient(customBinding, endpoint);
Service.Proxy.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, CertificateSubject);
CustomBehavior behavior = Service.Proxy.Endpoint.Behaviors.Find<CustomBehavior>();
if (behavior == null)
{
Service.Proxy.Endpoint.Behaviors.Add(new CustomBehavior()); // message inspector
}
Service.Proxy.Endpoint.Contract.Behaviors.Add(new CyclicReferencesAwareContractBehavior(true));
Service.Proxy.Endpoint.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoEndpointBehavior());
/* code used for protobuf-net v2
OperationDescription op = Service.Proxy.Endpoint.Contract.Operations.Find("GetListOsobas");
if (op != null)
{
DataContractSerializerOperationBehavior dcsBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsBehavior != null)
op.Behaviors.Remove(dcsBehavior);
op.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoOperationBehavior(op));
} */
Service.Proxy.ClientCredentials.UserName.UserName = LogOn.UserName;
Service.Proxy.ClientCredentials.UserName.Password = LogOn.Password;
Service.Proxy.Open();
EDIT
To provide even more information, I have read what's written there but it didn't help. I have deleted the service reference generated by Visual Studio and created my own, sharing the whole service contract, but nothing has changed.
After concentrating a bit better, I decided to restart the solution from scratch. I created one class library for the EDMX with it's POCOs, one for ServiceContract and DataContracts and one for the actual WCF service implementation. Then I shared those two libraries containing ServiceContract and DataContracts, and POCOs with the WCF client and tried again, which yielded the same results as before. After trying some other operations which didn't use protobuf-net for serialization, turned out they behaved the same as the first one, resulting in empty fields (!).
The thing was that, I screwed my WCF client's .datasource files while refactoring after I decided to use the assembly sharing technique. So this was a typical PEBKAC, it of course works fine when done properly. Great work with protobuf-net, Marc Gravell!

Castle Windsor WCF Client service registration based on convention

I have a client-server application, which intercommunicate via WCF. They also both use Castle Windsor for resolving dependencies.
My goal, is to completely avoid having to explicitly register server, or client WCF endpoints.
I have achieved the server-side by 'convention' using the following code
// registers all services which implement exactly 1 [ServiceContract]
_windsorContainer.Register(
AllTypes.FromThisAssembly().IncludeNonPublicTypes().Where(
t => 1 == (from i in t.GetInterfaces() where i.IsDefined(typeof(ServiceContractAttribute), true) select i).Count())
.Configure(c => c.LifeStyle.PerWcfSession()
.ActAs(new DefaultServiceModel().AddEndpoints(
WcfEndpoint.BoundTo(new NetTcpBinding())
.At("net.tcp://" + LocalAddress.ToString() + ":7601/" + c.ServiceType.Name),
WcfEndpoint.FromEndpoint(new UdpDiscoveryEndpoint())
))
).WithService.Select(
(Type type, Type[] baseTypes) => from i in type.GetInterfaces() where i.IsDefined(typeof(ServiceContractAttribute), true) select i
)
);
This code will find all classes in the current assembly, and any which implement a service-contract interface (identified by the ServiceContract attribute) will be registered (with UDP discovery) at the address "net.tcp://localhost:7601/[service-contract-interface-name]".
Now, I just want the client-side of the equation.
Typically, to use castle to generate client-proxies for WCF contracts, the following code will work:
var model = new DefaultClientModel
{
Endpoint = WcfEndpoint.ForContract<IServiceContract>().BoundTo(new NetTcpBinding()).Discover(typeof(IServiceContract))
};
container.Register(
Component.For<ChannelReconnectPolicy>(),
Castle.Facilities.WcfIntegration.WcfClient.ForChannels(model),
);
What I want, is for Castle to do this kind of registration for all 'service-contract' interfaces in a given assembly - however the AllTypes helper seems to return only classes, not interfaces (which I guess makes it an 'AllClasses', not 'AllTypes'!)... Can Castle do this, and what is the syntax? Krzysztof? (help!)
Thanks!
Apologies for such a late reply - a developer's life is never a quiet one!
I dug out the code, and its not as 'succinct' as I would have liked, perhaps someone can look at integrating something like this in to Castle... but here goes...
// these are all the namespaces that will be scanned for WCF service contracts
string[] remoteServiceNamespaces
= new string[] { "MyContracts.Services", "Interlinq" };
// everything from here on is in the Castle DLLs (all the types)
List<IRegistration> clientContractRegistrations = new List<IRegistration>();
foreach (
var interfaceContract in
(from s in remoteServiceNamespaces
select (from i in Assembly.LoadWithPartialName(s).GetTypes()
where i.IsInterface
&&
i.IsDefined(typeof(ServiceContractAttribute), false)
select i)).SelectMany( x => x )
)
{
ServiceContractAttribute attr
= Attribute.GetCustomAttribute(
interfaceContract,
typeof(ServiceContractAttribute))
as ServiceContractAttribute;
if (null != attr)
{
WcfClientModelBase model = null;
// here we handle the case of the service being duplex...
if (null != attr.CallbackContract)
{
model = new DuplexClientModel
{
// All the automatically registered services will use NetTcp,
// and will discover their addresses (you could use binding
// inference aswell if you liked)
// here I have a method called 'CreateNetTcpBinding' which
// creates my custom binding that ALL my services use.
Endpoint =
WcfEndpoint.ForContract(interfaceContract)
.BoundTo(CreateNetTcpBinding())
.Discover(interfaceContract)
.PreferEndpoint(list => list[0])
}.Callback(_windsor.Resolve(attr.CallbackContract));
}
else
{
model = new DefaultClientModel
{
Endpoint = WcfEndpoint.ForContract(interfaceContract)
.BoundTo(new NetTcpBinding())
.Discover(interfaceContract)
.PreferEndpoint(list => list[0])
};
}
clientContractRegistrations.Add(WcfClient.ForChannels(model));
}
}
// now that we've built our registration list, let's actually register
// them all with Windsor
_windsor.Register(
clientContractRegistrations.ToArray()
);

How can I set the maxItemsInObjectGraph property programmatically from a Silverlight Application?

I have a Silverlight 3.0 application that is using a WCF service to communicate with the database, and when I have large amounts of data being returned from the service methods I get Service Not Found errors. I am fairly confident that the solution to it is to simply update the maxItemsInObjectGraph property, but I am creating the service client progrogrammatically and cannot find where to set this property. Here is what I am doing right now:
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
MaxReceivedMessageSize = int.MaxValue,
MaxBufferSize = int.MaxValue
};
MyService.MyServiceServiceClient client = new MyService.MyServiceProxyServiceClient(binding, new EndpointAddress(new Uri(Application.Current.Host.Source, "../MyService.svc")));
It's not defined in binding, but in Service Behavior.
In Silveright, maxItemsInObjectGraph defaults to int.MaxValue.
Here is an article on how to change it for .NET application, but not Silverlight: Programattically setting the MaxItemsInObjectGraph property in client
A snippet of the code:
protected ISecurityAdministrationService GetSecAdminClient()
{
ChannelFactory<ISecurityAdministrationService> factory = new ChannelFactory<ISecurityAdministrationService>(wsSecAdminBinding, SecAdminEndpointAddress);
foreach (OperationDescription op in factory.Endpoint.Contract.Operations)
{
DataContractSerializerOperationBehavior dataContractBehavior =op.Behaviors.Find<DataContractSerializerOperationBehavior>() as DataContractSerializerOperationBehavior;
if (dataContractBehavior != null)
{
dataContractBehavior.MaxItemsInObjectGraph = 2147483647;
}
}
ISecurityAdministrationService client = factory.CreateChannel();
return client;
}
The following is a function that I've used inside a client object that inherits from
System.ServiceModel.ClientBase(Of IServiceName)
The purpose of the method is to programatically set the MaxItemsInObjectGraph value for each operation. This allows me to have much more complex structures.
Private Sub IncreaseObjectCount()
For Each op As System.ServiceModel.Description.OperationDescription In Me.Endpoint.Contract.Operations
For Each dscob As System.ServiceModel.Description.DataContractSerializerOperationBehavior In op.Behaviors.FindAll(Of System.ServiceModel.Description.DataContractSerializerOperationBehavior)()
dcsob.MaxItemsInObjectGraph = Integer.MaxValue
Next dcsob
Next op
End Sub
I usually call it in the constructors of the object.
Change the maxItemsInObjectGraph in your WCF service for each endpoint, changing it in Silverlight means the client will be able to support the behavior, but the service must support it aswell.
After changing it in your service, regenerate the proxy/update web service, and you will get a new ServiceReference.config, that will include the new maxItemsInObjectGraph value