Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable serialization of POCO types via MobileFormatter #2148

Open
rockfordlhotka opened this issue Mar 11, 2021 · 9 comments
Open

Enable serialization of POCO types via MobileFormatter #2148

rockfordlhotka opened this issue Mar 11, 2021 · 9 comments

Comments

@rockfordlhotka
Copy link
Member

rockfordlhotka commented Mar 11, 2021

There are times when it would be nice to include a simple POCO style type in an object graph. Right now the most basic serializable type is MobileObject, which requires manual work to serialize things like fields.

There are several thoughts to consider. One is documented here, which is to use a base class. Another would be to have MF trigger on an attribute that delegates to an arbitrary "serializer" that returns/accepts a byte[] representing that class. Yet another would be to pass in a list of serializers to MobileFormatter (via DI), where each of these serializers would have the opportunity to say that they can serialize a given type that would otherwise not be serialized by MF.

MobilePoco base class

It is possible to create a MobilePoco type that uses reflection to get/set property and field values as needed by the MobileFormatter. This should be added to the Csla.Core namespace.

Here's a quick prototype of the idea:

using System;
using Csla.Serialization.Mobile;

namespace ConsoleApp3
{
  [Serializable]
  public abstract class MobilePoco : IMobileObject
  {
    void IMobileObject.GetChildren(SerializationInfo info, MobileFormatter formatter)
    {
    }

    void IMobileObject.SetChildren(SerializationInfo info, MobileFormatter formatter)
    {
    }

    private const System.Reflection.BindingFlags bindingFlags =
      System.Reflection.BindingFlags.Public |
      System.Reflection.BindingFlags.NonPublic |
      System.Reflection.BindingFlags.Instance;

    void IMobileObject.GetState(SerializationInfo info)
    {
      var fields = this.GetType().GetFields(bindingFlags);
      foreach (var item in fields)
        info.AddValue(item.Name, item.GetValue(this));
      var properties = this.GetType().GetProperties(bindingFlags);
      foreach (var item in properties)
        info.AddValue(item.Name, item.GetValue(this));
    }


    void IMobileObject.SetState(SerializationInfo info)
    {
      var fields = this.GetType().GetFields(bindingFlags);
      foreach (var item in fields)
        item.SetValue(this, info.GetValue<object>(item.Name));
      var properties = this.GetType().GetProperties(bindingFlags);
      foreach (var item in properties)
        item.SetValue(this, info.GetValue<object>(item.Name));
    }
  }
}

SerializeUsing attribute

[SerializeUsing(typeof(MySerializer))]
public class MyPoco {}

Presumably the MySerializer type would implement Csla.Serialization.ISerializationFormatter.

Custom Serializer List

Regarding the DI injection of a list of serializers, those serializers might implement an interface like this:

public interface ISerializer
{
    bool SerializesType(Type type);
    byte[] Serialize(object obj);
    object Deserialize(byte[] data);
}

When MF encounters a type it wouldn't otherwise be able to serialize, it would go through the list of custom serializers, calling SerializesType on each. If one returns true, then it would be used to serialize the object in question.

This technique could be used to generalize the way ClaimsPrincipal is currently handled. Right now it is hard-coded into MF as an exception, but that implementation could be moved out to a custom serializer instead.

@rockfordlhotka rockfordlhotka changed the title Add MobilePoco to Csla.Core Enable serialization of POCO types via MobileFormatter Mar 12, 2021
@angtianqiang
Copy link

GOOD

@bradtwurst
Copy link
Contributor

bradtwurst commented Jul 21, 2021

As I am attempting to migrate away from WCF and deal with a lot of POCO objects, I found my way here.

I found MobileFormatter very tough (frustrating) to deal with as it is sealed, locked-down, and is currently hard-coded in many of the portal implementations (although I do see that this last issue has changed in main branch).

While this may not be the ultimate solution, would it make sense to have an OnSerializing/Deserializing method hooks that could be utilized to allow the app developer to inherit from MF and tweak the logic.

I know that this wouldn't solve all the things, but it is a fairly straight-forward pattern that was used in other parts of the framework and it could be implemented in the current v5 release without breaking all the things.

If it sounds like an acceptable strategy, I'd be willing to invest some effort into this area as it is directly affecting my current focus.

James

@rockfordlhotka
Copy link
Member Author

@bradtwurst let's have that discussion here: #2386 - so as not to dilute this particular backlog item, which is more focused on a specific issue.

@TheCakeMonster
Copy link
Contributor

I have been continuing to think about the addition to support custom serializer types; I hadn't forgotten - I just chose another route for the serialization of POCOs. I am particularly aware of the hard-coding in MobileFormatter associated with CslaClaimsPrincipal, and I thought the support for custom serializers would alleviate that. However, I then started to wonder if there was a simpler approach to that solution; if we implemented IMobileObject on CslaClaimsPrincipal that might negate the need for a custom serializer for it. If I'm right then the need for a significant change to the framework might feel less appropriate again.

Is there any reason why implementing IMobileObject on CslaClaimsPrincipal doesn't remove the need for the custom code which handles it in the MobileFormatter class? The section of code to which I refer is in MobileFormatter.cs, around line 151, as well as the equivalent in the Deserialize method, around line 265.

@TheCakeMonster
Copy link
Contributor

TheCakeMonster commented Oct 2, 2021

Even if we resolve the problem with CslaClaimPrincipal as I have described, there is one remaining issue - the custom handling of System.Security.Claims.ClaimsPrincipal.

We can't resolve ClaimsPrincipal through the addition of a SerializeUsing attribute; we don't have control over the source code of that type (nor would we want to add the assembly reference that it would require.)

Using DI to manage custom serializers is an alternative that would work for a built in type. The approach I considered was to query DI to ask if a serializer is registered for a type. Sadly the ability to query DI has only just been added as part of .NET 5 or 6, meaning this approach wouldn't work for older runtimes.

I think that leaves registering a list of custom serializers as the most viable option to resolving the problem with ClaimsPrincipal.

@rockfordlhotka
Copy link
Member Author

@TheCakeMonster there are some warnings during build relative to the new generator code:

2>E:\src\rdl\csla\Source\Csla\Serialization\AutoNonSerializedAttribute.cs(19,10,19,36): warning CS1591: Missing XML comment for publicly visible type or member 'AutoNonSerializedAttribute.AutoNonSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializableAttribute.cs(20,10,20,35): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializableAttribute.AutoSerializableAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializedAttribute.cs(20,10,20,33): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializedAttribute.AutoSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoNonSerializedAttribute.cs(19,10,19,36): warning CS1591: Missing XML comment for publicly visible type or member 'AutoNonSerializedAttribute.AutoNonSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializableAttribute.cs(20,10,20,35): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializableAttribute.AutoSerializableAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializedAttribute.cs(20,10,20,33): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializedAttribute.AutoSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoNonSerializedAttribute.cs(19,10,19,36): warning CS1591: Missing XML comment for publicly visible type or member 'AutoNonSerializedAttribute.AutoNonSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializableAttribute.cs(20,10,20,35): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializableAttribute.AutoSerializableAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializedAttribute.cs(20,10,20,33): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializedAttribute.AutoSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoNonSerializedAttribute.cs(19,10,19,36): warning CS1591: Missing XML comment for publicly visible type or member 'AutoNonSerializedAttribute.AutoNonSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializableAttribute.cs(20,10,20,35): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializableAttribute.AutoSerializableAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializedAttribute.cs(20,10,20,33): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializedAttribute.AutoSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoNonSerializedAttribute.cs(19,10,19,36): warning CS1591: Missing XML comment for publicly visible type or member 'AutoNonSerializedAttribute.AutoNonSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializableAttribute.cs(20,10,20,35): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializableAttribute.AutoSerializableAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializedAttribute.cs(20,10,20,33): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializedAttribute.AutoSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoNonSerializedAttribute.cs(19,10,19,36): warning CS1591: Missing XML comment for publicly visible type or member 'AutoNonSerializedAttribute.AutoNonSerializedAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializableAttribute.cs(20,10,20,35): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializableAttribute.AutoSerializableAttribute()'
2>E:\src\rdl\csla\Source\Csla\Serialization\AutoSerializedAttribute.cs(20,10,20,33): warning CS1591: Missing XML comment for publicly visible type or member 'AutoSerializedAttribute.AutoSerializedAttribute()'

@TheCakeMonster
Copy link
Contributor

Oh, OK. Yes, I'll have a look at that. I didn't spot those, sorry. I didn't think of adding documentation to them, as they are not really visible for the documentation to be of benefit.

As an aside, you can now see the generated output if you have a special workload installed.

http://stevetalkscode.co.uk/debug-source-generators-with-vs2019-1610

@TheCakeMonster
Copy link
Contributor

I've created issue #2659 to track the one remaining area of concern - that the source generator creates build warnings (and probably doesn't work) for libraries that target anything other than .NET Standard. This allows easier tracking of that problem.

With that separated out, this task can be closed.

@rockfordlhotka
Copy link
Member Author

Related to #2531.

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

No branches or pull requests

4 participants