csla icon indicating copy to clipboard operation
csla copied to clipboard

Add NonSerialized feature for managed backing fields

Open rockfordlhotka opened this issue 4 years ago • 20 comments

It should be possible to have a RegisterNonSerializedProperty method for each RegisterProperty method in BusinessBase and ReadOnlyBase to indicate that the property should not be serialized by MobileFormatter.

rockfordlhotka avatar May 15 '20 21:05 rockfordlhotka

@rockfordlhotka , will this change mean that you can declare an using reference property as a normal managed property without the requirement to create a private backing field for it?

I am just thinking in the lines of existing properties where I need to create the property as a private field only because I do not want to serialize it.

JacoJordaan avatar May 19 '20 16:05 JacoJordaan

@rockfordlhotka . I probably misinterpreted this. This is probably then for replacing the [NonSerialized] attribute that we had with the binary formatter. You will still need the private backing field and still have the [NotUndoable] attribute for an using reference property.

JacoJordaan avatar May 19 '20 16:05 JacoJordaan

The intent is that you should be able to use a managed property (no private field) and indicate that it is nonserialized. And notundoable would make sense too.

Perhaps the overload method idea isn't a good one. Perhaps this is better done using the same relationships parameter used for PrivateField - make that a Flags enum

  • PrivateField
  • NonSerialized
  • NotUndoable

rockfordlhotka avatar May 19 '20 16:05 rockfordlhotka

This will be great - to have a managed property for both nonserialized and notundoable properties. Flag enums sounds good.

I can already see that I can use things like LazyGetPropertyAsync for using reference property that already handles checking to only run once. I have also picked up that the PropertyHasChanged method is not called for private field properties and this should also resolve this.

JacoJordaan avatar May 19 '20 16:05 JacoJordaan

We used this notation in version 4.X to handle backing fields value:

[NotUndoable]
private SomeType _originalObject;

It was helping to "save" field value between DataPortal operations.

@rockfordlhotka, there was any changes with such logic up to version 5.2?

Cause, as i have noticed, value of field _originalObject is not defined at DataPortal_Insert method but was set, for example, at DataPortal_Create method. Before calling .Save i still have value for field _originalObject in business object.

Seems like, after serialization, it wasn't set.

BaHXeLiSiHg avatar May 05 '21 17:05 BaHXeLiSiHg

Nothing has changed in how private backing fields work, not since v4.0.

@BaHXeLiSiHg there are several things going on here.

  1. NotUndoable only affects n-level undo, not serialization
  2. The MobileFormatter doesn't work exactly like the old BinaryFormatter; BinaryFormatter is being slowly deprecated by Microsoft, so eventually it won't matter anymore
  3. Properties with managed backing fields work differently than those with private backing fields (in MobileFormatter)

Your example has a private backing field, marked so it won't participate in n-level undo. It also is not automatically serialized by MobileFormatter, so if it you want it to flow over the network, you need to implement the get/set state methods.

I recommend against using private backing fields because they require manual work, and are therefore error-prone.

rockfordlhotka avatar May 05 '21 18:05 rockfordlhotka

I see, but we have to use such backing fields because they have links to required sources (objects).

Have implemented something like this and it works:

protected override void OnGetState(SerializationInfo info, StateMode mode)
{
    base.OnGetState(info, mode);

    info.AddValue("OriginalObject", ReflectionHelper.GetPropertyValue<object>(this, "OriginalObject"));
}

protected override void OnSetState(SerializationInfo info, StateMode mode)
{
    base.OnSetState(info, mode);

    var originalObject = info.GetValue<object>("OriginalObject");

    ReflectionHelper.SetPropertyValue(this, "OriginalObject", originalObject);
}

But it requires to declare OriginalObject as public property, to access it via reflection :(

Is there any other way to access private property on get/set state if those methods are in base class?

BaHXeLiSiHg avatar May 05 '21 19:05 BaHXeLiSiHg

Since the field is in your same class, why can't you just access it directly without using reflection?

Or if you must use reflection for some reason, use a helper library that doesn't limit you to public fields. Reflection itself can access non-public fields just fine, and CSLA actually has helpers for that purpose.

Also, why would you go through the property to get at the field? It would be more efficient to just get/set the field itself.

rockfordlhotka avatar May 05 '21 19:05 rockfordlhotka

Updated answer, methods was inside base class, but i think i will move them inside target classes to avoid reflection. Thx for tips, going to change classes in this direction.

BaHXeLiSiHg avatar May 05 '21 20:05 BaHXeLiSiHg

How would this be used? I can only vaguely guess what's wanted here. Do you want:

public string NonSerializedProp { get; set; }

// Somewhere in your Code
RegisterNonSerializedProperty(NonSerializedProp);

or

public static readonly PropertyInfo<Guid> IDProperty = RegisterNonSerializedProperty<Guid>(nameof(ID));
public Guid ID
{
    get => GetProperty(IDProperty);
    set => SetProperty(IDProperty, value);
}

If it's for plain old C# propeties. Maybe the notation

[field: NonSerialized]
public int Id { get; set; }

ossendorf-at-hoelscher avatar Mar 13 '24 09:03 ossendorf-at-hoelscher

This is for the MobileFormatter with managed backing fields. Right now, all properties with managed backing fields are serialized by MobileFormatter. Sometimes people have properties that are calculated by rules (for example), and don't need to be serialized.

rockfordlhotka avatar Mar 14 '24 18:03 rockfordlhotka