Thanks for the quick answers all. But I am looking for an answer and not a workoaround (serialize as string) as I want to know how to use other types from the framework
I am fairly good at WCF but I think I am still at the beginners stage since I cannot serialize a System.Drawing.Color.
This is my Service Contract
using System.Drawing;
using System.ServiceModel;
namespace wcfServer
{
[ServiceContract]
public interface IColorService
{
[OperationContract]
Color DoWork();
}
}
And here is an implementation
public class ColorService : IColorService
{
public Color DoWork()
{
return Color.Yellow;
}
}
However, at the client WCF doesn't use a System.Drawing.Color but it generates it own color type (a struct) ?
The net result is that the color Yellow does not arrive at the client
I thought that this wasn't a problem since the .net Color type is marked with the serializable attribute
Kind Regards, Tom
Colors are usually a mess - there are so many of them. Just convert to color to a 32-bit ARGB structure (the Color class has a method that does this) and use that in your WCF interface. If you want to be extra careful, define your own struct with A, R, G and B (as bytes, WPF has them as doubles, but nobody really needs that), and decouple your service from any specific UI platform.
However, at the client WCF doesn't use a System.Drawing.Color but it generates it own color type (a struct)? [...] I thought that this wasn't a problem since the .net Color type is marked with the serializable attribute
I'm assuming you use basicHttp or wsHttp here. What I'm saying doesn't go for all bindings.
Communication between a WCF service and client has nothing to do with .NET. Keyword is interoperability. The client doesn't have to be written in .NET, it might very well be a PHP or Java or whatever kind of client.
WCF therefore uses SOAP to send and receive data, which all major programming languages implement. So to let a service and client exchange data, a format for that data has to be defined. You can't say "Hey, I'm gonna send a System.Drawing.Color", since that may very well not be a valid class or struct definition in the client's language.
So your service defines a WSDL, containing a schema definition, where the contents of the Color struct will be copied from System.Drawing.Color. It won't be linked to the .NET framework from the point it gets serialized and sent over the wire.
I was able to fix this problem by using "KnownTypeAttribute" on a data contract. So you can try "ServiceKnownTypeAttribute" on a service contract like this :
[ServiceContract]
[ServiceKnownType(typeof(System.Drawing.Color))]
public interface IColorService
{
[OperationContract]
Color DoWork();
}
This works fine assuming that the client code is also using .NET.
The strategy with "KnownType" worked well in my project http://www.nquotes.net/ and let me avoid additional serialization hassle. They should have included Color as one of the base types (as they do with Guid, for example, which is "known" automatically - http://msdn.microsoft.com/en-us/library/ms731923.aspx ).
Related
In some enterprise-like project (.NET, WCF) i saw that all service contracts accept a single Request parameter and always return Response:
[DataContract]
public class CustomerRequest : RequestBase {
[DataMember]
public long Id { get; set; }
}
[DataContract]
public class CustomerResponse : ResponseBase {
[DataMember]
public CustomerInfo Customer { get; set; }
}
where RequestBase/ResponseBase contain common stuff like ErrorCode, Context, etc. Bodies of both service methods and proxies are wrapped in try/catch, so the only way to check for errors is looking at ResponseBase.ErrorCode (which is enumeration).
I want to know how this technique is called and why it's better compared to passing what's needed as method parameters and using standard WCF context passing/faults mechanisms?
The pattern you are talking about is based on Contract First development. It is, however not necessary that you use the Error block pattern in WCF, you can still throw faultexceptions back to the client, instead of using the Error Xml block. The Error block has been used for a very long time and therefore, a lot of people are accustom to its use. Also, other platform developers (java for example) are not as familiar with faultExceptions, even though it is an industry standard.
http://docs.oasis-open.org/wsrf/wsrf-ws_base_faults-1.2-spec-os.pdf
The Request / Response pattern is very valuable in SOA (Service Oriented Architecture), and I would recommend using it rather than creating methods that take in parameters and pass back a value or object. You will see the benefits when you start creating your messages. As stated previously, they evolved from Contract First Development, where one would create the messages first using XSDs and generate your classes based on the XSDs. This process was used in classic web services to ensure all of your datatypes would serialize properly in SOAP. With the advent of WCF, the datacontractserializer is more intelligent and knows how to serialize types that would previously not serialize properly(e.g., ArrayLists, List, and so on).
The benefits of Request-Response Pattern are:
You can inherit all of your request and responses from base objects where you can maintain consistency for common properties (error block for example).
Web Services should by nature require as little documentation as possible. This pattern allows just that. Take for instance a method like public BusScheduleResponse GetBusScheduleByDateRange(BusDateRangeRequest request); The client will know by default what to pass in and what they are getting back, as well, when they build the request, they can see what is required and what is optional. Say this request has properties like Carriers [Flag Enum] (Required), StartDate(Required), EndDate(Required), PriceRange (optional), MinSeatsAvailable(Option), etc... you get the point.
When the user received the response, it can contain a lot more data than just the usual return object. Error block, Tracking information, whatever, use your imagination.
In the BusScheduleResponse Example, This could return Multiple Arrays of bus schedule information for multiple Carriers.
Hope this helps.
One word of caution. Don't get confused and think I am talking about generating your own [MessageContract]s. Your Requests and Responses are DataContracts. I just want to make sure I am not confusing you. No one should create their own MessageContracts in WCF, unless they have a really good reason to do so.
I have an application where client and server share types, and interoperability is not one of our concerns. I am planning to have a single repository for all web enabled objects, and i was thinking of a generic interface for my exposed service.
something like T GetObject(int id)
but wcf doesnt like it since its trying to expose its schema (which i dont really care about)
is it possible to do such a thing with WCF ?, i can use any type of binding doesnt have to be httpbinding or wsbinding...
No, you can't. Whether or not you want or need interoperability, the most basic foundation of WCF is message exchange.
The client send the server a message and gets back a response. That message is all that passes between client and server, and needs to be serializable into a XML or binary format. That's why any data being passed around must be atomic (like int, string) or a DataContract - a description for the WCF service stack about how to serialize and deserialize such objects.
You cannot pass any interfaces, or other "trickery" - all that goes between client and server must be expressable in XML schema, basically.
So I'm afraid what you're trying to achieve is quite contrary to what WCF offers. The world and paradigms of SOA (Service-Oriented Apps) are quite different and not always 100% in sync with the idea and mechanisms of OOP.
Marc
I suppose this is possible, though I'm not sure you'd want this. I'd take the following approach (untested, not sure if it works). First create the following project structure in your solution:
ServiceInterfaces
ServiceImplementations (references ServiceInterfaces and ModelClasses)
ModelClasses
Host (references ServiceInterfaces and ServiceImplementations)
Client (references ServiceInterfaces and ModelClasses)
In ServiceInterfaces you have an interface like this (I skipped the namespaces, etc to make the example shorter):
[ServiceContract]
public interface IMyService<T>
{
T GetObject(int id);
}
In ServiceImplementations you have a class that implements IMyService<T>:
public class MyService<T> : IMyService<T>
{
T GetObject(int id)
{
// Create something of type T and return it. Rather difficult
// since you only know the type at runtime.
}
}
In Host you have the correct configuration for your service in an App.config (or Web.config) file and the following code to host your service (given that it is a stand-alone app):
ServiceHost host = new ServiceHost(typeof(MessageManager.MessageManagerService))
host.Open();
And finally in Client you use a ChannelFactory<TChannel> class to define a proxy:
Binding binding = new BasicHttpBinding(); // For the example, could be another binding.
EndpointAddress address = new EndpointAddress("http://localhost:8000/......");
IMyService<string> myService =
ChannelFactory<IMyService<string>>.CreateChannel(binding, address);
string myObject = myService.GetObject(42);
Again, I'm not sure if this works. The trick is to share your service interfaces (in ServiceInterfaces) and domain model objects (in ModelClasses) between the host and the client. In my example I use a string to return from the service method but it could be any data contract type from the ModelClasses project.
You CAN DO that if you use ServiceKnownTypesDiscovery.
For example:
[ServiceKnownType("GetKnownTypes", typeof(ServiceKnownTypesDiscovery))]
public interface ISomeService
{
[OperationContract]
object Request(IRequestBase parameters);
}
where GetKnownTypes could be declared like so:
public static class ServiceKnownTypesDiscovery
{
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
var types = new List<Type>();
foreach (var asmFile in Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory, "*.dll"))
{
Assembly asm = Assembly.LoadFrom(asmFile);
types.AddRange(asm.GetTypes().Where(p=> Attribute.IsDefined(p,typeof(DataContractAttribute))));
}
return types;
}
}
In this case everything declared with [DataContract] (as long as they are discoverable on the server AND the client side) can be serialized.
I hope this helped!
Following the previous example, you could declare a DataContract with an object as DataMember. Then you could add an extension method to get and set a generic type on the object data member. You could also make this internal, this way you would be obliged to use the extension methods to get and set the value.
Of course, it only works if you generate the client using svcutil (or Visual Studio) and you reference the assembly containing the data contract and the class with the extensions methods.
Hope this helps...
I created a WCF service that exposed a method that has one paramater:
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
The service has two endpoints defined (wsHttpBinding and basicHttpBinding) so that it would be compatable with older clients.
The service runs just fine in a .NET 3.0 and .NET 3.5 client app. However, when I create a .NET 2.0 client, the GetData method requires 2 parameters: an integer (expected) and a bool parameter called valueSpecified (unexpected). I never defined the second parameter. Why is this happening and how can I get rid of the second parameter?
Since value types can't be null (in latter versions of .net framework there is no Nullable<T>) VS besides to generate additional parameter to give you ability to not specify value type, you can call your service method like this.
service.GetData(val,true);
see this post, where John Saunders suggest to add [DataMember(Required = true)] attribute in the property.
The exact same question has been posted here.
Another way to avoid the extra boolean parameter to be generated on the client proxy when using .NET 2.0 is to switch to RPC-style enconding in the service contract (the default for both WCF and ASMX is Document Style).
This way the XmlSerializer on the client will make sure that the parameter always appears in the SOAP requests since it's part of the SOAP 1.1 specification, which is enforced when using the RPC-Style encoding.
In WCF you can specify the encoding style using the DataContractFormat attribute, either at the service or at the operation level.
[ServiceContract]
public interface IService
{
[OperationContract]
[DataContractFormat(Style = OperationFormatStyle.Rpc)]
string GetData(int value);
}
More information on the differences between RPC Style and Document Style encoding in SOAP can be found here.
In any case please consider carefully the implications of changing the contract of your services, since it can potentially break compatibility with any existing clients.
You could manually remove the valueSpecified property from GetData operation within your proxy class.
I have an service Interface:
[ServiceContract]
[ServiceKnownType(typeof(Models.ArticleImage))]
public interface IPhotoManagementService
{
[OperationContract]
bool Login(string username, string password);
[OperationContract]
bool IsLoggedIn();
[OperationContract]
void UpdateImage(string articleID, string selectedImage);
}
As you can see I specify a typeof(Models.ArticleImage) on my ServiceContract.
So building the WSDL of this service should cause ArticleImage to pop up in the WSDL. Unfortunarly this doesn't happen at all. Why is that?
ArticleImage has DataContract on it. And when I return an ArticleImage in my interface, then the WSDL does pick up ArticleImage.
Edit: it doesn't even pop up in the service reference in the consuming project!
This is the result of a lot of testing:
The model I'm trying to add is a LINQ to SQL model.
When I add a normal model with ServiceKnownType it works.
When I use my LINQ to SQL entities in my Interface it works.
When I add my LINQ to SQL entity through ServiceKnownType it doesn't pop up.
Only types used as input/output parameters of service contract operations are published in the WSDL.
Why would it need to? Where does your service expose something that could possibly be an ArticleImage?
Re your comment; when using [ServiceKnownType], the extra trype is still exposed in the "mex" (consumed via "svcutil") - but not by the WSDL. Are you using a WCF client? It should appear (I've just checked... it did). In general, though, returning vague data from a web-service isn't a great idea... sub-types, sure! Dictionary<string,ArticleImage> or even Dictionary<string,SomeBaseType> (with [KnownType] etc), fine! But object, HashTable, etc - aren't a good idea (IMO).
You might also just return a list of your type (List<ArticleImage>) which will work in all scenarios (and be easy for WSDL etc); and let the client make the dictionary at their end.
With regards to LINQ-to-SQL; objects for "mex" need to be decorated with [DataContract] / [DataMember]. You can do this in the designed by toggling the "serialization" property for the dbml. With this set (Serialization Mode = Unidirectional), it should work. To be honest, though, I think you be better-off just adding a dummy method that makes the type explicit on the API.
Adding a service reference to a web service (this is all WCF) in Visual Studio produces some generated code including a client-side restatement of the interface being exposed.
I understand why this interface is generated: you might be consuming a 3rd party service and not have access to the actual interface.
But I do, and the two are not assignment compatible even though the transparent proxy does indeed exactly implement the interface to which I want to cast.
I can use reflection, but that's ugly. Is there some way to defeat this faux type safety and inject metadata to so I can use an interface with a class?
My specific problem departs from the norm in complicated ways that have to do with a single client that uses some derivatives of a base class directly and uses others remotely via service references. The base class for each server needs to keep references to subscribing clients in a collection for enumeration to notify events, and the problem was type varied due to the use of proxies.
None of these answers solves my specific problem, yet every single answer was instructive and helpful. I found my own solution (use a dual binding) but I would never have figured it out if you hadn't radically improved my understanding of the whole business.
Three excellent answers. How to choose just one? I choose the first, because it directly solves the problem I first thought I had.
If you already have the contract dll at the client, you don't even need a service reference (unless you are using it to write the setup code for you) - you can simply subclass ClientBase and expose the Channel, and use that directly - something like (no IDE handy...):
public class WcfClient<T> : ClientBase<T> where T : class
{
public new T Channel {get {return base.Channel;}}
}
Then you can just do things like:
using(var client = new WcfClient<IFoo>())
{
client.Channel.Bar(); // defined by IFoo
}
You still need the configuration settings in the config to determine the address, binding, etc - but less messy than proxy generation. Also, you might choose to re-implement IDipsoable to deal with the fact that WCF proxies can throw in Dispose() (which is bad):
public class WcfClient<T> : ClientBase<T>, IDisposable where T : class
{
public new T Channel {get {return base.Channel;}}
void IDisposable.Dispose() {
try {
switch(State) {
case CommunicationState.Open: Close(); break;
// etc
}
} catch {} // swallow it down (perhaps log it first)
}
}
When you add the service reference, go to "Advanced" and make sure "Reuse types in referenced assemblies" is selected and that the assembly containing your interface definition is selected. You can also do this with an existing service reference by right clicking on it and going to "Configure".
In order to return an interface from a service you need to use the KnownType attribute:
http://weblogs.asp.net/avnerk/archive/2006/07/31/WCF-Serialization-part-1_3A00_-Interfaces_2C00_-Base-classes-and-the-NetDataContractFormatSerializer.aspx
If you want to return a custom type from the service:
http://msdn.microsoft.com/en-us/library/bb628653.aspx
Does any of that help?