WCF - do not serialize (emit) empty collections - c#

We are implementing an Client / Server application. Data are sent throughout the LAN. Where LAN means company network with several sites / locations.
We are using WCF and NetTcpBinding (EDIT: VS2010 .net 4.0).
I know that [DataMember(EmitDefaultValue = false)] is not recommended by Microsoft. But as mentionend above, data might be sent from one site to another. Therefore: size really matters!
Not sending the default value works most of the time fine. I have just an issue with collections of any kind. I do not want to transfer empty collections!
So I usually end up with to members of the same type (one for work, one work network) and I need to implement the methods OnSerializing and OnDeserialized.
[DataMember(EmitDefaultValue = false)]
private List<someType> data = new List<someType>();
[NonSerialized]
private List<someType> network = new List<someType>();
[OnDeserialized]
private void OnDeserialized(StreamingContext c)
{
if (network == null)
data = new List<someType>();
else
data = network;
}
[OnSerializing]
private void OnSerializing(StreamingContext c)
{
if (data.Count > 0)
network = data;
else
network = null;
}
Is there any elegant way to do that?
Or maybe even a completely different approach?
Remark: for simplicity I did not care about possible multi-threading issues.

But as mentionend above, data might be sent from one site to another.
Therfore: size really matters!
Do you really think that a few Bytes will make a big difference using NetTcpBinding in a LAN ? Did you made a load test to show that.
I know that [DataMember(EmitDefaultValue = false)] is not recommended
by Microsoft
It's not recommanded because it's not interoperable. This recomandation does not apply to your case as you have only WCF Clients/Server on a NetTcpBinding. The config already does not support interop (through java or php).
The WCF binary encoder (uned in NetTcpBinding) supports Gzip/Deflate compression since .net 4.5. You will gain more Bytes with this feature than removing empty collections.
Read more here.

Related

PcapDotNet and Datalink type

I'm trying to manipulate some network captures (pcap format) using Pcap.net.
I'm opening the pcap file and creating the dumper with:
OfflinePacketDevice selectedDevice = new OfflinePacketDevice(pcapInFile);
using (PacketCommunicator PcapReader = selectedDevice.Open(655360, PacketDeviceOpenAttributes.Promiscuous, 1000))
{
PacketDumpFile PcapWriter = PcapReader.OpenDump(pcapOutFile);
PcapReader.ReceivePackets(count, PacketDispatcher);
}
And the PacketDispatcher would be something like:
private void PacketDispatcher(Packet packet)
{
// Manipulate the packet
PcapWriter.Dump(packet);
}
Everything is ok as far as the pcapInFile Datalink is ethernet type. But i have several captures without ethernet layer (rawip) and i have to build a new ethernet layer. In this kind of caps the datalink type is the same as the pcapInFile (raw ip) and i want to change it to ethernet...
If i store all the manipulated packets in a ienumerable and dump them with:
PacketDumpFile.Dump(pcapOutFile, new PcapDataLink(1), Packets.Count(), Packets);
It works fine... But, this is not very useful if you are dealing with files of several gigas...
Any idea?
Thanks!
Assuming the concern is about having all packets in RAM, you can create an IEnumerable that doesn't contain everything in RAM using yield.
This way as Dumper dumps packets it will call your method to populate the next item using yield.
Well, despite this is not an answer but a workaround, and since nobody wrote a better solution this is how a fixed it:
Use SharPcap instead of PcapDotNet, then you can declare a reader and a writer this way:
public CaptureFileReaderDevice PcapReader;
public CaptureFileWriterDevice PcapWriter;
PcapReader = new CaptureFileReaderDevice(fileIn);
PcapReader.OnPacketArrival += packetDispatcher;
PcapReader.Capture();
In the packetDispatcher function:
RawCapture raw = new RawCapture(LinkLayers.Ethernet, e.Packet.Timeval,RebuildNullLinkLayer(e, offset));
CaptureHandler.PcapWriter.Write(raw);
And in the RebuildNullLinkLayer function you can add the ethernet layer, modify whatever you want, etc...
Note that when you call the RawCapture Constructor you can choose the Link Layer (mi original issue with Pcap.Net...), so if you are parsing a RawIp capture, you can convert the packets to Ethernet.

WCF Compression with NetTcpBinding in .net 4.0

I finally managed to get compression working with NetTcpBinding in WCF in .net 4.0. But it seems quite nasty to me, so maybe someone else has a better idea.
Some general Information:
I know this is working with .net 4.5 out of the box - but we are stuck to 4.0
I found several examples with CustomBinding - but we want to stick with NetTcpBinding because of the binary enconding (it is simply faster than text enconding)
We are doing all the configuration in code (except server address), so for the customer it is just plug and play and no chance to change anything - but made it also sometimes difficult to get an example working (most are provided in config files)
My first approach was implementing a message dispatcher on client and server side which does the compression:
http://dotnetlombardia.org/b/tonyexpo/archive/2011/03/09/compressione-in-wcf.aspx
But whenever I changed (or replaced) the original message by any means, the AfterReceiveReply on client side was never executed.
Though in WCF traces I could see that the client received the message and did even send an ACK to the server which the server received! But the client went in timeout?!
Then I found the MessageEncoderFactory for compression by Microsoft:
http://msdn.microsoft.com/en-us/library/ms751458%28v=vs.110%29.aspx
And applied the bug fix provided here:
http://blogs.msdn.com/b/dmetzgar/archive/2011/03/14/gzipmessageencoder-part-two.aspx
Finally I did inherit the NetTcpBinding and applied the new message encoder in the CreateBindingElements function:
public class CompressedNetTcpBinding : NetTcpBinding
{
MyCompressionMessageEncodingBindingElement compressionEncoding;
public CompressedNetTcpBinding()
: base()
{
FieldInfo fi = typeof(NetTcpBinding).GetField("encoding", BindingFlags.Instance | BindingFlags.NonPublic);
BinaryMessageEncodingBindingElement binaryEncoding = (BinaryMessageEncodingBindingElement)fi.GetValue(this);
compressionEncoding = new MyCompressionMessageEncodingBindingElement(binaryEncoding, CompressionAlgorithm.Deflate);
}
/// <summary>
/// Exchange <see cref="BinaryMessageEncodingBindingElement"/> and use compressed encoding binding element.
/// </summary>
/// <returns>binding elements with compressed message binding element</returns>
public override BindingElementCollection CreateBindingElements()
{
BindingElementCollection bec = base.CreateBindingElements();
BinaryMessageEncodingBindingElement enc = null;
foreach (BindingElement be in bec)
{
if (be is BinaryMessageEncodingBindingElement)
{
enc = (BinaryMessageEncodingBindingElement)be;
break;
}
}
bec.Remove(enc);
bec.Insert(2, compressionEncoding);
return bec;
}
}
I did forward the ordinary BinaryMessageEncoder to the compression encoding, so when I change any settings of the NetTcpBinding, e.g. ReaderQuotas they are applied correctly.
I know the enconding member in NetTcpBinding is also used in the private function IsBindingElementsMatch but so far that did not cause any problems.
On my local machine, the (startup) performance penalty is insignificant (100ms - 250ms). But on LAN and WAN there is a significat performance increase (up to several seconds).
So what do you think:
Is that a (the) way to go?
Are there any better solutions?

How return List<string>[] in a web service method

I want to return List<string>[] in a web service and use that return in a windows form like below :
[WebMethod]
public List<string>[] MyMethod()
{
...
...
List<string>[] list_ar = new List<string>[]{list_optgroup, list_option, list_optgroup_option};
return list_ar;
}
but on the windows form side I should get that return value like this :
MyService_Soft service = new MyService_Soft();
string[][] str_ar = service.MyMethod();
What is going on and how can I get that List<string>[] on the windows form side?
Also it seems like I have an error in these lines :
MyService_Soft service = new MyService_Soft();
string[][] str_ar = service.FillComboBoxes();
Error :
Unable to automatically step into the server. Connecting to the server
machine 'blablabla' failed.unknown user name or bad password...
What does this error mean and how can I figure out what line in that web service causes this error?
I see no bad errors. You can't debug 2 processes simultaneously from one debugging process. Since server code is running in separate process, you can't step-into it.
To debug server code, open another instance of MS Visual studio (or whatever IDE you use) with server project source code, and go to menu Debug -> Attach to process, then find your server service hosting process and click "Attach".
As for returning string[][] instead of List[] - it is also expected behaviour as client application doesn't know the type of returned collection - the proxy class is autogenerated based on WSDL file. Actually you can change it to use List<> instead of array. Considering WCF service, open WCF SErvice reference properties and select the type of collections (by default array, but can be changed to List in you desire).
But I see no reasons to require to get List instead of array. The only difference is that List is mutable. You shouldn't logically want to have ability to change the returned collection! You'd better create a new collection, based on the returned array and modify it instead.
UPDATE: Code request.
The code for the last and main recommendation is really very straight forward:
public List<string>[] SomeClientBuilsenessLogicMethod()
{
var serviceClient = GetServiceClientInstance(); //you might want to have single instance (single connection) of WCF service client, so I implemented it's creation as factory method here.
string[][] serviceData = serviceClient.MyMethod();
List<string>[] mutableDataList = serviceData.Select(x=>x.ToList()).ToArray();//Not the best memory usage here probably (better is a loop), but for not big data lists it's fine.
//Perform any needed operations for list changing. Again there is NO NEED to use List if you don't plan to add/remove items to/from that collection.
return mutableDataList;
}

MVC Caching of frequently accessed data that would very infrequently change

I'm new to the concept of caching data and I wanted to ask if there are any issues I"m overlooking with the strategy I"m building for caching data in my MVC web application from my database. I want to stress that basically the data I'm looking at caching is practically read only, and would only be updated very very infrequently (coinciding with code upgrades where the app would be being refreshed).
From what I have been able to research I was planning on using a static class to serve as a helper for building and maintaining the cache. The below code shows how I would build the cache from a database table which would contain the different baseball card condition grades (Mint, Near Mint, etc)
Static CacheHelper class:
public static class CacheHelpers
{
private static HashSet<baseballcardcondition> _CardConditionsCache = null;
public static HashSet<baseballcardcondition> CardConditionsCache
{
get
{
if (_CardConditionsCache == null)
{
_CardConditionsCache = (HttpContext.Current.Cache["CardConditionsCache"] as HashSet<baseballcardconditions>);
if (_CardConditionsCache == null)
{
mydbEntities db = new mydbEntities();
_CardConditionsCache = new HashSet<baseballcardconditions>(db.baseballcardconditions);
HttpContext.Current.Cache.Insert("CardConditionsCache", _CardConditionsCache);
}
}
return _CardConditionsCache;
}
set
{
HttpContext.Current.Cache.Insert("CardConditionsCache", _CardConditionsCache);
}
}//public static HashSet<baseballcardconditions> CardConditionsCache
}//public static class CacheHelpers
Later I would be using this data to build a formatted string for a jqGrid grid (a jQuery javascript plugin for displaying tabular data) and would access it via:
//cardConditionSelectListHelperString
string tempString = "";
foreach (var keyPair in CacheHelpers.CardConditionsCache)
{
tempString += keyPair.IdBaseballCardCondition + ":" + keyPair.Condition + ";";
}
Does this look like a solid, robust, and thread safe way to manage my cache for my MVC web application? I would probably expand my CacheHelpers class to have other cached Hashsets so I don't have to hit the database for this read only, basically static data.
Thank you in advance.
"Does this look like a solid, robust, and thread safe way to manage my cache for my MVC web application? "
No, it doesn't. For example how do you keep the size of your data low? You should flush the unused data from the cache (check this: http://msdn.microsoft.com/en-us/library/dd632018.aspx). In addition it is not thread safe.
I think create an own cache solution is the typical "re-inviting the wheel" scenario, there are a lot of cache (even distributed cache) on the market - in addition some of them are free. A good cache solution is much more complex than just start using some static field.

How do I look up a Corba service with C# and IIOP.NET?

I am writing a C# client for a Corba server and I am using IIOP.NET, going by the example on the following page: http://iiop-net.sourceforge.net/rmiAdderDNClient.html
I have gotten this far without errors:
// Register channel
IiopClientChannel channel = new IiopClientChannel();
ChannelServices.RegisterChannel(channel, false);
// Access COS naming context
CorbaInit init = CorbaInit.GetInit();
NamingContext context = init.GetNameService(host, port);
The variable "host" is a string with the computer name of the server and "port" is an int representing the port number. The values for these are currently used by other systems to connect to the server so I can confirm that they are correct.
However, trying to connect to the trader service yields an exception in runtime. Here is the code I use to do that:
// Looking up VB Trader
NameComponent[] names = new NameComponent[] { new NameComponent("TraderInterface") };
object obj = context.resolve(names);
And here is the error message I'm getting:
"CORBA system exception : omg.org.CORBA.INV_OBJREF, completed: Completed_No minor: 10102."
This seems to suggest an invalid object reference, but what does that mean? Is the string I am passing to the resolve method incorrectly formatted? I have tried many different names for this service as used in other systems, but I always get the same error, which makes me wonder whether I am even interpreting it correctly.
Incidentally, in my desperation, I have also attempted to obtain an object reference from the IOR, but this again throws a different exception (namely omg.org.CORBA.ORB_package.InvalidName).
OrbServices orb = OrbServices.GetSingleton();
object obj = orb.resolve_initial_references(traderIOR);
Any advice is welcome.
I was never able to reach my server with any of the above methods, however the following code is what finally got the communication working:
Hashtable props = new Hashtable();
props[IiopChannel.BIDIR_KEY] = true;
props[IiopServerChannel.PORT_KEY] = port;
// register corba services
IiopChannel channel = new IiopChannel(props);
ChannelServices.RegisterChannel(channel, false);
MyInterface obj = (MyInterface)RemotingServices.Connect(typeof(MyInterface), ior);
I'm not entirely sure why I had to use this (seemingly) unconventional way. Perhaps it is due to the lack of a naming service running on the server. Whatever the cause, I hope this helps somebody out there.

Categories