I have just started WCF. I had a question with respect to serialization. I know WCF uses DataContract Serialization by default behind the scenes. I have looked at some code online by Aaron Skonnard in his article http://msdn.microsoft.com/en-us/magazine/cc163569.aspx. The code below is from his article. In this he has written code to serialize a Person object. My question is doesn't WCF do this behind the scenes. Why do we have to write the serialization logic? and then de-serialize it back again. As a WCF starter I am confused as to when to write this code and when not to. I think this would just work fine if there were no serialization logic written. Help appreciated!
static void WriterPersonSerializable()
{
Person p = new Person("Bob", 34, "secret");
p.spouse = new Person("Jane", 33, "secret");
using (FileStream fs = new FileStream("person.xml", FileMode.Create))
using (XmlDictionaryWriter writer =
XmlDictionaryWriter.CreateTextWriter(fs))
{
DataContractSerializer serializer =
new DataContractSerializer(typeof(Person));
serializer.WriteObject(writer, p);
}
You don't have to do something like that. Ofcourse, you can setup your own serialization. But by default it is ok if you just mark your classes by [DataContract] attribute and properties of classes by [DataMember] attribute. After that all you need to do is to inform your service about what classes it should be able to serialize/deserialize in [ServiceKnownType] attribute. That's it, everything will work out of the box.
Related
I'm using a really simple WCF project which is being hosted by a WPF application. It basically operates as a REST server for dishing up data on my PC. No IIS, and it runs as SingleInstance. I want to see what IP's are accessing MyService, and what WebMethod they're attempting to invoke.
Ok so I can have an event as part of my Service, declared in the service class itself. Here's some code that gets it going, it all works exactly as expected (no flames about m_ please ;)):
MyService ds = new MyService(); // It's not really called this :)
ds.Request += new EventHandler(ds_Request); // I want to avoid this
ds.SomePropertySetFromMyRehostingClient = "something"; // SingleInstance now required
m_Service = new ServiceHost(ds, new Uri(GetServerHostUri()));
m_Service.Description.Behaviors.Find<ServiceBehaviorAttribute>().InstanceContextMode = InstanceContextMode.Single;
m_Service.BeginOpen(new TimeSpan(0, 0, 5), new AsyncCallback(EndStartService), null);
Then in each service method, I can raise this event so my app knows that someone has tried to use it. Brilliant, but let's face it, this is awful.
I have to write something along the lines of:
var who = OperationContext.Current.IncomingMessageProperties.Via;
var what = OperationContext.Current.IncomingMessageProperties["UriTemplateMatchResults"];
for each service call.
Is there a more generic catch-all-event that can detect a call to my service? There's probably one fired by one of the many Behaviour/ChannelDispatcher types which I admittedly don't fully understand.
Thanks for your help, Tom
Using IParameterInspector you can hook to any method calls and inspect the method and/or parameters.
There is no other way to get the extra information (IP address, etc) of the incoming message other than the one you have used. This is just a bad design by Microsoft IMHO (see my rant here).
You can find an example here.
I'm using the WCF REST Template where services are implemented with just a class and registered in the Global.ascx (much like MVC controllers are).
RouteTable.Routes.Add(new ServiceRoute("Games/Games", new WebServiceHostFactory(), typeof(Games.Games)));
Games.Games has a ctor accepting a Dal.Games.IGames and I have a NinjectModule with the Bindings ready but I cant for the life of me figure out where to pass the kernel to to have it control the creation of the service classes.
My services dont have a markup (svc) file so I'm guessing that it will have something do with replacing the WebServiceHostFactory with one from Ninject. I was able to find one in the Ninject Web extension but just dropping that in didnt change anything not to mention I coulnt find anywhere to setup the kenel in that class.
Any solutions, hints or tips are greatly appreciated.
Let me preface this by saying, someone who actually knows inner workings of Ninject could probably provide a much cleaner solution. I've been wrestling with the same issue as you mentioned though.
Mostly through trial & error I determined that if you make the following code changes in the Ninject.Extensions.Wcf library, Ninject will work its magic on your service classes.
In NinjectServiceHostFactory.cs, i changed the base class and the type passed to .Get<T>
public class NinjectServiceHostFactory : WebServiceHostFactory //<-- Changed base class
{
protected override ServiceHost CreateServiceHost( Type serviceType, Uri[] baseAddresses )
{
var serviceTypeParameter = new ConstructorArgument( "serviceType", serviceType );
var baseAddressesParameter = new ConstructorArgument( "baseAddresses", baseAddresses );
return KernelContainer.Kernel.Get<NinjectServiceHost>( serviceTypeParameter, baseAddressesParameter );
}
}
In the NinjectServiceHost.cs i changed the base class to WebServiceHost.
Also, I added this reference to both:
using System.ServiceModel.Web;
I'm sure this solution breaks this extension for other WCF service types so hopefully a Ninject guru will come along and provide a real solution.
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 have a a datacontract which has important attributes on.
For example, in the following code:
[DataMember]
[Description("My Description")]
public string Name { get; set; }
I want the Description attribute to be auto generated on the client proxy.
Is there any solution, or workarounds besides massive duplication?
Many thanks!
You don't, not really. Remember that you're not passing instances of objects, but rather textual messages.
If it's really important then you can abandon the generated proxy classes and share implementation of the data objects and contracts instead, however this is a bunch more work and of course you're running the risk of the client and server becoming out of sync.
If you want to try that then put your contracts and operation interface into a separate assembly, with public modifiers, then try the following
Binding binding = new BasicHttpBinding(); // or which one you
EndpointAddress endpoint =
new EndpointAddress("endpointUrl");
ChannelFactory<IServiceInterface> channelFactory =
new ChannelFactory<IServiceInterface>(binding, endpoint);
IServiceInterface client = channelFactory.CreateChannel();
MyDataType result = client.Operation(myOtherDataType);
((IClientChannel)client).Close();
You would probably have to dig pretty deep into the creation of the service description (and the resulting WSDL file) in order to pass those attributes in such a form that the client could detect and recreate them. Next to impossible, really.
Plus: remember that WCF is designed to be interoperable - what should a Java or PHP client do with those attributes, really?
Service-oriented programming is quite a different beast from "regular" component- or object-based systems. All you do is basically pass around messages in text or binary format. That does limit certain things that you might be accustomed to when dealing with .NET based objects and components - it just works differently in SOA world.
Marc
I created some POX services using the REST Starter kit.
At first, it was a little complicated to create the unit tests for the service layer, but in the end, it was well designed and I succeed in mocking the context and set expectations.
But, I'm starting in with the client side now, and I'm having some problems figuring out how to mock things. It's not so straight forward.
So, I have this sample method that post via HTTP some data using a datacontract and XML as transport.
public class ServiceClient: IService
{
private const string uri_template = "http://{0}:{1}/SomeService.svc/";
private readonly HttpClient http = new HttpClient();
public ServiceClient(string host, int port)
{
http.BaseAddress = new Uri(string.Format(uri_template , host, port));
}
public void Create(MyDataContract my_data_contract)
{
var content = HttpContentExtensions
.CreateDataContract(
my_data_contract,
Encoding.Default,
"text/xml",
null);
var response = http.Post("Create", content);
if (response.StatusCode != HttpStatusCode.Created) {
throw new Exception("something is not right");
}
}
}
This method is working and indeed posting the data and serializing correctly.
What I want to do, is to unit test it.
My questions are:
How do I make stubs/mocks for HttpContextExtensions knowing that it does not implement any interface?
And for HttpClient? this is worst since it is full of extension methods defined elsewhere (Post and the like methods are extensions).
In 2. I guess I can stick to 'Send' and it's overloads, but then is the same problem as in 1
What I was thinking to do, is to extract interfaces for HttpContextExtensions and HttpClient, make a children for each one and implement the interfaces delegating to the parent, and then using the interfaces in my code.
But that's a lot of work IMHO.
I'm using RhinoMocks as my mocking framework so I can't mock/stub concrete classes, and I would like to stick to it, because I really think that mocking concrete classes is not very elegant.
So, is there a cleaner/faster/smarter way to unit test code like the above?
PD: This is for WCF Starter Kit preview 2
If you really want to mock that code, an approach that could work for this scenario is to receive an HttpClient instance in the ServiceClient class. The HttpClient class contains a processing pipeline that you can customize with custom handlers. For your tests, you can basically inject a handler in that httpclient instance to return a mocked response to the test before the real service is called in the last handler (Transport Stage). Take a look at this post to get an idea about how this can be implemented,
http://weblogs.asp.net/cibrax/archive/2009/03/18/negotiating-a-saml-token-for-rest-clients-with-httpclient.aspx
Regards,
Pablo.
I wrote most of the HttpClient code. Pablo's suggestion is what I did in the unit tests -- see FixedTransport.cs in the source zip.