In short: Trying to write a wcf service for a winform-app that invokes a stored procedure on a webserver.
So far no problem - my service exposes the method "execStp(string nameOfStoredProcedure, stpParamList parameterList)"
[OperationContract]
int execStp(string stpName, srsWcfLib.Utilities.stpParamList paramList);
where stpParamList is another class holding of a third class stpParams (which basically holds a name/value pair of an SqlParameter
To add parameters to the list, I wrote a method in the second class
public void addParameter(string ParamName, object ParamValue)
{
this._Parameters.Add(new stpParam(ParamName, ParamValue));
}
List<stpParam> _Parameters = new List<stpParam>();
[DataMember]
public List<stpParam> Parameters
{
get { return _Parameters; }
set { _Parameters = value; }
}
When instantiating the List-class in the win-app
stpParamList stpParams = new stpParamList();
I can access stpParams.Parameters, but NOT stpParams.addParameter (name, value);
What am I missing (obviously)...?
Thank you,
Reinhard
WCF only brings over the properties to the client, not the functions. I forget the term they use, but it is basically just a property/field dump that gets sent through.
To solve this, reference the same Entity library in the client, and under "configure WCF Service" check "reuse reference" for that library.
Erich has nailed the problem on the head: WCF is a MESSAGE based transport system, e.g. you will transfer a serialized message of your object. The server-side object with any additional functions will be serialized into XML or a binary format, sent across the wire to the client.
The client however only has the XSD (XML Schema) file exposed by your service at its disposal - it will create a client-side class that has the same "data signature" on the wire - i.e. the same fields, by the same name and type - but it cannot magically recreate any functions and/or methods your server-side code has. All it can do is deserialize the message (text or binary) back into a data-only representation of the class.
This is not a bug or a problem of a binding - this is a fundamental design choice for WCF - the only connection between the client and the server is the serialized message - anything that can be represented in XML schema. It will happily serialize and deserialize data - but it cannot move code / behavior across. You're NOT passing the actual objects by reference like with a normal function call - the WCF runtime serializes your parameters into XML and sends it across.
Now if you do control both ends of the wire, e.g. both the client and the server, there is a way around this, which violates the SOA principles a bit (but it can be useful). If you put your service contracts and data contracts into a separate assembly Contracts.dll (a class library), and then reference it from both the server side and the client side, you can indeed actually share the common .NET type srsWcfLib.Utilities.stpParamList with all its functionality. In those cases, however, you'll need to do a bit more work on the client side and instantiate your client proxy manually in code (rather than have Visual Studio or svcutil create the client proxy for you), since you need to reference that shared contracts assembly and use its types directly (rather than creating client-side classes).
Marc
Related
Suppose you have a method called "CalcArea()" in a WCF Contract. Once I get an instance of an object in a client I would like to let's say call myObject.CalcArea() and have it transmit that invoke request to the server object (that was used when I serialized my client object) and return the client the result. Is there a way to do this without creating a separate WCF service to "CalcArea()"? So something like this:
MyObject obj = Channel.GiveMeObject(name)
obj.ExecMethodOnServer()
I'm not quite sure I understand, so let me trace this out:
You have a WCF service endpoint exposing a method CalcArea.
You also have an instance of an object MyObject representing some geometric abstract, which also has a CalcArea() method.
You want to calculate the area of this object using an instance method call, and have the data available on the client, but you want the heavy lifting of the area calculation (perhaps surface area calculation of a 3D model containing thousands of triangles) to be performed on the server.
Sure, you can do this. You need the CalcArea WCF OperationContract method to accept an object of the local type. As MyObject is apparently already a DataContract of this WCF service (evidenced by getting one from the GiveMeObject method), this is fairly trivial as long as MyObject instances have access to a WCF proxy:
public class MyObject()
{
private MyWcfProxy WcfChannel;
public MyObject (MyWcfProxy wcfChannel) {WcfChannel = wcfChannel;}
public decimal CalcArea() { /*Area calculation logic*/ }
public decimal CalcAreaOnServer() { return WcfChannel.CalcArea(this); }
}
You can also pass the connected WCF proxy object to CalcAreaOnServer on a call-by-call basis, allowing you to use a different instance of the proxy than the one from which you received this object instance (but requiring consuming code to be able to provide that proxy, possibly exposing implementation details best kept secret).
I am very new to WCF programmng and having some difficulties.
I have client application which collects student data and then that student data will be stored to the DB through a web services.
So I have student class in client side and when new student comes I create the student object and assigned collected values.
What is the most suitable way to pass this data to the services??
Options that I can follow is;
Option 1
public void InsertStudent(string id, string name, string address)
{
}
I feel this is not good. Because if I have to pass so may data the parameter list will get lengthy.
Option 2
define a data contract on service
public void InsertStudent(WcfStudent obj)
{
}
I feel this is good. But I have some issues with how to send the parameter(WcfStudent).
Do I need to create this WcfStudent object on my client side and pass the object.
WcfStudent obj=new WcfStudent()
obj.name=myStudent.name;
obj.id=myStudent.id;
client.InsertStudent(obj);
is this the correct way to to do this??
Please advice me.
Of course like you said second option is better. In WCF Project you have to create DataContract WcfStudent. In project which have service reference (this is what you call client) you have to just execute method (OperationContract) InsertStudent. How you create this object in theory it doesn't matter but I think it's a good practice to have in your 'client' also an object named for example StudentBO and create mapping between WcfStudent and StudentBO or use some tool for example Automapper.
If you're totally new in WCF please read about DataContract, DataMember.
Good luck!
UPDATE
Speaking about your comment would be better something like:
public WcfStudent(MyStudent myStudent)
{
this.Id= myStudent.Id;
this.Name= myStudent.Name;
}
Your WCF Service will contain the definition of your WcfStudent class, and it will define that class as a Data Contract (if you are not using separate DTOs).
Referencing the WCF Service in your client project, either via a WSDL file or via the Visual Studio interface, gives your client application visibility or knowledge on the public classes, interfaces, and whatnot of the Service. This should include the WcfStudent class. You can then proceed with utilizing this in your code through something like:
MyWcfService.WcfStudent student = new MyWcfService.WcfStudent();
//assign values, etc
Manipulate that student object inside your application as you see fit, and then call your web service when you are ready to persist.
client.InsertStudent (student);
You would typically use data contracts instead of individual parameters, because then you can easily version them; and extend them easily with additional optional parameters and still be backwards compatible.
Personally I like to follow the request/response pattern consistently:
public InsertStudentResponse InsertStudent(InsertStudentRequest request)
{
}
If you don't expect a result, the response type could be void of course.
The request object contains only what you need in order to complete te request. Nothing more, nothing less. For example, if you want to change the address of a student, don't call ModifyStudent while passing all student properties, but call MoveStudent and pass only the new address information; so that you have clearly defined business operations.
Concerning the naming of your operations, InsertStudent is a CRUD-like naming, while I prefer to name them in business terminology, like RegisterNewStudent or something.
Also don't forget about fault contracts: don't throw exceptions, throw faults.
I am binding DataGrid.ItemsSource property to the List<PersonDetails> object. I am getting datas through Silverlight-enabled WCF Service. So the PersonDetails class is implemented in Web Project. Each DataGrid's header text is changing as i want if the class is located in Silverlight project. But then I can not use this class in the web service. The only solution is to add same class in to the both of the projects. But, is there any other way?
The class looks like that:
[DataContract]
public class PersonGeneralDetails
{
// Properties
[DataMember]
[DisplayAttribute(Name = "Sira")]
public int RowNumber { get; set; }
[DataMember]
[DisplayAttribute(Name = "Seriyasi")]
public string SerialNumber { get; set; }
}
It seems attributes aren't generated in web project. I know that I can change header text using DataGrid events. But i want to make it work using attributes.
The problem is the WCF DataContract is an inter-operable mechanism that can be used across languages and platforms.
If you take a look to serialized data generated by the DataContractSerializer (or its code in System.Runtime.Serialization.dll, specifically InternalWriteObjectXyz() methods) you'll see that it merely serializes values into a simple XML message. Nothing related to .NET Framework will be there so all kind of attributes, both custom and compiler generated, will be stripped out and won't even received by the client.
It works creating a copy of your data and sending them from server to client, clients will then create a new class with the same signature. Note: a NEW CLASS with the same signature, NOT JUST A NEW OBJECT of the original class.
Of course there are some workaround for this. You may write your own serializer (see this post on SO for an example) or your own ISerializationSurrogate.
If you can deploy/share your assemblies to your clients you have a nice workaround: just deploy them and DataContractSerializer will build the right object on your clients (exactly the same one you had on the server, with all its attributes). Just remember that:
If custom attributes comes from run-time values (for example because of localization) then they'll be resolved on the client, not on the server (because attributes will be created on the client, their values won't be included in the XML message).
In the client application you need to add a reference to the assembly that contains your types.
When you add your service reference you have to instruct VS to use them (or it'll create proxies), in the Service Reference Settings dialog select Reuse types in referenced assemblies (you can limit this to only assemblies you want to share).
I'm writing some software that modifies a Windows Server's configuration (things like MS-DNS, IIS, parts of the filesystem). My design has a server process that builds an in-memory object graph of the server configuration state and a client which requests this object graph. The server would then serialize the graph, send it to the client (presumably using WCF), the server then makes changes to this graph and sends it back to the server. The server receives the graph and proceeds to make modifications to the server.
However I've learned that object-graph serialisation in WCF isn't as simple as I first thought. My objects have a hierarchy and many have parametrised-constructors and immutable properties/fields. There are also numerous collections, arrays, and dictionaries.
My understanding of WCF serialisation is that it requires use of either the XmlSerializer or DataContractSerializer, but DCS places restrictions on the design of my object-graph (immutable data seems right-out, it also requires parameter-less constructors). I understand XmlSerializer lets me use my own classes provided they implement ISerializable and have the de-serializer constructor. That is fine by me.
I spoke to a friend of mine about this, and he advocates going for a Data Transport Object-only route, where I'd have to maintain a separate DataContract object-graph for the transport of data and re-implement my server objects on the client.
Another friend of mine said that because my service only has two operations ("GetServerConfiguration" and "PutServerConfiguration") it might be worthwhile just skipping WCF entirely and implementing my own server that uses Sockets.
So my questions are:
Has anyone faced a similar problem before and if so, are there better approaches? Is it wise to send an entire object graph to the client for processing? Should I instead break it down so that the client requests a part of the object graph as it needs it and sends only bits that have changed (thus reducing concurrency-related risks?)?
If sending the object-graph down is the right way, is WCF the right tool?
And if WCF is right, what's the best way to get WCF to serialise my object graph?
Object graphs can be used with DataContract serialization.
Note: Make sure you're preserving object references, so that you don't end up with multiple copies of the same object in the graph when they should all be the same reference, the default behavior does not preserve identity like this.
This can be done by specifying the preserveObjectReferences parameter when constructing a DataContractSerializer or by specifying true for the IsReference property on DataContractAttribute (this last attribute requires .NET 3.5SP1).
However, when sending object graphs over WCF, you have the risk of running afoul of WCF quotas (and there are many) if you don't take care to ensure the graph is kept to a reasonable size.
For the net.tcp transport, for example, important ones to set are maxReceivedMessageSize, maxStringContentLength, and maxArrayLength. Not to mention a hidden quota of 65335 distinct objects allowed in a graph (maxObjectsInGraph), that can only be overridden with difficulty.
You can also use classes that only expose read accessors with the DataContractSerializer, and have no parameterless constructors:
using System;
using System.IO;
using System.Runtime.Serialization;
class DataContractTest
{
static void Main(string[] args)
{
var serializer = new DataContractSerializer(typeof(NoParameterLessConstructor));
var obj1 = new NoParameterLessConstructor("Name", 1);
var ms = new MemoryStream();
serializer.WriteObject(ms, obj1);
ms.Seek(0, SeekOrigin.Begin);
var obj2 = (NoParameterLessConstructor)serializer.ReadObject(ms);
Console.WriteLine("obj2.Name: {0}", obj2.Name);
Console.WriteLine("obj2.Version: {0}", obj2.Version);
}
[DataContract]
class NoParameterLessConstructor
{
public NoParameterLessConstructor(string name, int version)
{
Name = name;
Version = version;
}
[DataMember]
public string Name { get; private set; }
[DataMember]
public int Version { get; private set; }
}
}
This works because DataContractSerializer can instantiate types without calling the constructor.
You got yourself mixed up with the serializers:
the XmlSerializer requires a parameter-less constructor, since when deserializing, the .NET runtime will instantiate a new object of that type and then set its properties
the DataContractSerializer has no such requirement
Check out the blog post by Dan Rigsby which explains serializers in all their glory and compares the two.
Now for your design - my main question is: does it make sense to have a single function that return all the settings, the client manipulates those and then another function which receives back all the information?
Couldn't you break those things up into smaller chunks, smaller method calls? E.g. have separate service methods to set each individual item of your configuration? That way, you could
reduce the amount of data being sent across the wire - the object graph to be serialized and deserialized would be much simpler
make your configuration service more granular - e.g. if someone needs to set a single little property, that client doesn't need to read the whole big server config, set one single property, and send back the huge big chunk - just call the appropriate method to set that one property
Background
I have made a Web Service in Visual Studio, and I'm trying to consume it using the automatically generated proxy class. The Web Service returns a class that I have implemented, containing a List.
Question
The proxy class has automatically generated methods to send the SOAP to the web service. It uses the Invoke() method to execute the call, and then casts the result as a DataSet. How can I get this object back into the class I know it is?
I know that I can hand-edit the auto-generated file, but that's not very maintainable, so I don't want to go down that route (any time the Web Service is rebuilt, the changes would have to be made again).
Is there a way to tell the generated class to be more specific, and actually use the correct data type? Or do I have to write a clunky set of deserialisers to get my data back into the correct shape?
Example
One method in my Web Service class:
[WebMethod]
public UpdateList RetrieveUpdates(long sessionID, string configurationVersion, string coreVersion, string researcherDBVersion)
{ ... }
Adding the class as a Web Reference generates the following proxy method:
public DataSet RetrieveUpdates(long sessionID, string configurationVersion, string coreVersion, string researcherDBVersion) {
object[] results = this.Invoke("RetrieveUpdates", new object[] {
sessionID,
configurationVersion,
coreVersion,
researcherDBVersion});
return ((DataSet)(results[0]));
}
The DataSet I receive from this method is always empty (because you can't cast from my class to a DataSet).
Thanks in advance
Since Web References generate partial classes, you should be able to add to your project a partial class extension to the proxy class that reimplements just the method in question (just copy and paste it) but changes the return type (and the name, of course). If the method signature changes, you'll have to update your extension, but at least if that doesn't happen and you regenerate the proxy you won't have to reapply any changes (and you can still use any other generated classes/methods as is).
I've used this approach before to "fix" proxy classes (for instance, to add SOAP headers that aren't defined in the WSDL), and while not ideal, it does work.
Unless your client code knows about your custom class (e.g. has a reference to the assembly) you will not be able to retrieve an object of that type from the service.
It sounds like what you are looking to do is share types across a service layer. In order to do that you will either have to give your client app a copy of the assembly that has the
UpdateList type or you will need to look at something like WCF.