Skip to content

Commit

Permalink
Feature/remove on deserialized (#3955)
Browse files Browse the repository at this point in the history
* Updated ISessionManager and SessionManager with timeout and cancellation support

Updated the `ISessionManager` interface and `SessionManager` class to include a `TimeSpan` parameter in the `RetrieveSession` and `SendSession` methods for timeout specification. Overloads of these methods have been added to accept a `CancellationToken` parameter for operation cancellation. The `StateManager` class's `GetState` and `SaveState` methods have also been updated to accept a `TimeSpan` parameter for timeout. Added a new project `Csla.Blazor.WebAssembly.Tests` for unit tests of the `SessionManager` class, including tests for timeout and cancellation scenarios. The solution file and a new project settings file have been updated accordingly. The `SessionManager` class now handles `OperationCanceledException` by rethrowing the exception.

* `Refactor SessionManager and StateManager for improved session handling`

Refactored `SessionManagerTests.cs` and `SessionManager.cs` to improve session handling. The `SessionMessage` object has been changed from a local variable to a class-level variable, allowing it to be accessed across different methods. The `RetrieveSession` and `SendSession` methods have been updated to return the session and assert that the returned session is equal to `SessionValue.Session`. The exception type expected in these methods with zero timeout and cancelled cancellation token has been changed from `TaskCanceledException` to `TimeoutException`. The `GetCancellationToken` method has been simplified to create a `CancellationTokenSource` with a timeout directly. In `StateManager.cs`, the `GetState` method has been updated to not return a `Session` object, instead, it just retrieves the session without assigning it to a variable.

* Refactor SessionManagerTests and remove certain test methods

This commit includes a significant refactoring of the `SessionManagerTests.cs` file. The `GetHttpClient` method has been simplified by removing the creation of a new `MemoryStream` instance and the use of `CslaBinaryWriter`. The stream's position is no longer reset to 0, and the array is directly obtained from the stream.

Several test methods have been removed, including `RetrieveSession_WithTimeoutValue_ShouldNotThrowException`, `RetrieveSession_WithValidCancellationToken_ShouldNotThrowException`, and `SendSession_WithZeroTimeout_ShouldThrowTimeoutException`. These tests were checking for specific exceptions or session values.

The `SendSession_WithTimeoutValue_ShouldNotThrowException` test method has been modified by removing the assertion that was previously checking if the operation is successful.

Lastly, the `RetrieveCachedSessionSession` method has been modified by removing the call to `GetCachedSession` method of `_sessionManager`.

* Refactor SessionManager and update related tests

Updated `SessionManager.cs` methods `RetrieveSession` and `SendSession` to handle `TaskCanceledException` internally and rethrow as `TimeoutException`. Simplified `SendSession` by removing exception handling and refactored `RetrieveSession` to move `stateResult` handling outside of try-catch block. Renamed test methods in `SessionManagerTests.cs` to reflect these changes and updated expected exception type.

* `Convert SaveState to async in StateManager.cs`

` `

`The SaveState method in StateManager.cs has been converted from a synchronous method to an asynchronous one. This is indicated by the addition of the async keyword and the change in return type from void to Task. Additionally, the call to _sessionManager.SendSession(timeout) within the SaveState method has been updated to use the await keyword, making this method call awaited, in line with the change to make SaveState an asynchronous method.`

* Updated SessionManager and its tests for async operations and better error handling

In this commit, several updates were made to the `SessionManager.cs` and `SessionManagerTests.cs` files. The variable `_sessionManager` was renamed to `_SessionManager` in `SessionManagerTests.cs`. The `Initialize` method was converted to an asynchronous method, and the `RetrieveSession` method call in it was updated to use `await`.

XML comments were added to the `RetrieveSession`, `SendSession`, and `GetSession` methods in `SessionManager.cs` for better code documentation. The `RetrieveSession` and `SendSession` methods were updated to handle `TaskCanceledException` and throw a `TimeoutException` with a custom message.

The `GetSession` method was updated to handle the case where `_session` is `null`, creating and returning a new `Session` object in this case. The `SendSession` method was updated to serialize the `_session` object and send it to the server if `SyncContextWithServer` is `true`.

Finally, the `RetrieveSession` method was updated to retrieve the session from the server if `SyncContextWithServer` is `true`, deserializing and storing the retrieved session in `_session` or calling `GetSession` to get or create a new session if the retrieval is unsuccessful.

* Subject: Refactored test methods and updated session retrieval

Refactored the `Initialize` method in `SessionManagerTests.cs` to be synchronous and removed the `RetrieveSession` call. The `RetrieveSession` call has been added to four test methods to ensure session retrieval before each test. Renamed and converted `RetrieveCAchedSessionSession` to an asynchronous method, adding a `RetrieveSession` call and an assertion for non-null cached sessions. Added a new test method `RetrieveNullCachedSessionSession` to assert null cached sessions.

* Refactor variable names to follow C# naming convention

Updated variable names `_SessionManager` and `SessionValue` to `_sessionManager` and `_sessionValue` respectively, to adhere to the common C# naming convention for private fields. All instances of these variables in the code, including in the `Initialize()`, `Deserialize()`, `RetrieveSession()`, `SendSession()`, and `GetCachedSession()` methods, as well as in test assertions, have been updated accordingly.

* Switch from Moq to NSubstitute in Csla.Blazor.WebAssembly.Tests

This commit represents a significant shift in the mocking framework used for unit testing in the `Csla.Blazor.WebAssembly.Tests.csproj` project. The `Moq` package has been replaced with `NSubstitute` in the project file and throughout the `SessionManagerTests.cs` file. This includes changes in the way mocks are created, set up, and how return values are specified for mocked methods and properties.

Additionally, a new `TestHttpMessageHandler` class has been added to `SessionManagerTests.cs` to mock the behavior of an `HttpClient`. The `GetHttpClient` method has been updated to use this new class, aligning with the switch from `Moq` to `NSubstitute`.

* Refactored code for readability and modified deserialization process

Removed `OnDeserialized` method and related code from several classes, indicating a change in the deserialization process. Reordered using directives and added spaces around equals signs and commas for better readability. Removed unnecessary curly braces and adjusted spaces around curly braces in various methods. Removed decrement of `EditLevel` in `AcceptChanges` method and changed conditions in `Child_PropertyChanged` method. Added `System.Collections.Concurrent` namespace in `ReadOnlyBase.cs`.

---------

Co-authored-by: Luiz Fernando Bicalho <[email protected]>
Co-authored-by: Rockford Lhotka <[email protected]>
  • Loading branch information
3 people authored Jun 1, 2024
1 parent 59035a2 commit 5e27ebc
Show file tree
Hide file tree
Showing 8 changed files with 15 additions and 173 deletions.
29 changes: 1 addition & 28 deletions Source/Csla/BusinessBindingListBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -861,33 +861,6 @@ bool ITrackStatus.IsDeleted

#endregion

#region Serialization Notification

[NonSerialized]
[NotUndoable]
private bool _deserialized = false;

/// <summary>
/// This method is called on a newly deserialized object
/// after deserialization is complete.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnDeserialized()
{
_deserialized = true;
base.OnDeserialized();

foreach (IEditableBusinessObject child in this)
{
child.SetParent(this);
}

foreach (IEditableBusinessObject child in DeletedList)
child.SetParent(this);
}

#endregion

#region Child Data Access

/// <summary>
Expand Down Expand Up @@ -1361,7 +1334,7 @@ void IDataPortalTarget.Child_OnDataPortalException(DataPortalEventArgs e, Except
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void Child_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_deserialized && RaiseListChangedEvents && e != null)
if (RaiseListChangedEvents && e != null)
{
for (int index = 0; index < Count; index++)
{
Expand Down
19 changes: 0 additions & 19 deletions Source/Csla/BusinessListBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,6 @@ private void AcceptChanges(int parentEditLevel)
if (child.EditLevelAdded > EditLevel)
DeletedList.RemoveAt(index);
}

if (EditLevel < 0) EditLevel = 0;
}

Expand Down Expand Up @@ -883,24 +882,6 @@ bool ITrackStatus.IsDeleted

#endregion

#region Serialization Notification

/// <summary>
/// Reset parent references on deserialization.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnDeserialized()
{
base.OnDeserialized();
foreach (IEditableBusinessObject child in this)
child.SetParent(this);

foreach (IEditableBusinessObject child in DeletedList)
child.SetParent(this);
}

#endregion

#region Child Data Access

/// <summary>
Expand Down
23 changes: 1 addition & 22 deletions Source/Csla/Core/ExtendedBindingList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,32 +249,11 @@ protected virtual void OnRemoveEventHooks(T item)
child.ChildChanged -= Child_Changed;
}

/// <summary>
/// This method is called on a newly deserialized object
/// after deserialization is complete.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnDeserialized()
{
// do nothing - this is here so a subclass
// could override if needed
}

void ISerializationNotification.Deserialized()
{
// don't rehook events here, because the MobileFormatter has
// created new objects and so the lists will auto-subscribe
// the events
OnDeserialized();
}

[System.Runtime.Serialization.OnDeserialized]
private void OnDeserializedHandler(System.Runtime.Serialization.StreamingContext context)
{
foreach (T item in this)
OnAddEventHooks(item);

OnDeserialized();
// the events;
}

[NonSerialized]
Expand Down
36 changes: 7 additions & 29 deletions Source/Csla/Core/ManagedObjectBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
// <summary>Base class for an object that is serializable</summary>
//-----------------------------------------------------------------------

using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using Csla.Core.FieldManager;
using System.ComponentModel;
using Csla.Reflection;
using Csla.Serialization.Mobile;

Expand All @@ -21,9 +21,9 @@ namespace Csla.Core
/// </summary>
[Serializable]
public abstract class ManagedObjectBase : MobileObject,
INotifyPropertyChanged,
INotifyPropertyChanged,
IManageProperties,
IUseApplicationContext,
IUseApplicationContext,
IUseFieldManager
{
/// <summary>
Expand Down Expand Up @@ -85,7 +85,7 @@ protected static PropertyInfo<P> RegisterProperty<P>(Type objectType, PropertyIn
/// <typeparam name="P">Type of property</typeparam>
/// <param name="propertyLambdaExpression">Property Expression</param>
/// <returns>The provided IPropertyInfo object.</returns>
protected static PropertyInfo<P> RegisterProperty<T,P>(Expression<Func<T, object>> propertyLambdaExpression)
protected static PropertyInfo<P> RegisterProperty<T, P>(Expression<Func<T, object>> propertyLambdaExpression)
{
PropertyInfo reflectedPropertyInfo = Reflect<T>.GetProperty(propertyLambdaExpression);

Expand Down Expand Up @@ -116,7 +116,7 @@ protected static PropertyInfo<P> RegisterProperty<T, P>(Expression<Func<T, objec
/// <param name="propertyLambdaExpression">Property Expression</param>
/// <param name="friendlyName">Friendly description for a property to be used in databinding</param>
/// <returns>The provided IPropertyInfo object.</returns>
protected static PropertyInfo<P> RegisterProperty<T,P>(Expression<Func<T, object>> propertyLambdaExpression, string friendlyName)
protected static PropertyInfo<P> RegisterProperty<T, P>(Expression<Func<T, object>> propertyLambdaExpression, string friendlyName)
{
PropertyInfo reflectedPropertyInfo = Reflect<T>.GetProperty(propertyLambdaExpression);

Expand All @@ -132,7 +132,7 @@ protected static PropertyInfo<P> RegisterProperty<T,P>(Expression<Func<T, object
/// <param name="propertyLambdaExpression">Property Expression</param>
/// <param name="friendlyName">Friendly description for a property to be used in databinding</param>
/// <param name="defaultValue">Default Value for the property</param>
protected static PropertyInfo<P> RegisterProperty<T,P>(Expression<Func<T, object>> propertyLambdaExpression, string friendlyName, P defaultValue)
protected static PropertyInfo<P> RegisterProperty<T, P>(Expression<Func<T, object>> propertyLambdaExpression, string friendlyName, P defaultValue)
{
PropertyInfo reflectedPropertyInfo = Reflect<T>.GetProperty(propertyLambdaExpression);

Expand Down Expand Up @@ -384,7 +384,7 @@ protected virtual bool LoadPropertyMarkDirty(IPropertyInfo propertyInfo, object
var method = t.GetMethods(flags).FirstOrDefault(c => c.Name == "LoadPropertyMarkDirty" && c.IsGenericMethod);
var gm = method.MakeGenericMethod(propertyInfo.Type);
var p = new object[] { propertyInfo, newValue };
return (bool) gm.Invoke(this, p);
return (bool)gm.Invoke(this, p);
}

#endregion
Expand Down Expand Up @@ -459,28 +459,6 @@ protected override void OnSetChildren(SerializationInfo info, MobileFormatter fo

#endregion

#region OnDeserialized


[System.Runtime.Serialization.OnDeserialized]
private void OnDeserializedHandler(System.Runtime.Serialization.StreamingContext context)
{
OnDeserialized(context);
}

/// <summary>
/// This method is called on a newly deserialized object
/// after deserialization is complete.
/// </summary>
/// <param name="context">Serialization context object.</param>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
{
FieldManager?.SetPropertyList(GetType());
}

#endregion

bool IManageProperties.FieldExists(IPropertyInfo property) => FieldManager.FieldExists(property);
List<IPropertyInfo> IManageProperties.GetManagedProperties() => FieldManager.GetRegisteredProperties();
object IManageProperties.GetProperty(IPropertyInfo propertyInfo) => throw new NotImplementedException();
Expand Down
29 changes: 4 additions & 25 deletions Source/Csla/Core/ObservableBindingList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
// <summary>Extends ObservableCollection with behaviors required</summary>
//-----------------------------------------------------------------------

using System.ComponentModel;
using Csla.Serialization.Mobile;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Csla.Properties;
using Csla.Serialization.Mobile;

namespace Csla.Core
{
Expand Down Expand Up @@ -77,7 +77,7 @@ public bool AllowRemove
/// </summary>
public bool RaiseListChangedEvents
{
get { return _raiseListChangedEvents; }
get { return _raiseListChangedEvents; }
set { _raiseListChangedEvents = value; }
}

Expand Down Expand Up @@ -359,32 +359,11 @@ protected virtual void OnRemoveEventHooks(T item)

#region ISerializationNotification Members

/// <summary>
/// This method is called on a newly deserialized object
/// after deserialization is complete.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnDeserialized()
{
// do nothing - this is here so a subclass
// could override if needed
}

[System.Runtime.Serialization.OnDeserialized]
private void OnDeserializedHandler(System.Runtime.Serialization.StreamingContext context)
{
foreach (T item in this)
OnAddEventHooks(item);

OnDeserialized();
}

void ISerializationNotification.Deserialized()
{
// don't rehook events here, because the MobileFormatter has
// created new objects and so the lists will auto-subscribe
// the events
OnDeserialized();
}

#endregion
Expand Down Expand Up @@ -665,7 +644,7 @@ protected override void SetLoadListMode(bool enabled)
else
{
if (_oldRLCE.Count > 0)
_raiseListChangedEvents = _oldRLCE.Pop();
_raiseListChangedEvents = _oldRLCE.Pop();
}
}
}
Expand Down
20 changes: 0 additions & 20 deletions Source/Csla/DynamicBindingListBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,26 +416,6 @@ private PropertyDescriptor GetPropertyDescriptor(string propertyName)

#endregion

#region Serialization Notification

/// <summary>
/// This method is called on a newly deserialized object
/// after deserialization is complete.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnDeserialized()
{
foreach (IEditableBusinessObject child in this)
{
child.SetParent(this);
if (child is INotifyPropertyChanged c)
c.PropertyChanged += Child_PropertyChanged;
}
base.OnDeserialized();
}

#endregion

#region Data Access

private void DataPortal_Update()
Expand Down
20 changes: 2 additions & 18 deletions Source/Csla/DynamicListBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,9 @@ void IParent.RemoveChild(IEditableBusinessObject child)
}


IParent IParent.Parent
IParent Csla.Core.IParent.Parent
{
get { return null; }
get { return null; }
}

#endregion
Expand Down Expand Up @@ -497,22 +497,6 @@ public override bool IsBusy
}
#endregion

#region Serialization Notification

/// <summary>
/// Set parent reference.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnDeserialized()
{
foreach (IEditableBusinessObject child in this)
child.SetParent(this);

base.OnDeserialized();
}

#endregion

#region Data Access

private void DataPortal_Update()
Expand Down
12 changes: 0 additions & 12 deletions Source/Csla/ReadOnlyBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -519,20 +519,8 @@ private void OnDeserializedHandler(System.Runtime.Serialization.StreamingContext
if (_fieldManager != null)
FieldManager.SetPropertyList(GetType());
InitializeBusinessRules();
OnDeserialized(context);
}

/// <summary>
/// This method is called on a newly deserialized object
/// after deserialization is complete.
/// </summary>
/// <param name="context">Serialization context object.</param>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
{
// do nothing - this is here so a subclass
// could override if needed
}

#endregion

Expand Down

0 comments on commit 5e27ebc

Please sign in to comment.