« Progress On Dataphor | Main | Migrating a Remoting Service to WCF »

Porting a large .NET application to Silverlight

If you've followed the latest in the Dataphor wiki you'll know we're building a Silverlight client for Dataphor.  From the sound of "new client", one would think this effort would involve merely adding a new component to Dataphor, but in truth the effort is basically a port.  This is due to the fact that a Dataphor client runs a Dataphor engine, and because Silverlight doesn't support "older" .NET technologies such as remoting.  All in all, this "new client" actually entails:

  • Modernizing the Dataphor code base.  The code now makes use of generics, compiler inferred type declarations, lambda functions, etc.
  • Removal of legacy portions.
  • Splitting of the Dataphor server into Server and Engine.
  • Replacement of Remoting with WCF.
  • Adding platform support to libraries.
  • Delayed class loading.
  • Porting of the engine to Silverlight.
  • Oh, and a new Silverlight client.

This effort represented nothing short of a massive upheaval of the source base, mostly for the better.  I've not seen a detailed account of the porting of something as large as a relational database engine to Silverlight, so we kept notes along the way in hopes that this might help others.  There are of course many levels at which this port could be discussed, from architecture to syntax.  In this post, I'll just give a rough list of the runtime and BCL issues that were encountered in the port.  Perhaps we'll expand on some of these in a future post.  We may also post more about the architectural issues, such as thread implcations, in a future post.

Here's the overview: 

Issue

Resolution

Unsafe code for buffer splicing

Replaced with safe alternatives.  Added ByteArrayUtility to provide services similar to BitConverter and BinaryReader/BinaryWriter

Can't reference non-Silverlight projects from Silverlight projects

New Silverlight projects with links for each file

No HashTable and ArrayList

Replaced with Dictionary and List generics

Dictionary and List semantic differences

  1. TryGetValue versus this[] == null
  2. No virtuals for added/removed
  3. StringComparer won't work on <object, object>
  4. DictionaryEntry -> KeyValuePair
  5. No ToArray() methods on Keys and Values

No SerializationAttribute

Removed attributes, replaced with DataContract (WCF)

No SerializationInfo

Removed, exception overloads replaced with FaultContract

No StringCollection

Replaced with List<string>

P/Invoke calls to QueryPerformanceCounter APIs

Replaced with calls to Environment.TickCount

No Stopwatch class

Implemented alternative based on Environment.TickCount

No File.GetAttributes()

Moved dependency into platform specific assembly

No AppDomain.GetAssemblies()

Moved dependency into platform specific assembly

No Assembly.Load (remaining overloads are security restricted

Refactored to use AssemblyPart.Load from a byte[]

No WebRequest.GetResponse

Removed dependency

No WebClient.OpenRead, only async supported

No longer dependency

No MD5CryptoServiceProvider

Used this to replace framework implementation: http://www.markharris.net.au/blog/2008/10/23/md5cryptoserviceprovider-for-silverlight/

...Then removed because a comparison was better than a hash in usage case.

No XmlTextWriter

Replaced with XmlWriter

No XmlTextReader

Replaced with XmlReader

No XmlDocument and related

Replaced with XDocument (had to go to 3.5 framework)

No TypeDescriptor (thus no GetTypeConverter)

Replaced with StringToValue and ValueToString implementations that switch on the type and convert the native and native like types to and from strings

No Type.GetInterface overload with one argument

Used the two argument version.

No Diagnostics.Trace class

Used the Debug class instead

No String.Compare with case insenstivite

Replaced with String.Equals(x, y, StringComparison.OrdinalIgnoreCase) or similar Compare overload

No Enum.Parse w/o case specifier

Added explicit false third argument.

No remoting

Replaced with WCF

No Diagnostics.TraceLevel

Removed usage

No UnicodeEncoding.ASCII encoding

Replaced with UTF8 encoding

No 2 argument Convert.ChangeType

Passed Thread.CurrentThread.CurrentCulture as 3rd argument

No Assembly.GetType passing ignoreCase

Called another overload

No Rijndael encryption

Replaced with Aes

No Thread.Interrupt()

Removed usage

ThreadInterruptedException not public

Rewrote Interrupt pattern with AutoResetEvent()

No Environment.MachineName

Compiler conditional

No BitVector32

Removed reference (could have used BitVector)

No System.Drawing (Image etc.)

Image replaced with Media.Imaging.BitmapImage.  Image.Load() becomes BitmapImage.SetSource()

No System.Drawing attributes

Attributes redefined as dummies;

No ComponentModel attributes

Attributes redefined as dummies;

No ReaderWriterLock

Found custom implementation, modified.  Then found usage was minor and removed.

P/Invoke to Terminal Services API

Compiler defined wrapper class

ChannelFactory doesn't support overload that takes URI string

Constructed EndpointAddress as argument

ComponentCollection of IContainer doesn't support enumeration (stub)

Compiler defined usage

No ManualResetEvent.WaitOne(int, false) overload.

Replaced with invocation with just timeout.

No Assembly.CreateQualifiedName

Replaced with string concatenation of <name>,<assembly name>

No Thread.ResetAbort()

Removed reference, we no longer use abort (ever) anyway.

Assembly.GetName() is marked security critical (can't access)

Wrote parser to get Name and Version from the FullName

Can't DataMember serialize a private member

Changed to internal and used the "friend class" mechanism to allow the System.Runtime.Serialization assembly to access

Designer related attributes

Created dummy attributes; used the class name (string) overload rather than typeof() overloads to avoid dependency on designer assemblies.

No C# compiler

Ported Mono*

*This is still in-flux.  We've ported the mono compiler to Silverlight, but haven't tested it yet.  If we run into too much trouble with that, we'll invoke the compiler on the server and load the resulting binary on the client.

Here are some notes regarding the XmlDocument to XDocument porting:

Issue

Resolution

CreateElement

new XElement(…)

CreateAttribute

Element.SetAttributeValue

foreach (XmlAttribute in Element.Attributes)

foreach (XAttribute in Element.Attributes())

Node.NamespaceUri

Node.Name.NamespaceName

Node.Name

Node.Name.LocalName or Node.Name.NamespaceName

Attributes != null

HasAttributes

FirstChild, Children

Elements() enumerator

Document.DocumentElement

Document.Root

XmlDocument.Normalize()

XDocument.Normalize (not present in Silverlight) 

? may not be necessary any longer

Attributes.Remove

SetAttributeValue(x, null)

Attributes[x]

Attribute(x)

XDocument.Load(Stream)

XDocument.Load(XmlReader.Create(Stream))

(BCL doesn't support the overload SL does)

Node.Parent.RemoveChild(Node)

Node.Remove()

ChildNodes

Elements() (in most cases)

 

TrackBack

TrackBack URL for this entry:
http://databaseconsultinggroup.com/blog-mt/mt-tb.fcgi/30

Comments

Sounds very cool guys, great job!

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)