If I have a Server/Client application that both reference the same DLL which contains interfaces used for a REST server and for the WebChannelFactory to reference the web server, what would happen to legacy clients if the Servers interface gets and update? For example say version one of an application gets pushed out with the following interface:
[ServiceContract]
public interface ISampleInterface
{
[OperationContract]
[WebInvoke(UriTemplate = "/PutDevice", ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)]
void PutDevice(Device device);
}
[DataContract(Namespace = "")]
public class Device
{
[DataMember]
public Guid id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Description { get; set; }
} // End of Device
This is what the REST service uses as a contract and the agent would have code similar to the following:
WebChannelFactory<ISampleInterface> client = new WebChannelFactory<IGridVisionService>(new Uri(targetHost));
ISampleInterface sampleInterface = client.CreateChannel();
sampleInterface.PutDevice(new Device() { id = Guid.Empty(), Name = "Test Device", Description = "Test Device Description" });
So the client application is already deployed to hundreds of computers, but we realize for the version we also want the client to send it's domain so we modify the device data contract to be the following:
[DataContract(Namespace = "")]
public class Device
{
[DataMember]
public Guid id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Domain { get; set; }
[DataMember]
public string Description { get; set; }
} // End of Device
It's easy to update the server, but now there are hunders of agents that do not know about the Domain. What is the proper way to deal with this situation? My only thought was to not use a DataContract but an XElement that I could manually parse. Then add login to the Server for dealing with the case of a missing Domain, but this seems sloppy. Is there a better solution that I am overlooking?
I was able to test this myself. In the case that my Client Device linking to a dll that did not know about the Domain parameter, the method call still succeeded and the Domain parameter was simply null. This is the result I was hoping for!
Related
Hi I wrote WCF service that uses entity framework code first entities as data contracts for example I have Step entity:
[DataContract(IsReference=true)]
public class Step
{
[Key]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
public int Order { get; set; }
[DataMember]
public string ExpectedResult { get; set; }
[DataMember]
public bool ContinueOnFail { get; set; }
#region Navigation Properties
[DataMember]
public virtual Test Test { get; set; }
#endregion
}
I have in my service function that return IEnumrable
[ServiceContract]
public interface IStepsService
{
IEnumerable<Step> GetSteps();
}
the target platform of my service is Any CPU
now I need to change my target platform to x86
and here is the tricky part: no error is thrown but my service now always return an empty list!
someone knows why this is happening?
I read this question: How to change WCF project Platform Target to x86?
but it is not answering my question.
I will be glad if you help me because the project have to be x86!
thank you.
I am adding my implementataion
in the server the implementation looks like this:
try
{
steps=(from step in db.Steps
select step).ToArray();
}
catch (Exception e)
{
Tracer.Write(Tracer.TraceLevel.ERROR, "Error in getting steps from database", e);
return null;
}
return steps;
and in the client it just use this... by the way I don't know if it is important but the client is ASP.NET MVC 4 that adding the service reference to this service... and the db class is class that inherit from entity framework DbContext class...
Use a tcp packet capture software to capture the message from service to client. If no steps are in the message, then it's server-side issue. If there are steps returned, then it's client-side deserialization issue.
There are probably 500 questions like this on SO, and a million websites out there all offering tidbits of information - but I just can't see the wood for the trees. This seems like it should be embarrassingly simple to do, but I just can't make it work.
I have a WCF webservice that returns a serialized JSON object:
[OperationContract(Name = "PeopleData"), WebGet(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "people/{subset}", ResponseFormat = WebMessageFormat.Json)]
PeopleObject GetPeople(string subset);
This works - if I hit that URI from a browser, GetPeople is invoked and returns a JSON-serialized PeopleObject (actual data values redacted for privacy here):
{"HashValue":"XXXXX","People":[{"EmailAddress":"XXXXX","EmployeeID":99999,"Gender":"X","JobTitle":"XXXXX","Office":"","PreferredName":"XXXXX","Surname":"XXXXX","WorkExtensionNumber":"XXXXX","WorkPhoneNumber":"XXXXX","Department":"XXXXX","DeskNumber":"XXXXX","EmploymentClassification":"XXXXX","InternationalExtensionNumber":"XXXXX","IsFirstAider":false,"Languages":[{"LanguageID":9,"LanguageSkillID":9},{"LanguageID":9,"LanguageSkillID":9}],"QualificationInitials":"XXXXX","QualificationTitle":"XXXXX","Secretaries":null,"WorkMobilePhoneNumber":"XXXXX"}],"RecordCount":"1","SizeBytes":"12345"}
In this example the PeopleObject payload contains just one Person object in the collection, but could contain many (depending on the parameter supplied in /{subset}.
Here is the class hierarchy for PeopleObject - it's a top-level container holding some metadata about the payload, and a List<> of Person objects. Those objects in turn have a bunch of simple type attributes, plus two further nested List<> of Language and Secretary objects (which may or may not be populated):
[DataContract]
public class PeopleObject
{
[DataMember]
public string HashValue { get; set; }
[DataMember]
public List<Person> People { get; set; }
[DataMember]
public string RecordCount { get; set; }
[DataMember]
public string SizeBytes { get; set; }
}
[DataContract]
public class Person
{
[DataMember]
public string EmailAddress { get; set; }
// <-- snip - lots of fields like this, no point listing them all here
[DataMember]
public bool IsFirstAider { get; set; }
[DataMember]
public List<Language> Languages { get; set; }
[DataMember]
public List<Secretary> Secretaries { get; set; }
}
[DataContract]
public class Language
{
[DataMember]
public int LanguageID { get; set; }
[DataMember]
public int LanguageSkillID { get; set; }
}
[DataContract]
public class Secretary
{
[DataMember]
public int EmployeeID { get; set; }
[DataMember]
public char FirstSurnameLetter { get; set; }
}
So far, so good - WCF responds with a JSON structure that contains all the fields and their contents. Now to deserialize that structure in a client application (using the same class hierarchy definitions):
// I have a little helper-class to manage the WCF request and return a Stream
using (Stream response = wcfHelper.GetRequestResponseStream(MY_WCF_URI))
{
// This is debug code to prove the response arrives as expected - it does
//StreamReader sr = new StreamReader(response);
//Console.WriteLine("\nResponse:\n{0}", sr.ReadToEnd());
// Deserialise the response
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(PeopleObject));
PeopleObject p = (PeopleObject)dc.ReadObject(response);
// The object shows 1 record (in the example) but nothing in the List<>
Console.WriteLine("\nDeserialized records: '{0}' [{1}]", p.RecordCount, p.People.Count);
}
So this correctly deserializes the container object, giving me the record count, hash value, and payload size in bytes. The object does also have a List<> of Person objects, but it's null - the content from the JSON response hasn't successfully rehydrated the List<> by creating and adding a Person object.
What am I missing? My understanding was that this rehydration of the C# object hierarchy from the JSON structure should happen automatically, so either that's not the case (and I need to write some code to make it happen) or it is, but I've missed something obvious.
I haven't done what you are doing before, but judging by the documentation, I'd assume the following would work:
List<Type> types = new List<Type>();
types.Add(typeof(Person));
types.Add(typeof(Language));
types.Add(typeof(Secretary));
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(PeopleObject), types);
PeopleObject p = (PeopleObject)dc.ReadObject(response);
You basically need to tell the Serializer all the types it may encounter while serializing/deserializing your object.
I have a site that give me this xml response on my GET request:
<ServerUnits>
<State Name="ServerName" Date="2008-04-01" >
<Users>
<User login="someUser1" Password="123456">
<User login="someUser2" Password="qwerty">
</Users>
</ServerUnits>
I want use WCF Client for work with this service.
How to discraibe Message Contract of this response for WCF Clien
It is best to create client proxies for the WCF service. It will create the data contracts for you (as mentioned by #Aliostad) so you don't have to create them manually. To do this right click on your solution and select "Add Service Reference..." from the context-menu and enter the address to your WCF service.
I think that WCF is not useful is your case.
A more simple way would be to write objects that match this xml response and just deserialize xml stream onto objects instances.
What you have posted is not a SOAP message so MessageContract is not appropriate.
I imagine what you posted is the SOAP body content so you need to do something along the line of this:
[DataContract]
public class ServerUnits
{
[DataMember]
public ServerState State { get; set; }
[DataMember]
public List<User> Users { get; set; }
}
[DataContract]
public class ServerState
{
[DataMember]
public string Name { get; set; }
[DataMember]
public DateTime Date { get; set; }
}
[DataContract]
public class User
{
[DataMember]
public string login { get; set; }
[DataMember]
public string password { get; set; }
}
UPDATE
Your message is not SOAP. But you can still use the code above if you use webHttpBinding which sends and receives POX.
Consider a WCF service using WsHttpBinding for which only the domain users are allowed to call this service.
How can you find the Active Directory username of the caller?
Get the value of System.ServiceModel.ServiceSecurityContext.Current.WindowsIdentity.Name property.
It does not matter which binding you use as long as the security mode is different from None for the binding.
If the security mode is None then System.ServiceModel.ServiceSecurityContext.Current will be null.
You can get identity of the user by calling:
ServiceSecurityContext.Current.WindowsIdentity.Name
or
OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name
You will have to add some sort of User information to the message structure you are using to contact the service.
e.g.
public class UserInformation
{
public string User { get; set; }
public string Password { get; set; }
}
[DataContract]
public class Request
{
[DataMember]
public UserInformation User { get; set; }
[DataMember]
public MyRequest RequestBody { get; set; }
}
This way you can query active directory at your client side, populate the UserInformation object and send over the user details as part of the message structure.
I have created two WCF Services (Shipping & PDFGenerator). They both, along with my ClientApp, share an assembly named Kyle.Common.Contracts. Within this assembly, I have three classes:
namespace Kyle.Common.Contracts
{
[MessageContract]
public class PDFResponse
{
[MessageHeader]
public string fileName { get; set; }
[MessageBodyMember]
public System.IO.Stream fileStream { get; set; }
}
[MessageContract]
public class PDFRequest
{
[MessageHeader]
public Enums.PDFDocumentNameEnum docType { get; set; }
[MessageHeader]
public int? pk { get; set; }
[MessageHeader]
public string[] emailAddress { get; set; }
[MessageBodyMember]
public Kyle.Common.Contracts.TrackItResult[] trackItResults { get; set; }
}
[DataContract(Name = "TrackResult", Namespace = "http://kyle")]
public class TrackResult
{
[DataMember]
public int SeqNum { get; set; }
[DataMember]
public int ShipmentID { get; set; }
[DataMember]
public string StoreNum { get; set; }
}
}
My PDFGenerator ServiceContract looks like:
namespace Kyle.WCF.PDFDocs
{
[ServiceContract(Namespace="http://kyle")]
public interface IPDFDocsService
{
[OperationContract]
PDFResponse GeneratePDF(PDFRequest request);
[OperationContract]
void GeneratePDFAsync(Kyle.Common.Contracts.Enums.PDFDocumentNameEnum docType, int? pk, string[] emailAddress);
[OperationContract]
Kyle.Common.Contracts.TrackResult[] Test();
}
}
If I comment out the GeneratePDF stub, the proxy generated by VS2010 realizes that Test returns an array of Kyle.Common.Contracts.TrackResult. However, if I leave GeneratePDF there, the proxy refuses to use Kyle.Common.Contracts.TrackResult, and instead creates a new class, ClientApp.PDFDocServices.TrackResult, and uses that as the return type of Test.
Is there a way to force the proxy generator to use Kyle.Common.Contracts.TrackResult whenever I use a MessageContract? Perhaps there's a better method for using a Stream and File Name as return types?
I just don't want to have to create a Copy method to copy from ClientApp.PDFDocServices.TrackResult to Kyle.Common.Contracts.TrackResult, since they should be the exact same class.
After a lot of extra digging, I realize that it was actually the Enum that "broke" it. It has do with the way DataContractSerializer works vs. XmlSerializer. Long story short, the solution was to turn the Enum into a nullable.
[MessageContract]
public class PDFRequest
{
[MessageHeader]
public Enums.PDFDocumentNameEnum? docType { get; set; }
[MessageHeader]
public int? pk { get; set; }
[MessageHeader]
public string[] emailAddress { get; set; }
[MessageBodyMember]
public Kyle.Common.Contracts.TrackItResult[] trackItResults { get; set; }
}
I ran into the same problem (MessageContract+enums) and your post helped me. Indeed if you explicitly set the enum fields to nullable it works. The issue is that when enums are used, WCF uses the XML serializer which cannot tell null from empty string.
There is a detailed explanation of this behaviour here by one of the actual WCF team members.
In the case of document/literal when using bare messages, WCF falls back to XmlSerializer when handling enum types. ... XmlSerializer treats null as missing by default ... we encounter a schema without nillable="true" ... The detection logic for value types currently only handles primitive value types, it does not check for enums.
In other words WCF does not like enums... But hey, it works, you just need to be aware of it!
You can instruct Visual Studio to re-use classes from referenced assemblies. So if your test project has an assembly reference to the Kyle.Common.Contracts assembly, it should re-use those types defined in there rather than adding new client-side proxy classes.
The switch to enable this is on the Advanced page in the Add Service Reference dialog window - it should be on by default:
Make sure that your project
has an assembly reference to the common data contract assembly
that this setting is really ON when you add the service reference