C# - WCF - Generics - [KnownType(typeof(xxx))] - c#

My client has 10 tables that it needs to load via an internal WCF to a server. Since all this is internal, I can write both client and server using whatever technique i want.
On the Client, I thought to use LINQ to load data from the tables to a List, List and so on...
On the Server, I thought to have a [DataContract] as follow:
[DataContract]
[KnownType(typeof(Table1))]
[KnownType(typeof(Table2))]
[KnownType(typeof(Table3))]
public class GenericType<T>
{
[DataMember]
public List<T> Data { get; set; }
}
and then add the classes that will represent the matching Tables on the Client.
[DataContract]
public class Table1
{
[DataMember]
public int UserID { get; set; }
[DataMember]
public string FullName { get; set; }
}
[DataContract]
public class Table2
{
[DataMember]
public int UserID { get; set; }
[DataMember]
public string Address1 { get; set; }
}
[DataContract]
public class Table3
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Description { get; set; }
}
When I create the client reference, i'm NOT getting all the classes declared on the server and it seems that ONLY the 1st [KnownType] specified on the [DataContract] becomes visible to the Client.
I was under the impression that Generics was meant to allow multiple types but am I right to think that WCF can only handle one [KnownType] x class ??
And if so, my only way to code this would be to copy and paste 10 times the GenericType class and on each copy, change the [KnownType] ??
Cause if that's the only solution, then what are the real benefits to use Generic instead of straight defined List, List for my params ??
Any thought will help clarify my mind here
The problem happens because unless ONE of the WCF methods uses any of the CLASSES declared as [DataContract] ...it seems that WCF does NOT brings those classes to the Client.
Is this the expected case?

You could try attributing your interface method with the ServiceKnownType attribute for each of the classes.
There is another option, which is to implement the generic lists in classes that are attributed with CollectionDataContract:
[CollectionDataContract]
public class Table1Collection
Inherits List<Table1>
On the client side, you can the edit Reference.svcmap and enumerate each of the collections in the CollectionMappings section:
<CollectionMappings>
<CollectionMapping TypeName="My.Namespace.Table1Collection" Category="List" />
This allows you to reuse the same code on both ends of the pipe.

Related

C# Get value of a property from an object hidden behind Interface

To explain it better, I have an object that has property of interface type that can be carrying a variety of different objects.
public class RequestParameters {
public IRequest Request { get; set; }
}
Say the interface looks something like this:
public interface IRequest {
string Username { get; set; }
string Password { get; set; }
}
Now upon receiving the RequestParameters object, I want to get the value of a property PersonId from the object inside Request property. The interface doesn't and shouldn't contain this property, as not all of the Request types have that property. And I don't know which of the dozens of types will the Request object correspond to, so casting isn't a solution.
So how can I possibly fetch the property PersonId in such a case? I suppose some sort of reflection is the only way, but how exactly can it be done?
What about creating an intermediate interface?
public class RequestParameters
{
public IRequest Request { get; set; }
}
public interface IRequest {
string Username { get; set; }
string Password { get; set; }
}
public interface IRequestWithId : IRequest
{
string ID {get; set; }
}
Now you can check against that interface instead of a concrete class to check if there is an ID or not.
Other than that there is only reflection which is a really bad idea here. When defining interfaces you want to reduce code-coupling which means you donĀ“t want to rely on actual types (that define how things are done) but only their behaviour (what can be done with them). Using reflection would circumvent this as you indirectly rely on the actual types. Furthermore using reflection is bad for maintainance, in particular when you refactor the methods (renaming for example).
first of all, in c# there is another type called abstract class which in my opinion would be a better suit for your situation. take a look at this question. interfaces are not meant to hold variables, only functions.
public abstract class ARequest
{
string Username { get; set; }
string Password { get; set; }
public abstract int GetId() {}
}

Architecture: entity framework Web Api

I'm creating a web api program using entity framework. So as the basis, I have an sql server database which I'm connected to with entity framework trought my web api program. Using an add-on for entity framework, I'v generated classes according to my database tables. However i don't want to use these classes for my webservices because I don't need to display some of the attributes generated by the entity framework and little bit tricky with all the proxies problems. These attributes are especially generated because of the foreign keys. As below, for this generated class, I don't need to display "Societe" object and "Utilisateur" object:
public partial class FonctionnalitePerUser
{
public int FonctionUserLngId { get; set; }
public int FonctionUserLngUserId { get; set; }
public int FonctionUserLngSocieteId { get; set; }
public virtual Societe Societe { get; set; }
public virtual Utilisateur Utilisateur { get; set; }
}
I would need some advice to avoid displaying that entities in my webservices.
I was thinking about 3 possibilities:
As it's a partial class, I might create an other partial class with the same name where I put the attributes that I need and override the constructor.
I might inherit a custom class from that one to override the constructor in order to get one structured as I need.
I might create Management classes with functions that create the perfect objects that I need for my webservices. I mean functions that convert "FonctionnalitePerUser" object to "FonctionnalitePerUserCustom" objects.
These are the 3 solutions that I've found. In order to get the best performance, I was wondering if anyone can give me some advise about that or either propose some other solutions.
Thanks in advance
If your using Newtonsoft Json.NET which I think is the default in MVC5 then you can attribute your properties to tell newtonsoft what to serialize and what to ignore.
public class Car
{
// included in JSON
public string Model { get; set; }
public DateTime Year { get; set; }
public List<string> Features { get; set; }
// ignored
[JsonIgnore]
public DateTime LastModified { get; set; }
}
or if you have more properties you want to ignore than you want to serialize you can do this:
[DataContract]
public class Computer
{
// included in JSON
[DataMember]
public string Name { get; set; }
[DataMember]
public decimal SalePrice { get; set; }
// ignored
public string Manufacture { get; set; }
public int StockCount { get; set; }
public decimal WholeSalePrice { get; set; }
public DateTime NextShipmentDate { get; set; }
}
this information was taken from here.
In general, it is often useful to expose a different type of object for a web service API than for persistence. This is for exactly the reason you state: because you don't need to expose all of that persistence stuff to the rest of the world (clients).
Usually, you would map the information that you want to expose from your persistence model (EF entities etc) to a view model object (or DTO).
So, I would say your option 3 is on the right track.
I might create Management classes with functions that create the
perfect objects that I need for my webservices. I mean functions that
convert "FonctionnalitePerUser" object to
"FonctionnalitePerUserCustom" objects
There are several tools out there that help with the converting or mapping of the objects. One is AutoMapper which will map by convention. This can save a lot of mapping code.

WCF Data Services (OData) - Expand navigation properties using foreign key

I am currently writing an OData service that is based on the WCF Dataservices Toolkit.
There are several objects that are exposed by the service an example of which is listed below.
public class Entitlement : IEntity
{
#region Implementation of IEntity
public string Id { get; set; }
#endregion
public string ItemId { get; set; }
[ForeignProperty]
public Item Item { get; set; }
}
public class Item : IEntity
{
#region Implementation of IEntity
public string Id { get; set; }
#endregion
public string ItemName { get; set; }
}
Due to the data being retrieved from 2 separate data sources I only want to store the Id of the Item in the Entitlement object rather than the whole Item object.
This works for queries such as: Entitlement('1')/Item, the service understands that it needs to use the ItemId to perform the lookup.
However the problem occurs when I try and expand the Item using the below URL
Entitlement('1')?$expand=Item
The Item always comes back as null, I understand that this is because I am not storing the Item on the entitlement object, but is there anyway that I can force OData to treat the expand statement the same way it treats the projection?
I have tried Entitlement('1')?$select=Item but this also comes back as null.
Any suggestions would be greatly appreciated.
To expand the objects referenced by a navigation property (collection), I think you need to use the $links syntax in the URI.
See Section 3.2"Addressing Links Between Entities" in the OData Protocol URI Conventions doc.
To be able to use the $expand your module must have the virtual keyword on your linked property
public class Entitlement : IEntity
{
#region Implementation of IEntity
public string Id { get; set; }
#endregion
public string ItemId { get; set; }
public virtual Item Item { get; set; }
}
This will allow you to use the oData query option $expand
Entitlement('1')?$expand=Item

Change class signature, process old xml serialized instances

Let's assume I have got a class like:
public class SomeObject
{
public Guid InternalId { get; set; }
public string Address { get; set; }
}
I store instances of this object into the ASP.NET profile. It get's XML serialized and everything is fine. Now I want to reduce the size of the profile, and I want to replace the long propertynames by something shorter:
public class SomeObject
{
[XmlElement("id")]
public Guid InternalId { get; set; }
[XmlElement("ad")]
public string Address { get; set; }
}
New objects get serialized just fine, and short, and everything. However: the XmlSerializer cannot deserialize the old XML files. Is there any hook I can apply to change a classes signature, but still be able to deserialize old instances.
I have the eventhandler XmlSerializer_UnknownElement, and then I can set the value of the target property myself, however I only have the value of the element as a string, so I should parse it by myself which is quite error-prone.
Two answers, one I know will work, the other I'm not sure.
1) Implement the IXmlSerializable interface in your class. Its very easy to do, and gives you complete control over how the class is serialized and deserialized.
http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
2) Not sure if this will work, but try adding another XmlElementAttribute tag to your class properties. It compiles, but I'm not sure if it'll work.
public class SomeObject
{
[XmlElement("InternalId")]
[XmlElement("id")]
public Guid InternalId { get; set; }
[XmlElement("Address")]
[XmlElement("ad")]
public string Address { get; set; }
}

Why does the proxy generated code create a new class when a MessageContract is in my WCF Service?

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

Categories

Resources