...
 
Commits (7)
    https://gitcode.net/jobily/Prism/-/commit/7ca126dcddbad026d087b42f8c16914af12d7fcd feat: adding Exception Handler 2023-07-06T20:24:56-06:00 Dan Siegel me@dansiegel.net https://gitcode.net/jobily/Prism/-/commit/ee47bfffff7a4bc8e7d0bf9b67ce3b8179d925e2 chore: handle exceptions on CanExecute 2023-07-08T21:38:36-06:00 Dan Siegel me@dansiegel.net https://gitcode.net/jobily/Prism/-/commit/a128f206772a4b97a2fe5baacb50ca6c72f3913a Merge pull request #2887 from PrismLibrary/dev/ds/command-catch 2023-07-09T13:41:23+10:00 Dan Siegel me@dansiegel.net Adding Exception Handler https://gitcode.net/jobily/Prism/-/commit/7879531827c33ae2f75ea71b68f36c9b21dd5a12 chore: revert changes - add back support for net461 2023-07-08T21:53:43-06:00 Dan Siegel me@dansiegel.net https://gitcode.net/jobily/Prism/-/commit/fe01e6c0dd2b3ae0d6098706c85c3f790951bb3d chore: update nuget packages 2023-07-09T00:53:14-06:00 Dan Siegel me@dansiegel.net https://gitcode.net/jobily/Prism/-/commit/c0cc8294d089a722194602d9e7f644bcf2200586 Merge pull request #2908 from PrismLibrary/dev/ds/packages 2023-07-09T17:53:04+10:00 Dan Siegel me@dansiegel.net Update nuget packages https://gitcode.net/jobily/Prism/-/commit/6eb38ab4eaec796b5ecb6e2e00cee4176f3c227a Merge pull request #2906 from PrismLibrary/dev/ds/net46 2023-07-09T17:53:24+10:00 Dan Siegel me@dansiegel.net Revert changes - .NET 461 support
<Project> <Project>
<ItemGroup> <ItemGroup>
<PackageVersion Include="DryIoc.dll" Version="5.4.0" /> <PackageVersion Include="DryIoc.dll" Version="5.4.1" />
<PackageVersion Include="Unity.Container" Version="5.11.11" /> <PackageVersion Include="Unity.Container" Version="5.11.11" />
<PackageVersion Include="System.ValueTuple" Version="4.5.0" /> <PackageVersion Include="System.ValueTuple" Version="4.5.0" />
<PackageVersion Include="System.Reactive" Version="6.0.0" /> <PackageVersion Include="System.Reactive" Version="6.0.0" />
...@@ -9,15 +9,15 @@ ...@@ -9,15 +9,15 @@
</ItemGroup> </ItemGroup>
<!-- Uno --> <!-- Uno -->
<ItemGroup Condition=" $(IsUnoProject) == 'true' "> <ItemGroup Condition=" $(IsUnoProject) == 'true' ">
<PackageVersion Include="Uno.WinUI" Version="4.9.20" /> <PackageVersion Include="Uno.WinUI" Version="4.9.26" />
<PackageVersion Include="Uno.WinUI.Skia.Gtk" Version="4.9.20" /> <PackageVersion Include="Uno.WinUI.Skia.Gtk" Version="4.9.26" />
<PackageVersion Include="Uno.WinUI.Skia.Linux.FrameBuffer" Version="4.9.20" /> <PackageVersion Include="Uno.WinUI.Skia.Linux.FrameBuffer" Version="4.9.26" />
<PackageVersion Include="Uno.WinUI.Skia.Wpf" Version="4.9.20" /> <PackageVersion Include="Uno.WinUI.Skia.Wpf" Version="4.9.26" />
<PackageVersion Include="Uno.Wasm.Bootstrap" Version="7.0.24" /> <PackageVersion Include="Uno.Wasm.Bootstrap" Version="7.0.24" />
<PackageVersion Include="Uno.Wasm.Bootstrap.DevServer" Version="7.0.24" /> <PackageVersion Include="Uno.Wasm.Bootstrap.DevServer" Version="7.0.24" />
<PackageVersion Include="Uno.WinUI.WebAssembly" Version="4.9.20" /> <PackageVersion Include="Uno.WinUI.WebAssembly" Version="4.9.26" />
<PackageVersion Include="Uno.WinUI.RemoteControl" Version="4.9.20" /> <PackageVersion Include="Uno.WinUI.RemoteControl" Version="4.9.26" />
<PackageVersion Include="Uno.UI.Adapter.Microsoft.Extensions.Logging" Version="4.9.20" /> <PackageVersion Include="Uno.UI.Adapter.Microsoft.Extensions.Logging" Version="4.9.26" />
<PackageVersion Include="Uno.Core" Version="4.0.1" /> <PackageVersion Include="Uno.Core" Version="4.0.1" />
<PackageVersion Include="Uno.Core.Extensions.Logging.Singleton" Version="4.0.1" /> <PackageVersion Include="Uno.Core.Extensions.Logging.Singleton" Version="4.0.1" />
<PackageVersion Include="Uno.Microsoft.Xaml.Behaviors.Uwp.Managed" Version="2.3.0" /> <PackageVersion Include="Uno.Microsoft.Xaml.Behaviors.Uwp.Managed" Version="2.3.0" />
...@@ -51,10 +51,10 @@ ...@@ -51,10 +51,10 @@
<PackageVersion Include="Xamarin.Forms.Mocks" Version="4.7.0.1" /> <PackageVersion Include="Xamarin.Forms.Mocks" Version="4.7.0.1" />
<PackageVersion Include="Humanizer.Core" Version="2.14.1" /> <PackageVersion Include="Humanizer.Core" Version="2.14.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" /> <PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.2" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageVersion Include="Moq" Version="4.18.4" /> <PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="xunit" Version="2.4.2" /> <PackageVersion Include="xunit" Version="2.5.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" /> <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.0" />
<PackageVersion Include="Xunit.StaFact" Version="1.1.11" /> <PackageVersion Include="Xunit.StaFact" Version="1.1.11" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" $(UseMaui) != 'true' "> <ItemGroup Condition=" $(UseMaui) != 'true' ">
......
using System; using System;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Prism.Properties; using Prism.Properties;
...@@ -46,7 +47,17 @@ namespace Prism.Commands ...@@ -46,7 +47,17 @@ namespace Prism.Commands
///</summary> ///</summary>
public void Execute() public void Execute()
{ {
_executeMethod(); try
{
_executeMethod();
}
catch (Exception ex)
{
if (!ExceptionHandler.CanHandle(ex))
throw;
ExceptionHandler.Handle(ex, null);
}
} }
/// <summary> /// <summary>
...@@ -55,7 +66,19 @@ namespace Prism.Commands ...@@ -55,7 +66,19 @@ namespace Prism.Commands
/// <returns>Returns <see langword="true"/> if the command can execute,otherwise returns <see langword="false"/>.</returns> /// <returns>Returns <see langword="true"/> if the command can execute,otherwise returns <see langword="false"/>.</returns>
public bool CanExecute() public bool CanExecute()
{ {
return _canExecuteMethod(); try
{
return _canExecuteMethod();
}
catch (Exception ex)
{
if (!ExceptionHandler.CanHandle(ex))
throw;
ExceptionHandler.Handle(ex, null);
return false;
}
} }
/// <summary> /// <summary>
...@@ -100,5 +123,101 @@ namespace Prism.Commands ...@@ -100,5 +123,101 @@ namespace Prism.Commands
ObservesPropertyInternal(canExecuteExpression); ObservesPropertyInternal(canExecuteExpression);
return this; return this;
} }
/// <summary>
/// Registers an callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand Catch(Action<Exception> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
return this;
}
/// <summary>
/// Registers an callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand Catch(Action<Exception, object> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
return this;
}
/// <summary>
/// Registers an callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <typeparam name="TException">The Exception Type</typeparam>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand Catch<TException>(Action<TException> @catch)
where TException : Exception
{
ExceptionHandler.Register<TException>(@catch);
return this;
}
/// <summary>
/// Registers an callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <typeparam name="TException">The Exception Type</typeparam>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand Catch<TException>(Action<TException, object> @catch)
where TException : Exception
{
ExceptionHandler.Register<TException>(@catch);
return this;
}
/// <summary>
/// Registers an async callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand Catch(Func<Exception, Task> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
return this;
}
/// <summary>
/// Registers an async callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand Catch(Func<Exception, object, Task> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
return this;
}
/// <summary>
/// Registers an async callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <typeparam name="TException">The Exception Type</typeparam>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand Catch<TException>(Func<TException, Task> @catch)
where TException : Exception
{
ExceptionHandler.Register<TException>(@catch);
return this;
}
/// <summary>
/// Registers an async callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <typeparam name="TException">The Exception Type</typeparam>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand Catch<TException>(Func<TException, object, Task> @catch)
where TException : Exception
{
ExceptionHandler.Register<TException>(@catch);
return this;
}
} }
} }
...@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; ...@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Threading; using System.Threading;
using System.Windows.Input; using System.Windows.Input;
using Prism.Common;
namespace Prism.Commands namespace Prism.Commands
{ {
...@@ -17,6 +18,11 @@ namespace Prism.Commands ...@@ -17,6 +18,11 @@ namespace Prism.Commands
private SynchronizationContext _synchronizationContext; private SynchronizationContext _synchronizationContext;
private readonly HashSet<string> _observedPropertiesExpressions = new HashSet<string>(); private readonly HashSet<string> _observedPropertiesExpressions = new HashSet<string>();
/// <summary>
/// Provides an Exception Handler to register callbacks or handle encountered exceptions within
/// </summary>
protected readonly MulticastExceptionHandler ExceptionHandler = new MulticastExceptionHandler();
/// <summary> /// <summary>
/// Creates a new instance of a <see cref="DelegateCommandBase"/>, specifying both the execute action and the can execute function. /// Creates a new instance of a <see cref="DelegateCommandBase"/>, specifying both the execute action and the can execute function.
/// </summary> /// </summary>
...@@ -32,7 +38,7 @@ namespace Prism.Commands ...@@ -32,7 +38,7 @@ namespace Prism.Commands
/// <summary> /// <summary>
/// Raises <see cref="ICommand.CanExecuteChanged"/> so every /// Raises <see cref="ICommand.CanExecuteChanged"/> so every
/// command invoker can requery <see cref="ICommand.CanExecute"/>. /// command invoker can re-query <see cref="ICommand.CanExecute"/>.
/// </summary> /// </summary>
protected virtual void OnCanExecuteChanged() protected virtual void OnCanExecuteChanged()
{ {
...@@ -48,7 +54,7 @@ namespace Prism.Commands ...@@ -48,7 +54,7 @@ namespace Prism.Commands
/// <summary> /// <summary>
/// Raises <see cref="CanExecuteChanged"/> so every command invoker /// Raises <see cref="CanExecuteChanged"/> so every command invoker
/// can requery to check if the command can execute. /// can re-query to check if the command can execute.
/// </summary> /// </summary>
/// <remarks>Note that this will trigger the execution of <see cref="CanExecuteChanged"/> once for each invoker.</remarks> /// <remarks>Note that this will trigger the execution of <see cref="CanExecuteChanged"/> once for each invoker.</remarks>
[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
......
using System; using System;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Prism.Properties; using Prism.Properties;
...@@ -79,7 +80,17 @@ namespace Prism.Commands ...@@ -79,7 +80,17 @@ namespace Prism.Commands
///<param name="parameter">Data used by the command.</param> ///<param name="parameter">Data used by the command.</param>
public void Execute(T parameter) public void Execute(T parameter)
{ {
_executeMethod(parameter); try
{
_executeMethod(parameter);
}
catch (Exception ex)
{
if (!ExceptionHandler.CanHandle(ex))
throw;
ExceptionHandler.Handle(ex, parameter);
}
} }
///<summary> ///<summary>
...@@ -91,7 +102,19 @@ namespace Prism.Commands ...@@ -91,7 +102,19 @@ namespace Prism.Commands
///</returns> ///</returns>
public bool CanExecute(T parameter) public bool CanExecute(T parameter)
{ {
return _canExecuteMethod(parameter); try
{
return _canExecuteMethod(parameter);
}
catch (Exception ex)
{
if (!ExceptionHandler.CanHandle(ex))
throw;
ExceptionHandler.Handle(ex, parameter);
return false;
}
} }
/// <summary> /// <summary>
...@@ -100,7 +123,19 @@ namespace Prism.Commands ...@@ -100,7 +123,19 @@ namespace Prism.Commands
/// <param name="parameter">Command Parameter</param> /// <param name="parameter">Command Parameter</param>
protected override void Execute(object parameter) protected override void Execute(object parameter)
{ {
Execute((T)parameter); try
{
// Note: We don't call Execute because we would potentially invoke the Try/Catch twice.
// It is also needed here incase (T)parameter throws the exception
_executeMethod((T)parameter);
}
catch (Exception ex)
{
if (!ExceptionHandler.CanHandle(ex))
throw;
ExceptionHandler.Handle(ex, parameter);
}
} }
/// <summary> /// <summary>
...@@ -110,7 +145,21 @@ namespace Prism.Commands ...@@ -110,7 +145,21 @@ namespace Prism.Commands
/// <returns><see langword="true"/> if the Command Can Execute, otherwise <see langword="false" /></returns> /// <returns><see langword="true"/> if the Command Can Execute, otherwise <see langword="false" /></returns>
protected override bool CanExecute(object parameter) protected override bool CanExecute(object parameter)
{ {
return CanExecute((T)parameter); try
{
// Note: We don't call Execute because we would potentially invoke the Try/Catch twice.
// It is also needed here incase (T)parameter throws the exception
return CanExecute((T)parameter);
}
catch (Exception ex)
{
if (!ExceptionHandler.CanHandle(ex))
throw;
ExceptionHandler.Handle(ex, parameter);
return false;
}
} }
/// <summary> /// <summary>
...@@ -137,5 +186,101 @@ namespace Prism.Commands ...@@ -137,5 +186,101 @@ namespace Prism.Commands
ObservesPropertyInternal(canExecuteExpression); ObservesPropertyInternal(canExecuteExpression);
return this; return this;
} }
/// <summary>
/// Registers an callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand<T> Catch(Action<Exception> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
return this;
}
/// <summary>
/// Registers an callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand<T> Catch(Action<Exception, object> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
return this;
}
/// <summary>
/// Registers an callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <typeparam name="TException">The Exception Type</typeparam>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand<T> Catch<TException>(Action<TException> @catch)
where TException : Exception
{
ExceptionHandler.Register<TException>(@catch);
return this;
}
/// <summary>
/// Registers an callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <typeparam name="TException">The Exception Type</typeparam>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand<T> Catch<TException>(Action<TException, object> @catch)
where TException : Exception
{
ExceptionHandler.Register<TException>(@catch);
return this;
}
/// <summary>
/// Registers an async callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand<T> Catch(Func<Exception, Task> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
return this;
}
/// <summary>
/// Registers an async callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand<T> Catch(Func<Exception, object, Task> @catch)
{
ExceptionHandler.Register<Exception>(@catch);
return this;
}
/// <summary>
/// Registers an async callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <typeparam name="TException">The Exception Type</typeparam>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand<T> Catch<TException>(Func<TException, Task> @catch)
where TException : Exception
{
ExceptionHandler.Register<TException>(@catch);
return this;
}
/// <summary>
/// Registers an async callback if an exception is encountered while executing the <see cref="DelegateCommand"/>
/// </summary>
/// <typeparam name="TException">The Exception Type</typeparam>
/// <param name="catch">The Callback</param>
/// <returns>The current instance of <see cref="DelegateCommand"/></returns>
public DelegateCommand<T> Catch<TException>(Func<TException, object, Task> @catch)
where TException : Exception
{
ExceptionHandler.Register<TException>(@catch);
return this;
}
} }
} }
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Prism.Common;
#nullable enable
/// <summary>
/// Provides a wrapper for managing multicast delegates for handling specific errors
/// </summary>
public struct MulticastExceptionHandler
{
private readonly Dictionary<Type, MulticastDelegate> _handlers;
/// <summary>
/// Initializes a new MulticastExceptionHandler
/// </summary>
public MulticastExceptionHandler()
{
_handlers = new Dictionary<Type, MulticastDelegate>();
}
public void Register<TException>(MulticastDelegate callback)
where TException : Exception
{
_handlers.Add(typeof(TException), callback);
}
/// <summary>
/// Determines if there is a callback registered to handle the specified exception
/// </summary>
/// <param name="exception">An <see cref="Exception"/> to handle or rethrow</param>
/// <returns><c>True</c> if a Callback has been registered for the given type of <see cref="Exception"/>.</returns>
public bool CanHandle(Exception exception) =>
GetDelegate(exception.GetType()) is not null;
/// <summary>
/// Handles a specified
/// </summary>
/// <param name="exception"></param>
/// <param name="parameter"></param>
public async void Handle(Exception exception, object? parameter = null) =>
await HandleAsync(exception, parameter);
public async Task HandleAsync(Exception exception, object? parameter = null)
{
var multicastDelegate = GetDelegate(exception.GetType());
if (multicastDelegate is null)
return;
// Get Invoke() method of the delegate
var invokeMethod = multicastDelegate.GetType().GetMethod("Invoke");
if (invokeMethod == null)
throw new InvalidOperationException($"Could not find Invoke() method for delegate of type {multicastDelegate.GetType().Name}");
var parameters = invokeMethod.GetParameters();
var arguments = parameters.Length switch
{
0 => Array.Empty<object?>(),
1 => typeof(Exception).IsAssignableFrom(parameters[0].ParameterType) ? new object?[] { exception } : new object?[] { parameter },
2 => typeof(Exception).IsAssignableFrom(parameters[0].ParameterType) ? new object?[] { exception, parameter } : new object?[] { parameter, exception },
_ => throw new InvalidOperationException($"Handler of type {multicastDelegate.GetType().Name} is not supported", exception)
};
// Invoke the delegate
var result = invokeMethod.Invoke(multicastDelegate, arguments);
// If the handler is async (returns a Task), then we await the task
if (result is Task task)
{
await task;
}
#if NET6_0_OR_GREATER
else if (result is ValueTask valueTask)
{
await valueTask;
}
#endif
}
private MulticastDelegate? GetDelegate(Type type)
{
if (_handlers.ContainsKey(type))
return _handlers[type];
else if (type.BaseType is not null)
return GetDelegate(type.BaseType);
return null;
}
}
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net47;net6.0</TargetFrameworks> <TargetFrameworks>netstandard2.0;net461;net47;net6.0</TargetFrameworks>
<AssemblyName>Prism</AssemblyName> <AssemblyName>Prism</AssemblyName>
<PackageId>Prism.Core</PackageId> <PackageId>Prism.Core</PackageId>
<RootNamespace>Prism</RootNamespace> <RootNamespace>Prism</RootNamespace>
...@@ -14,6 +14,11 @@ ...@@ -14,6 +14,11 @@
<Compile Update="Properties\Resources.Designer.cs" DesignTime="True" AutoGen="True" DependentUpon="Resources.resx" /> <Compile Update="Properties\Resources.Designer.cs" DesignTime="True" AutoGen="True" DependentUpon="Resources.resx" />
<EmbeddedResource Update="Properties\Resources.resx" Generator="ResXFileCodeGenerator" LastGenOutput="Resources.Designer.cs" /> <EmbeddedResource Update="Properties\Resources.resx" Generator="ResXFileCodeGenerator" LastGenOutput="Resources.Designer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" $(TargetFramework) == 'net461' ">
<PackageReference Include="System.ValueTuple" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Prism.Events\Prism.Events.csproj" /> <ProjectReference Include="..\Prism.Events\Prism.Events.csproj" />
</ItemGroup> </ItemGroup>
......
using System; using System;
using System.Linq; using System.Linq;
#if NETSTANDARD || NET47 #if NETSTANDARD || NET461_OR_GREATER
namespace System.Runtime.CompilerServices; namespace System.Runtime.CompilerServices;
internal static class IsExternalInit internal static class IsExternalInit
......
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net47;net6.0</TargetFrameworks> <TargetFrameworks>netstandard2.0;net461;net47;net6.0</TargetFrameworks>
<!-- Summary is not actually supported at this time. Including the summary for future support. --> <!-- Summary is not actually supported at this time. Including the summary for future support. -->
<!--<Summary>Prism provides an implementation of a collection of design patterns that are helpful in writing well structured and maintainable applications.</Summary>--> <!--<Summary>Prism provides an implementation of a collection of design patterns that are helpful in writing well structured and maintainable applications.</Summary>-->
<Description>Prism.Events is a library that facilitates communication between loosely coupled components in an application. It provides an event aggregator service that allows publishers and subscribers to interact through events without direct references. With multicast publish/subscribe functionality, multiple publishers can raise the same event, and multiple subscribers can listen to it, enabling flexible and efficient communication.</Description> <Description>Prism.Events is a library that facilitates communication between loosely coupled components in an application. It provides an event aggregator service that allows publishers and subscribers to interact through events without direct references. With multicast publish/subscribe functionality, multiple publishers can raise the same event, and multiple subscribers can listen to it, enabling flexible and efficient communication.</Description>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net47;net6.0-windows</TargetFrameworks> <TargetFrameworks>net461;net47;net6.0-windows</TargetFrameworks>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<RootNamespace>Prism.DryIoc</RootNamespace> <RootNamespace>Prism.DryIoc</RootNamespace>
<PackageId>Prism.DryIoc</PackageId> <PackageId>Prism.DryIoc</PackageId>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net47;net6.0-windows</TargetFrameworks> <TargetFrameworks>net461;net47;net6.0-windows</TargetFrameworks>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<RootNamespace>Prism.Unity</RootNamespace> <RootNamespace>Prism.Unity</RootNamespace>
<PackageId>Prism.Unity</PackageId> <PackageId>Prism.Unity</PackageId>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<PropertyGroup> <PropertyGroup>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Prism</RootNamespace> <RootNamespace>Prism</RootNamespace>
<TargetFrameworks>net47;net6.0-windows</TargetFrameworks> <TargetFrameworks>net461;net47;net6.0-windows</TargetFrameworks>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<!--<Summary>Prism libraries related to user interface composition, regions, and modularity for WPF.</Summary>--> <!--<Summary>Prism libraries related to user interface composition, regions, and modularity for WPF.</Summary>-->
<Description>Prism is a fully open source version of the Prism guidance originally produced by Microsoft Patterns &amp; Practices. Prism provides an implementation of a collection of design patterns that are helpful in writing well structured, maintainable, and testable XAML applications, including MVVM, dependency injection, commanding, event aggregation, and more. Prism's core functionality is a shared library targeting the .NET Framework and .NET Standard. Features that need to be platform specific are implemented in the respective libraries for the target platform (WPF, Uno Platform, and Xamarin Forms). <Description>Prism is a fully open source version of the Prism guidance originally produced by Microsoft Patterns &amp; Practices. Prism provides an implementation of a collection of design patterns that are helpful in writing well structured, maintainable, and testable XAML applications, including MVVM, dependency injection, commanding, event aggregation, and more. Prism's core functionality is a shared library targeting the .NET Framework and .NET Standard. Features that need to be platform specific are implemented in the respective libraries for the target platform (WPF, Uno Platform, and Xamarin Forms).
......
using System; using System;
using System.IO;
using System.Windows.Input; using System.Windows.Input;
using Prism.Commands; using Prism.Commands;
using Prism.Mvvm; using Prism.Mvvm;
...@@ -819,6 +820,148 @@ namespace Prism.Tests.Commands ...@@ -819,6 +820,148 @@ namespace Prism.Tests.Commands
Assert.Equal(0, complexProp.GetPropertyChangedSubscribledLenght()); Assert.Equal(0, complexProp.GetPropertyChangedSubscribledLenght());
} }
[Fact]
public void DelegateCommand_Catch_CatchesException()
{
var caught = false;
var command = new DelegateCommand(() => { throw new Exception(); })
.Catch<Exception>(ex => caught = true);
command.Execute();
Assert.True(caught);
}
[Fact]
public void DelegateCommandT_Catch_CatchesException()
{
var caught = false;
var command = new DelegateCommand<MyClass>(x => { throw new Exception(); })
.Catch<Exception>(ex => caught = true);
command.Execute(new MyClass());
Assert.True(caught);
}
[Fact]
public void DelegateCommand_Catch_CatchesExceptionWithParameter()
{
var caught = false;
object parameter = new object();
var command = new DelegateCommand(() => { throw new Exception(); })
.Catch<Exception>((ex, value) =>
{
caught = true;
parameter = value;
});
command.Execute();
Assert.True(caught);
Assert.Null(parameter);
}
[Fact]
public void DelegateCommandT_Catch_InvalidCastException()
{
var caught = false;
ICommand command = new DelegateCommand<MyClass>(x => { })
.Catch<Exception>(ex =>
{
Assert.IsType<InvalidCastException>(ex);
caught = true;
});
command.Execute("Test");
Assert.True(caught);
}
[Fact]
public void DelegateCommandT_Catch_CatchesExceptionWithParameter()
{
var caught = false;
var parameter = new object();
var command = new DelegateCommand<MyClass>(x => { throw new Exception(); })
.Catch<Exception>((ex, value) =>
{
caught = true;
parameter = value;
});
command.Execute(new MyClass { Value = 3 });
Assert.True(caught);
Assert.IsType<MyClass>(parameter);
Assert.Equal(3, ((MyClass)parameter).Value);
}
[Fact]
public void DelegateCommand_Catch_CatchesSpecificException()
{
var caught = false;
var command = new DelegateCommand(() => { throw new FileNotFoundException(); })
.Catch<FileNotFoundException>(ex => caught = true);
command.Execute();
Assert.True(caught);
}
[Fact]
public void DelegateCommandT_Catch_CatchesSpecificException()
{
var caught = false;
var command = new DelegateCommand<MyClass>(x => { throw new FileNotFoundException(); })
.Catch<FileNotFoundException>(ex => caught = true);
command.Execute(new MyClass());
Assert.True(caught);
}
[Fact]
public void DelegateCommand_Catch_CatchesChildException()
{
var caught = false;
var command = new DelegateCommand(() => { throw new FileNotFoundException(); })
.Catch<IOException>(ex => caught = true);
command.Execute();
Assert.True(caught);
}
[Fact]
public void DelegateCommandT_Catch_CatchesChildException()
{
var caught = false;
var command = new DelegateCommand<MyClass>(x => { throw new FileNotFoundException(); })
.Catch<IOException>(ex => caught = true);
command.Execute(new MyClass());
Assert.True(caught);
}
[Fact]
public void DelegateCommand_Throws_CatchesException()
{
var caught = false;
var command = new DelegateCommand(() => { throw new Exception(); })
.Catch<FileNotFoundException>(ex => caught = true);
var ex = Record.Exception(() => command.Execute());
Assert.False(caught);
Assert.NotNull(ex);
Assert.IsType<Exception>(ex);
}
[Fact]
public void DelegateCommandT_Throws_CatchesException()
{
var caught = false;
var command = new DelegateCommand<MyClass>(x => { throw new Exception(); })
.Catch<FileNotFoundException>(ex => caught = true);
var ex = Record.Exception(() => command.Execute(new MyClass()));
Assert.False(caught);
Assert.NotNull(ex);
Assert.IsType<Exception>(ex);
}
public class ComplexType : TestPurposeBindableBase public class ComplexType : TestPurposeBindableBase
{ {
private int _intProperty; private int _intProperty;
...@@ -891,5 +1034,6 @@ namespace Prism.Tests.Commands ...@@ -891,5 +1034,6 @@ namespace Prism.Tests.Commands
internal class MyClass internal class MyClass
{ {
public int Value { get; set; }
} }
} }
using System;
using System.IO;
using System.Threading.Tasks;
using Prism.Common;
using Xunit;
namespace Prism.Core.Tests.Common;
#nullable enable
public class MulticastExceptionHandlerFixture
{
[Fact]
public void CanHandleGenericException()
{
var handler = new MulticastExceptionHandler();
void Callback(Exception exception) { }
handler.Register<Exception>(Callback);
Assert.True(handler.CanHandle(new Exception()));
}
[Fact]
public void DidHandleGenericException()
{
var handler = new MulticastExceptionHandler();
handler.Register<Exception>(Callback);
bool handled = false;
void Callback(Exception exception)
{
handled = true;
}
handler.Handle(new Exception());
Assert.True(handled);
}
[Fact]
public void CanHandleGenericExceptionAsync()
{
var handler = new MulticastExceptionHandler();
handler.Register<Exception>(Callback);
Task Callback(Exception exception)
{
return Task.CompletedTask;
}
Assert.True(handler.CanHandle(new Exception()));
}
[Fact]
public void CanHandleUsingBaseExceptionType()
{
var handler = new MulticastExceptionHandler();
handler.Register<IOException>(Callback);
Task Callback(IOException exception)
{
return Task.CompletedTask;
}
Assert.True(handler.CanHandle(new FileNotFoundException()));
}
[Fact]
public void DidHandleGenericExceptionAsync()
{
var handler = new MulticastExceptionHandler();
handler.Register<Exception>(Callback);
bool handled = false;
Task Callback(Exception exception)
{
handled = true;
return Task.CompletedTask;
}
handler.Handle(new Exception());
Assert.True(handled);
}
[Fact]
public void CanHandleSpecificException()
{
var handler = new MulticastExceptionHandler();
handler.Register<FileNotFoundException>(Callback);
void Callback(FileNotFoundException exception) { }
Assert.True(handler.CanHandle(new FileNotFoundException()));
}
[Fact]
public async Task DidHandleSpecificException()
{
var handler = new MulticastExceptionHandler();
handler.Register<FileNotFoundException>(Callback);
bool handled = false;
void Callback(FileNotFoundException exception)
{
handled = true;
}
await handler.HandleAsync(new FileNotFoundException());
Assert.True(handled);
}
[Fact]
public async Task DidHandleSpecificExceptionWithParameter()
{
var handler = new MulticastExceptionHandler();
var expected = Guid.NewGuid();
bool handled = false;
handler.Register<FileNotFoundException>(Callback);
void Callback(FileNotFoundException fnfe, object? parameter)
{
Assert.Equal(expected, parameter);
handled = true;
}
await handler.HandleAsync(new FileNotFoundException(), expected);
Assert.True(handled);
}
[Fact]
public void CanHandleSpecificExceptionAsync()
{
var handler = new MulticastExceptionHandler();
handler.Register<FileNotFoundException>(Callback);
Task Callback(FileNotFoundException exception)
{
return Task.CompletedTask;
}
Assert.True(handler.CanHandle(new FileNotFoundException()));
}
[Fact]
public async Task DidHandleSpecificExceptionAsync()
{
var handler = new MulticastExceptionHandler();
handler.Register<FileNotFoundException>(Callback);
bool handled = false;
Task Callback(FileNotFoundException exception)
{
handled = true;
return Task.CompletedTask;
}
await handler.HandleAsync(new FileNotFoundException());
Assert.True(handled);
}
[Fact]
public async Task DidHandleSpecificExceptionAsyncWithParameter()
{
var handler = new MulticastExceptionHandler();
bool handled = false;
var expected = Guid.NewGuid();
handler.Register<FileNotFoundException>(Callback);
Task Callback(FileNotFoundException fnfe, object? parameter)
{
Assert.Equal(expected, parameter);
handled = true;
return Task.CompletedTask;
}
await handler.HandleAsync(new FileNotFoundException(), expected);
Assert.True(handled);
}
[Fact]
public async Task DidHandleSpecificExceptionAsyncWithParameterFirst()
{
var handler = new MulticastExceptionHandler();
bool handled = false;
var expected = Guid.NewGuid();
handler.Register<FileNotFoundException>(Callback);
Task Callback(object? parameter, FileNotFoundException fnfe)
{
Assert.Equal(expected, parameter);
handled = true;
return Task.CompletedTask;
}
await handler.HandleAsync(new FileNotFoundException(), expected);
Assert.True(handled);
}
}