Serialization decoupling #15

Merged
merged 4 commits into from May 21, 2015

Projects

None yet

3 participants

@perturbare
Contributor

This pull request contains a set of changes I made for my own project that uses GraphX. With these changes, the serialization is further decoupled from the LogicCore and GraphArea to make it easier to use other storage types. In my case, I needed to use streams.

Please note: I apologize, but my development environment is Windows 7, so I cannot update the METRO assemblies at this time. If you like these changes, I think you'll find the conversion to be pretty trivial, but I have not messed with those because I cannot test them yet.

In GraphArea, I've changed SerializeToFile and DeserializeFromFile into methods that return or accept the collections of graph data. This makes the GraphArea serialization support more widely usable and allows developers to make use of other storage types without having to extend GraphArea.

The FileServiceProvider property is removed from LogicCore. This removes the file specific dependencies from LogicCore and supports the goal of using other storage types without having to add more provider types to LogicCore.

The IFileServiceProvider interface has been removed. It certainly does not have to be deleted, but it's not necessary to have a common interface for serialization.

FileServiceProviderWpf has been altered in a few ways. The IFileServiceProvider interface goes away. It is converted to a static class because no instance information is needed in it and the calling sites don't need to create an instance. Stream serialization is added for the sake of providing an example (this is the key piece I needed in my project).

GeneralGraph is changed to show how the FileServiceProviderWpf class is used with the changes to GraphArea.

I am open to suggestions, so please let me know your thoughts. This would be a breaking change for some users of GraphX, but I hope you'll find the value of decoupling from file specific I/O is worth the change. Again, I apologize for not being able to provide the METRO updates with this request.

Thanks.

Jon

perturbare added some commits May 20, 2015
@perturbare perturbare Merge pull request #1 from panthernet/PCL
Pull in panthernet's latest
ba1243c
@perturbare perturbare Refactored file handling
Refactored file handling, removing IFileServiceProvider and changing
GraphArea so that it simply provides the data for serialization and
accepts data for deserialization. This makes it easier to handle
serialization with other storage types. (Still need mods for METRO)
1f08a3c
@perturbare perturbare Added stream I/O support
Added stream I/O support to FileServiceProviderWPF. (Still need changes
for METRO projects)
97419fe
@perturbare perturbare Fixed method name
Fixed a method name to match what I intended earlier.
9ec93fe
@panthernet
Owner

Thanks, i think you're right. All the GraphX need to do in this case is to supply serialization data and leave the serialization details to custom code.

@panthernet panthernet merged commit 94c9edc into panthernet:PCL May 21, 2015
@perturbare perturbare deleted the perturbare:Serialization-decoupling branch May 22, 2015
@birbilis
birbilis commented Jun 3, 2015

Using static classes instead of interfaces can mean though that you need to use reflection to call them (e.g. if you wan to have a list of export plugins).

Instead can keep interfaces and make use of MEF to locate import/export and other plugins (you can have some class attribute there that mark the class as a GraphXExporter and MEF can be asked then to give you interface instances from classes that have that attribute.)

see usage at
http://clipflair.codeplex.com/SourceControl/latest#Client/ClipFlair.Windows/ClipFlair.Windows.Map/MapWindowFactory.cs
and
http://clipflair.codeplex.com/SourceControl/latest#Client/ClipFlair.Windows/ClipFlair.Windows.Image/ImageWindowFactory.cs


//Project: ClipFlair (http://ClipFlair.codeplex.com)
//Filename: MapWindowFactory.cs
//Version: 20140318

using System.ComponentModel.Composition;

namespace ClipFlair.Windows.Map
{

//Supported views
[Export("ClipFlair.Windows.Views.MapView", typeof(IWindowFactory))]
//MEF creation policy
[PartCreationPolicy(CreationPolicy.Shared)]
public class MapWindowFactory : IWindowFactory
{
public BaseWindow CreateWindow()
{
return new MapWindow();
}
}

}


//Project: ClipFlair (http://ClipFlair.codeplex.com)
//Filename: ImageWindowFactory.cs
//Version: 20140616

using System.ComponentModel.Composition;
using System.IO;

namespace ClipFlair.Windows.Image
{

//Supported file extensions
[Export(".PNG", typeof(IFileWindowFactory))]
[Export(".JPG", typeof(IFileWindowFactory))]
//Supported views
[Export("ClipFlair.Windows.Views.ImageView", typeof(IWindowFactory))]
//MEF creation Policy
[PartCreationPolicy(CreationPolicy.Shared)]
public class ImageWindowFactory : IFileWindowFactory
{

public const string LOAD_FILTER = "Image files (*.png, *.jpg)|*.png;*.jpg";

public static string[] SUPPORTED_FILE_EXTENSIONS = new string[] { ".PNG", ".JPG" };

public string[] SupportedFileExtensions()
{
  return SUPPORTED_FILE_EXTENSIONS;
}

public BaseWindow CreateWindow()
{
  return new ImageWindow();
}

}

}

then at
http://clipflair.codeplex.com/SourceControl/latest#Client/ClipFlair.Windows/ClipFlair.Windows.Base/Source/View/BaseWindow.xaml.cs

to get the first plugin that supports some contract (I get that contract name from the serialization file [using DataContracts]) for a loaded view, or that supports some file extension for a file dropped inside a component, I do:

protected static IWindowFactory GetWindowFactory(string contract)
{
  Lazy<IWindowFactory> win = mefContainer.GetExports<IWindowFactory>(contract).FirstOrDefault();
  if (win == null)
    throw new Exception(BaseWindowStrings.msgUnknownViewType + contract);
  else
    return win.Value;
}

protected static IFileWindowFactory GetFileWindowFactory(string contract)
{
  Lazy<IFileWindowFactory> win = mefContainer.GetExports<IFileWindowFactory>(contract).FirstOrDefault();
  if (win != null)
    return win.Value;
  else
    return null;
}
@perturbare
Contributor

The class I converted to static is in the sample application. It does not have to be static, but it was a reasonable conversion for the sample. In a case such as yours, you are free to define an interface and build many serializers that implement it. That added flexibility is a benefit of this set of changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment