From 12ac4a0703d2aa3dab6b0d02ad078669ca7800df Mon Sep 17 00:00:00 2001 From: Stefan Ossendorf Date: Wed, 23 Oct 2024 20:50:00 +0200 Subject: [PATCH] Csla.Server namespace nullable aware --- Source/Csla/ApplicationContext.cs | 5 +- Source/Csla/Core/IContextManager.cs | 2 +- .../Csla/DataPortalClient/DataPortalProxy.cs | 19 +- Source/Csla/DataPortalEventArgs.cs | 10 +- Source/Csla/DataPortalException.cs | 4 +- Source/Csla/DataPortalT.cs | 119 +++--- Source/Csla/Properties/Resources.Designer.cs | 9 + Source/Csla/Properties/Resources.resx | 4 + Source/Csla/Reflection/LateBoundObject.cs | 5 +- .../Reflection/ServiceProviderMethodCaller.cs | 29 +- .../ServiceProviderMethodCallerExtensions.cs | 49 +++ Source/Csla/Rules/BusinessRules.cs | 1 + .../Serialization/Mobile/MobileFormatter.cs | 2 +- Source/Csla/Server/ActiveAuthorizer.cs | 12 +- Source/Csla/Server/AuthorizeRequest.cs | 8 +- Source/Csla/Server/ChildDataPortal.cs | 203 +++++----- Source/Csla/Server/ChildDataPortalFactory.cs | 11 +- Source/Csla/Server/Dashboard/Activity.cs | 6 +- Source/Csla/Server/Dashboard/Dashboard.cs | 14 +- Source/Csla/Server/Dashboard/IDashboard.cs | 2 + Source/Csla/Server/DataPortal.cs | 356 +++++++----------- Source/Csla/Server/DataPortalBroker.cs | 72 ++-- Source/Csla/Server/DataPortalContext.cs | 36 +- Source/Csla/Server/DataPortalException.cs | 17 +- .../Csla/Server/DataPortalExceptionHandler.cs | 45 ++- Source/Csla/Server/DataPortalMethodCache.cs | 70 ++-- Source/Csla/Server/DataPortalMethodInfo.cs | 27 +- Source/Csla/Server/DataPortalResult.cs | 4 +- Source/Csla/Server/DataPortalSelector.cs | 58 +-- Source/Csla/Server/DataPortalTarget.cs | 8 +- .../Csla/Server/DefaultDataPortalActivator.cs | 23 +- .../Csla/Server/DefaultExceptionInspector.cs | 15 +- Source/Csla/Server/EmptyCriteria.cs | 2 +- Source/Csla/Server/FactoryDataPortal.cs | 113 +++--- .../Csla/Server/GenericBusinessException.cs | 6 +- .../DataPortalChannel/CriteriaRequest.cs | 57 ++- .../DataPortalChannel/DataPortalErrorInfo.cs | 19 +- .../DataPortalChannel/DataPortalResponse.cs | 23 +- .../Hosts/DataPortalChannel/UpdateRequest.cs | 39 +- Source/Csla/Server/IAuthorizeDataPortal.cs | 1 + Source/Csla/Server/IDataPortalActivator.cs | 4 + .../Server/IDataPortalExceptionInspector.cs | 24 +- Source/Csla/Server/IDataPortalServer.cs | 12 +- Source/Csla/Server/IInterceptDataPortal.cs | 60 ++- Source/Csla/Server/IObjectFactoryLoader.cs | 2 + .../Server/Interceptors/InterceptorManager.cs | 23 +- .../ServerSide/RevalidatingInterceptor.cs | 34 +- Source/Csla/Server/MobileFactoryAttribute.cs | 52 +-- Source/Csla/Server/NullAuthorizer.cs | 11 +- Source/Csla/Server/ObjectFactory.cs | 72 +++- Source/Csla/Server/ObjectFactoryAttribute.cs | 91 +++-- Source/Csla/Server/ObjectFactoryLoader.cs | 23 +- .../Server/SanitizingExceptionInspector.cs | 17 +- Source/Csla/Server/ServerException.cs | 14 +- .../Server/ServicedDataPortalReadCommitted.cs | 13 +- .../ServicedDataPortalReadUncommitted.cs | 14 +- .../ServicedDataPortalRepeatableRead.cs | 14 +- .../Server/ServicedDataPortalSerializable.cs | 40 +- Source/Csla/Server/SimpleDataPortal.cs | 144 +++---- Source/Csla/Server/TransactionalDataPortal.cs | 33 +- docs/Upgrading to CSLA 9.md | 9 +- 61 files changed, 1236 insertions(+), 975 deletions(-) create mode 100644 Source/Csla/Reflection/ServiceProviderMethodCallerExtensions.cs diff --git a/Source/Csla/ApplicationContext.cs b/Source/Csla/ApplicationContext.cs index 6045527b45..3332295abb 100644 --- a/Source/Csla/ApplicationContext.cs +++ b/Source/Csla/ApplicationContext.cs @@ -124,7 +124,7 @@ public IContextDictionary ClientContext { lock (_syncContext) { - IContextDictionary ctx = ContextManager.GetClientContext(ExecutionLocation); + IContextDictionary? ctx = ContextManager.GetClientContext(ExecutionLocation); if (ctx == null) { ctx = new ContextDictionary(); @@ -135,7 +135,7 @@ public IContextDictionary ClientContext } } - internal void SetContext(IContextDictionary clientContext) + internal void SetContext(IContextDictionary? clientContext) { lock (_syncContext) ContextManager.SetClientContext(clientContext, ExecutionLocation); @@ -449,5 +449,6 @@ internal object CreateGenericInstance(Type type, params Type[] paramTypes) return CreateInstance(gt); } + internal void SetUnauthenticatedUser() => ContextManager.SetUser(null); } } diff --git a/Source/Csla/Core/IContextManager.cs b/Source/Csla/Core/IContextManager.cs index af12453142..dee392b72b 100644 --- a/Source/Csla/Core/IContextManager.cs +++ b/Source/Csla/Core/IContextManager.cs @@ -38,7 +38,7 @@ public interface IContextManager /// Sets the current principal. /// /// Principal object. - void SetUser(IPrincipal principal); + void SetUser(IPrincipal? principal); /// /// Gets the local context. /// diff --git a/Source/Csla/DataPortalClient/DataPortalProxy.cs b/Source/Csla/DataPortalClient/DataPortalProxy.cs index f9fc8a97ab..75739d0607 100644 --- a/Source/Csla/DataPortalClient/DataPortalProxy.cs +++ b/Source/Csla/DataPortalClient/DataPortalProxy.cs @@ -338,15 +338,15 @@ internal bool ExecutionIsNotOnLogicalOrPhysicalServer private CriteriaRequest GetBaseCriteriaRequest() { - var result = ApplicationContext.CreateInstanceDI(); var securityOptions = ApplicationContext.GetRequiredService(); - result.CriteriaData = null; - result.ClientContext = ApplicationContext.GetRequiredService().Serialize(ApplicationContext.ClientContext); - result.Principal = ApplicationContext.GetRequiredService() - .Serialize(securityOptions.FlowSecurityPrincipalFromClient ? ApplicationContext.User : null); - result.ClientCulture = System.Globalization.CultureInfo.CurrentCulture.Name; - result.ClientUICulture = System.Globalization.CultureInfo.CurrentUICulture.Name; - return result; + + var serializer = ApplicationContext.GetRequiredService(); + var clientContext = serializer.Serialize(ApplicationContext.ClientContext); + var principal = serializer.Serialize(securityOptions.FlowSecurityPrincipalFromClient ? ApplicationContext.User : null); + var clientCulture = System.Globalization.CultureInfo.CurrentCulture.Name; + var clientUICulture = System.Globalization.CultureInfo.CurrentUICulture.Name; + + return ApplicationContext.CreateInstanceDI(principal, clientContext, clientCulture, clientUICulture); } private UpdateRequest GetBaseUpdateCriteriaRequest() @@ -355,8 +355,7 @@ private UpdateRequest GetBaseUpdateCriteriaRequest() var securityOptions = ApplicationContext.GetRequiredService(); result.ObjectData = null; result.ClientContext = ApplicationContext.GetRequiredService().Serialize(ApplicationContext.ClientContext); - result.Principal = ApplicationContext.GetRequiredService() - .Serialize(securityOptions.FlowSecurityPrincipalFromClient ? ApplicationContext.User : null); + result.Principal = ApplicationContext.GetRequiredService().Serialize(securityOptions.FlowSecurityPrincipalFromClient ? ApplicationContext.User : null); result.ClientCulture = Thread.CurrentThread.CurrentCulture.Name; result.ClientUICulture = Thread.CurrentThread.CurrentUICulture.Name; return result; diff --git a/Source/Csla/DataPortalEventArgs.cs b/Source/Csla/DataPortalEventArgs.cs index 08a4bfc429..53fd883d7c 100644 --- a/Source/Csla/DataPortalEventArgs.cs +++ b/Source/Csla/DataPortalEventArgs.cs @@ -18,7 +18,7 @@ public class DataPortalEventArgs : EventArgs /// The DataPortalContext object passed to the /// server-side DataPortal. /// - public Server.DataPortalContext DataPortalContext { get; } + public Server.DataPortalContext? DataPortalContext { get; } /// /// Gets the requested data portal operation. @@ -34,7 +34,7 @@ public class DataPortalEventArgs : EventArgs /// exception occurred. Exceptions are returned only as part /// of a data portal complete event or method. /// - public Exception Exception { get; } + public Exception? Exception { get; } /// /// Gets the object type being processed by the @@ -46,7 +46,7 @@ public class DataPortalEventArgs : EventArgs /// Gets the criteria object or business object /// being processed by the data portal. /// - public object Object { get; } + public object? Object { get; } /// /// Creates an instance of the type. @@ -63,7 +63,7 @@ public class DataPortalEventArgs : EventArgs /// /// Data portal operation being performed. /// - public DataPortalEventArgs(Server.DataPortalContext dataPortalContext, Type objectType, object obj, DataPortalOperations operation) + public DataPortalEventArgs(Server.DataPortalContext? dataPortalContext, Type objectType, object? obj, DataPortalOperations operation) { DataPortalContext = dataPortalContext; Operation = operation; @@ -89,7 +89,7 @@ public DataPortalEventArgs(Server.DataPortalContext dataPortalContext, Type obje /// /// Exception encountered during processing. /// - public DataPortalEventArgs(Server.DataPortalContext dataPortalContext, Type objectType, object obj, DataPortalOperations operation, Exception exception) + public DataPortalEventArgs(Server.DataPortalContext dataPortalContext, Type objectType, object? obj, DataPortalOperations operation, Exception exception) : this(dataPortalContext, objectType, obj, operation) { Exception = exception; diff --git a/Source/Csla/DataPortalException.cs b/Source/Csla/DataPortalException.cs index 7b10cf9bbb..aaf85018b3 100644 --- a/Source/Csla/DataPortalException.cs +++ b/Source/Csla/DataPortalException.cs @@ -38,7 +38,7 @@ public DataPortalException(string message, object businessObject) /// Inner exception. /// The business object /// as it was at the time of the exception. - public DataPortalException(string message, Exception ex, object businessObject) + public DataPortalException(string message, Exception ex, object? businessObject) : base(message, ex) { _innerStackTrace = ex.StackTrace; @@ -132,7 +132,7 @@ public DataPortalErrorInfo BusinessErrorInfo /// state may have been altered by the server and /// may no longer reflect data in the database. /// - public object BusinessObject { get; } + public object? BusinessObject { get; } private Exception _businessException; diff --git a/Source/Csla/DataPortalT.cs b/Source/Csla/DataPortalT.cs index 0e51e792ee..7018a2f7f1 100644 --- a/Source/Csla/DataPortalT.cs +++ b/Source/Csla/DataPortalT.cs @@ -6,10 +6,12 @@ // Client side data portal used for making asynchronous //----------------------------------------------------------------------- +using System.Diagnostics.CodeAnalysis; using System.Globalization; using Csla.Configuration; using Csla.DataPortalClient; using Csla.Properties; +using Csla.Reflection; namespace Csla { @@ -90,7 +92,7 @@ private Reflection.ServiceProviderMethodCaller ServiceProviderMethodCaller get { if (serviceProviderMethodCaller == null) - serviceProviderMethodCaller = (Reflection.ServiceProviderMethodCaller)_applicationContext.CreateInstanceDI(typeof(Reflection.ServiceProviderMethodCaller)); + serviceProviderMethodCaller = _applicationContext.CreateInstanceDI< ServiceProviderMethodCaller>(); return serviceProviderMethodCaller; } } @@ -102,19 +104,12 @@ private async Task DoCreateAsync(Type objectType, object criteria, bool try { if (!await Csla.Rules.BusinessRules.HasPermissionAsync(_applicationContext, Rules.AuthorizationActions.CreateObject, objectType, Server.DataPortal.GetCriteriaArray(criteria), ct)) - throw new Csla.Security.SecurityException(string.Format( - Resources.UserNotAuthorizedException, - "create", - objectType.Name)); - Reflection.ServiceProviderMethodInfo method; - if (criteria is Server.EmptyCriteria) - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null, false); - else - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, Server.DataPortal.GetCriteriaArray(criteria), false); + throw new Csla.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "create", objectType.Name)); + + _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); var proxy = GetDataPortalProxy(method); - dpContext = - new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); + dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); try { @@ -201,20 +196,17 @@ private async Task DoFetchAsync(Type objectType, object criteria, bool i Server.DataPortalResult result = null; Server.DataPortalContext dpContext = null; - Reflection.ServiceProviderMethodInfo method = null; + Reflection.ServiceProviderMethodInfo? method = null; try { if (!await Csla.Rules.BusinessRules.HasPermissionAsync(_applicationContext, Rules.AuthorizationActions.GetObject, objectType, Server.DataPortal.GetCriteriaArray(criteria), ct)) - throw new Csla.Security.SecurityException(string.Format( - Resources.UserNotAuthorizedException, - "get", - objectType.Name)); - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, Server.DataPortal.GetCriteriaArray(criteria), false); + throw new Csla.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "get", objectType.Name)); + + _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out method); var proxy = GetDataPortalProxy(method); - dpContext = - new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); + dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); try { @@ -244,45 +236,39 @@ private async Task DoFetchAsync(Type objectType, object criteria, bool i private async Task DoExecuteAsync(Type objectType, object criteria, bool isSync, CancellationToken ct = default) { - Server.DataPortalResult result = null; - Server.DataPortalContext dpContext = null; - Reflection.ServiceProviderMethodInfo method = null; - try - { - if (!await Csla.Rules.BusinessRules.HasPermissionAsync(_applicationContext, Rules.AuthorizationActions.EditObject, objectType, Server.DataPortal.GetCriteriaArray(criteria), ct)) - throw new Csla.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, - "execute", - objectType.Name)); - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, Server.DataPortal.GetCriteriaArray(criteria), false); + Server.DataPortalResult? result = null; + Server.DataPortalContext? dpContext = null; + Reflection.ServiceProviderMethodInfo? method = null; - var proxy = GetDataPortalProxy(method); + if (!await Csla.Rules.BusinessRules.HasPermissionAsync(_applicationContext, Rules.AuthorizationActions.EditObject, objectType, Server.DataPortal.GetCriteriaArray(criteria), ct)) + throw new Csla.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, + "execute", + objectType.Name)); - dpContext = - new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); + _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out method); + var proxy = GetDataPortalProxy(method); - try - { - result = await Cache.GetDataPortalResultAsync(objectType, criteria, DataPortalOperations.Execute, - async () => await proxy.Fetch(objectType, criteria, dpContext, isSync)); - } - catch (AggregateException ex) - { - if (ex.InnerExceptions.Count > 0) - { - if (ex.InnerExceptions[0] is Server.DataPortalException dpe) - HandleDataPortalException("Execute", dpe); - } - throw new DataPortalException($"DataPortal.Execute {Resources.Failed}", ex, null); - } - catch (Server.DataPortalException ex) + dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); + + try + { + result = await Cache.GetDataPortalResultAsync(objectType, criteria, DataPortalOperations.Execute, + async () => await proxy.Fetch(objectType, criteria, dpContext, isSync)); + } + catch (AggregateException ex) + { + if (ex.InnerExceptions.Count > 0) { - HandleDataPortalException("Execute", ex); + if (ex.InnerExceptions[0] is Server.DataPortalException dpe) + HandleDataPortalException("Execute", dpe); } + throw new DataPortalException($"DataPortal.Execute {Resources.Failed}", ex, null); } - catch + catch (Server.DataPortalException ex) { - throw; + HandleDataPortalException("Execute", ex); } + return result.ReturnObject; } @@ -347,7 +333,7 @@ internal async Task DoUpdateAsync(T obj, bool isSync, CancellationToken ct = var factoryInfo = Server.ObjectFactoryAttribute.GetObjectFactoryAttribute(objectType); if (factoryInfo != null) { - Server.DataPortalMethodInfo method = null; + Server.DataPortalMethodInfo? method = null; var factoryLoader = _applicationContext.CurrentServiceProvider.GetService(typeof(Server.IObjectFactoryLoader)) as Server.IObjectFactoryLoader; var factoryType = factoryLoader?.GetFactoryType(factoryInfo.FactoryTypeName); @@ -405,13 +391,13 @@ internal async Task DoUpdateAsync(T obj, bool isSync, CancellationToken ct = method = Server.DataPortalMethodCache.GetMethodInfo(factoryType, factoryInfo.UpdateMethodName, [obj]); } } - if (method == null) - method = new Server.DataPortalMethodInfo(); - proxy = GetDataPortalProxy(method.RunLocal); + + var runLocal = method?.RunLocal ?? false; + proxy = GetDataPortalProxy(runLocal); } else { - Reflection.ServiceProviderMethodInfo method; + Reflection.ServiceProviderMethodInfo? method; var criteria = Server.DataPortal.GetCriteriaArray(Server.EmptyCriteria.Instance); if (obj is Core.ICommandObject) { @@ -420,7 +406,7 @@ internal async Task DoUpdateAsync(T obj, bool isSync, CancellationToken ct = throw new Csla.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "execute", objectType.Name)); - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, criteria, false); + _ = ServiceProviderMethodCaller.TryFindDataPortalMethod(objectType, criteria, out method); } else { @@ -432,7 +418,7 @@ internal async Task DoUpdateAsync(T obj, bool isSync, CancellationToken ct = throw new Csla.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "delete", objectType.Name)); - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, criteria, false); + _ = ServiceProviderMethodCaller.TryFindDataPortalMethod(objectType, criteria, out method); } else if (bbase.IsNew) { @@ -440,7 +426,7 @@ internal async Task DoUpdateAsync(T obj, bool isSync, CancellationToken ct = throw new Csla.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "create", objectType.Name)); - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, criteria, false); + _ = ServiceProviderMethodCaller.TryFindDataPortalMethod(objectType, criteria, out method); } else { @@ -448,12 +434,12 @@ internal async Task DoUpdateAsync(T obj, bool isSync, CancellationToken ct = throw new Csla.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "save", objectType.Name)); - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, criteria, false); + _ = ServiceProviderMethodCaller.TryFindDataPortalMethod(objectType, criteria, out method); } } else { - method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, criteria, false); + _ = ServiceProviderMethodCaller.TryFindDataPortalMethod(objectType, criteria, out method); } } proxy = GetDataPortalProxy(method); @@ -500,15 +486,16 @@ private void HandleUpdateDataPortalException(Server.DataPortalException ex) HandleDataPortalException("Update", ex); } +#if NET8_0_OR_GREATER + [DoesNotReturn] +#endif private void HandleDataPortalException(string operation, Server.DataPortalException ex) { var result = ex.Result; var original = ex.InnerException; if (original.InnerException != null) original = original.InnerException; - throw new DataPortalException( - String.Format("DataPortal.{2} {0} ({1})", Resources.Failed, original.Message, operation), - ex.InnerException, result.ReturnObject); + throw new DataPortalException(String.Format("DataPortal.{2} {0} ({1})", Resources.Failed, original.Message, operation), ex.InnerException, result.ReturnObject); } /// @@ -552,7 +539,7 @@ internal async Task DoDeleteAsync(Type objectType, object criteria, bool isSync, "delete", objectType.Name)); - var method = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, Server.DataPortal.GetCriteriaArray(criteria), false); + _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); var proxy = GetDataPortalProxy(method); dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); @@ -676,7 +663,7 @@ public async Task ExecuteAsync(params object[] criteria) return (T)await DoFetchAsync(typeof(T), Server.DataPortal.GetCriteriaFromArray(criteria), false); } - private IDataPortalProxy GetDataPortalProxy(Reflection.ServiceProviderMethodInfo method) + private IDataPortalProxy GetDataPortalProxy(Reflection.ServiceProviderMethodInfo? method) { if (method != null) return GetDataPortalProxy(method.MethodInfo.RunLocal()); diff --git a/Source/Csla/Properties/Resources.Designer.cs b/Source/Csla/Properties/Resources.Designer.cs index b3848930fe..f71202fb60 100644 --- a/Source/Csla/Properties/Resources.Designer.cs +++ b/Source/Csla/Properties/Resources.Designer.cs @@ -690,6 +690,15 @@ public static string NoDeleteRootException { } } + /// + /// Looks up a localized string similar to No business object instance was provided for authorization action {0} of type {1}.. + /// + public static string NoInstanceProvidedForAuthorizationCheck { + get { + return ResourceManager.GetString("NoInstanceProvidedForAuthorizationCheck", resourceCulture); + } + } + /// /// Looks up a localized string similar to No principal object should be passed to DataPortal when using Windows integrated security. /// diff --git a/Source/Csla/Properties/Resources.resx b/Source/Csla/Properties/Resources.resx index b857e0a99f..d1f116aad0 100644 --- a/Source/Csla/Properties/Resources.resx +++ b/Source/Csla/Properties/Resources.resx @@ -529,4 +529,8 @@ The server side portal could not deserialize the incoming request stream. + + No business object instance was provided for authorization action {0} of type {1}. + 0 - auth action, 1 - bo type + \ No newline at end of file diff --git a/Source/Csla/Reflection/LateBoundObject.cs b/Source/Csla/Reflection/LateBoundObject.cs index 3ff7027f02..841e9d1326 100644 --- a/Source/Csla/Reflection/LateBoundObject.cs +++ b/Source/Csla/Reflection/LateBoundObject.cs @@ -212,7 +212,7 @@ public async Task CallMethodTryAsyncDI(bool isSync, params object?[] paramete throw new ArgumentNullException(nameof(parameters)); ThrowInvalidOperationExceptionWhenApplicationContextIsNull(); - var method = ServiceProviderMethodCaller.FindDataPortalMethod(Instance, parameters)!; + var method = ServiceProviderMethodCaller.FindDataPortalMethod(Instance, parameters); try { Utilities.ThrowIfAsyncMethodOnSyncClient(_applicationContext!, isSync, method.MethodInfo); @@ -228,6 +228,9 @@ public async Task CallMethodTryAsyncDI(bool isSync, params object?[] paramete } } +#if NET8_0_OR_GREATER + [MemberNotNull(nameof(_applicationContext))] +#endif private void ThrowInvalidOperationExceptionWhenApplicationContextIsNull() { if (_applicationContext is null) diff --git a/Source/Csla/Reflection/ServiceProviderMethodCaller.cs b/Source/Csla/Reflection/ServiceProviderMethodCaller.cs index 8a9734118e..10d6a4ce7f 100644 --- a/Source/Csla/Reflection/ServiceProviderMethodCaller.cs +++ b/Source/Csla/Reflection/ServiceProviderMethodCaller.cs @@ -48,14 +48,35 @@ public class ServiceProviderMethodCaller : Core.IUseApplicationContext /// Object with methods /// Data portal criteria values /// is . - public ServiceProviderMethodInfo? FindDataPortalMethod(object target, object?[]? criteria) + public ServiceProviderMethodInfo FindDataPortalMethod(object target, object?[]? criteria) where T : DataPortalOperationAttribute { if (target == null) throw new ArgumentNullException(nameof(target)); var targetType = target.GetType(); - return FindDataPortalMethod(targetType, criteria); + return FindDataPortalMethod(targetType, criteria, true)!; + } + + /// + /// Find a method based on data portal criteria + /// and providing any remaining parameters with + /// values from an IServiceProvider + /// + /// Type of attribute to look for + /// Type of domain object + /// Data portal criteria values + /// The maybe found method. + /// if a method with the provided attribute was found. Otherwise . + public bool TryFindDataPortalMethod(Type targetType, object?[]? criteria, +#if NET8_0_OR_GREATER + [NotNullWhen(true)] +#endif + out ServiceProviderMethodInfo? dataPortalMethod) + where T : DataPortalOperationAttribute + { + dataPortalMethod = FindDataPortalMethod(targetType, criteria, false); + return dataPortalMethod != null; } /// @@ -68,7 +89,7 @@ public class ServiceProviderMethodCaller : Core.IUseApplicationContext /// Throw exceptions on error /// The of the data portal method if found. Does not return if is . /// is . - public ServiceProviderMethodInfo? FindDataPortalMethod(Type targetType, object?[]? criteria, bool throwOnError = true) + private ServiceProviderMethodInfo? FindDataPortalMethod(Type targetType, object?[]? criteria, bool throwOnError) where T : DataPortalOperationAttribute { if (targetType == null) @@ -562,4 +583,4 @@ private class ScoredMethodInfo #endif } } -} +} \ No newline at end of file diff --git a/Source/Csla/Reflection/ServiceProviderMethodCallerExtensions.cs b/Source/Csla/Reflection/ServiceProviderMethodCallerExtensions.cs new file mode 100644 index 0000000000..c5abc5ed8c --- /dev/null +++ b/Source/Csla/Reflection/ServiceProviderMethodCallerExtensions.cs @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Dynamically find/invoke methods with DI provided params +//----------------------------------------------------------------------- + +using Csla.Server; + +namespace Csla.Reflection +{ + /// + /// Provides extension methods for . + /// + internal static class ServiceProviderMethodCallerExtensions + { + public static DataPortalMethodInfo GetDataPortalMethodInfoFor(this ServiceProviderMethodCaller source, Type objectType) + where TAttribute : DataPortalOperationAttribute + { + return GetDataPortalMethodInfoFor(source, objectType, EmptyCriteria.Instance); + } + + public static DataPortalMethodInfo GetDataPortalMethodInfoFor(this ServiceProviderMethodCaller source, Type objectType, object criteria) + where TAttribute : DataPortalOperationAttribute + { + var dataPortalMethod = source.FindDataPortalMethod(objectType, UnpackCriteria(criteria)); + dataPortalMethod.PrepForInvocation(); + return dataPortalMethod.DataPortalMethodInfo!; + } + + public static bool TryGetProviderMethodInfoFor(this ServiceProviderMethodCaller source, Type objectType, object criteria, out ServiceProviderMethodInfo? providerMethodInfo) + where TAttribute : DataPortalOperationAttribute + { + return source.TryFindDataPortalMethod(objectType, UnpackCriteria(criteria), out providerMethodInfo); + } + + private static object?[]? UnpackCriteria(object criteria) + { + object?[]? methodCriteria = null; + if (criteria is not EmptyCriteria) + { + methodCriteria = Server.DataPortal.GetCriteriaArray(criteria); + } + + return methodCriteria; + } + } +} diff --git a/Source/Csla/Rules/BusinessRules.cs b/Source/Csla/Rules/BusinessRules.cs index 9b2b2d85a2..f5f240b81e 100644 --- a/Source/Csla/Rules/BusinessRules.cs +++ b/Source/Csla/Rules/BusinessRules.cs @@ -560,6 +560,7 @@ public static Task HasPermissionAsync(ApplicationContext applicationContex throw new ArgumentNullException(nameof(applicationContext)); if (obj is null) throw new ArgumentNullException(nameof(obj)); + return HasPermissionAsync(action, obj, applicationContext, obj.GetType(), null, applicationContext.RuleSet, ct); } diff --git a/Source/Csla/Serialization/Mobile/MobileFormatter.cs b/Source/Csla/Serialization/Mobile/MobileFormatter.cs index 61f537fb4a..42d717063f 100644 --- a/Source/Csla/Serialization/Mobile/MobileFormatter.cs +++ b/Source/Csla/Serialization/Mobile/MobileFormatter.cs @@ -37,7 +37,7 @@ public void Serialize(Stream serializationStream, object? graph) } /// - byte[] ISerializationFormatter.Serialize(object graph) + byte[] ISerializationFormatter.Serialize(object? graph) { using var buffer = new MemoryStream(); Serialize(buffer, graph); diff --git a/Source/Csla/Server/ActiveAuthorizer.cs b/Source/Csla/Server/ActiveAuthorizer.cs index 6241f29150..b60fc4b217 100644 --- a/Source/Csla/Server/ActiveAuthorizer.cs +++ b/Source/Csla/Server/ActiveAuthorizer.cs @@ -19,17 +19,18 @@ namespace Csla.Server /// public class ActiveAuthorizer : IAuthorizeDataPortal { + private readonly ApplicationContext _applicationContext; + /// /// Creates an instance of the type. /// /// + /// is . public ActiveAuthorizer(ApplicationContext applicationContext) { - _applicationContext = applicationContext; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); } - private ApplicationContext _applicationContext; - /// /// Checks authorization rules for the request. /// @@ -42,11 +43,14 @@ public ActiveAuthorizer(ApplicationContext applicationContext) public async Task AuthorizeAsync(AuthorizeRequest clientRequest, CancellationToken ct) { if (_applicationContext.LogicalExecutionLocation == ApplicationContext.LogicalExecutionLocations.Server && - _applicationContext.ExecutionLocation == ApplicationContext.ExecutionLocations.Server) + _applicationContext.ExecutionLocation == ApplicationContext.ExecutionLocations.Server) { if (clientRequest.Operation == DataPortalOperations.Update || clientRequest.Operation == DataPortalOperations.Execute) { + if (clientRequest.RequestObject is null) + throw new InvalidOperationException(string.Format(Resources.NoInstanceProvidedForAuthorizationCheck, clientRequest.Operation.ToSecurityActionDescription(), clientRequest.ObjectType.Name)); + // Per-Instance checks if (!await BusinessRules.HasPermissionAsync(_applicationContext, clientRequest.Operation.ToAuthAction(), clientRequest.RequestObject, ct)) { diff --git a/Source/Csla/Server/AuthorizeRequest.cs b/Source/Csla/Server/AuthorizeRequest.cs index 950ee2cf0c..8b3e6ef9bb 100644 --- a/Source/Csla/Server/AuthorizeRequest.cs +++ b/Source/Csla/Server/AuthorizeRequest.cs @@ -18,20 +18,20 @@ public class AuthorizeRequest /// Gets the type of business object affected by /// the client request. /// - public Type ObjectType { get; private set; } + public Type ObjectType { get; } /// /// Gets a reference to the criteria or /// business object passed from /// the client to the server. /// - public object RequestObject { get; private set; } + public object? RequestObject { get; } /// /// Gets the data portal operation requested /// by the client. /// - public DataPortalOperations Operation { get; private set; } + public DataPortalOperations Operation { get; } - internal AuthorizeRequest(Type objectType, object requestObject, DataPortalOperations operation) + internal AuthorizeRequest(Type objectType, object? requestObject, DataPortalOperations operation) { ObjectType = objectType; RequestObject = requestObject; diff --git a/Source/Csla/Server/ChildDataPortal.cs b/Source/Csla/Server/ChildDataPortal.cs index 30c138668f..f50243ea89 100644 --- a/Source/Csla/Server/ChildDataPortal.cs +++ b/Source/Csla/Server/ChildDataPortal.cs @@ -18,9 +18,10 @@ public class ChildDataPortal /// Creates an instance of the type. /// /// + /// is . public ChildDataPortal(ApplicationContext applicationContext) { - _applicationContext = applicationContext; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); } /// @@ -32,16 +33,15 @@ public ChildDataPortal(ApplicationContext applicationContext) /// Create a new business object. /// /// Type of business object to create. + /// is . public object Create(Type objectType) { - try - { - return DoCreateAsync(objectType).Result; - } - catch (AggregateException ex) - { - throw ex.InnerException; - } + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + + return ExecuteWithAggregateExceptionHandling( + () => DoCreateAsync(objectType).Result + ); } /// @@ -51,16 +51,17 @@ public object Create(Type objectType) /// /// Criteria parameters passed from caller. /// - public object Create(Type objectType, params object[] parameters) + /// or is . + public object Create(Type objectType, params object?[] parameters) { - try - { - return DoCreateAsync(objectType, parameters).Result; - } - catch (AggregateException ex) - { - throw ex.InnerException; - } + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); + + return ExecuteWithAggregateExceptionHandling( + () => DoCreateAsync(objectType, parameters).Result + ); } /// @@ -77,14 +78,17 @@ public async Task CreateAsync() /// /// Criteria parameters passed from caller. /// - public async Task CreateAsync(params object[] parameters) + /// is . + public async Task CreateAsync(params object?[] parameters) { + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); return (T)await DoCreateAsync(typeof(T), parameters).ConfigureAwait(false); } - private async Task DoCreateAsync(Type objectType, params object[] parameters) + private async Task DoCreateAsync(Type objectType, params object?[] parameters) { - DataPortalTarget obj = null; + DataPortalTarget? obj = null; var eventArgs = new DataPortalEventArgs(null, objectType, parameters, DataPortalOperations.Create); try { @@ -108,14 +112,13 @@ private async Task DoCreateAsync(Type objectType, params object[] parame { // ignore exceptions from the exception handler } - object outval = null; + object? outval = null; if (obj != null) outval = obj.Instance; - throw new Csla.DataPortalException( - "ChildDataPortal.Create " + Properties.Resources.FailedOnServer, ex, outval); + throw new Csla.DataPortalException("ChildDataPortal.Create " + Properties.Resources.FailedOnServer, ex, outval); } finally { - object reference = null; + object? reference = null; if (obj != null) reference = obj.Instance; //ApplicationContext.DataPortalActivator.FinalizeInstance(reference); @@ -126,16 +129,15 @@ private async Task DoCreateAsync(Type objectType, params object[] parame /// Get an existing business object. /// /// Type of business object to retrieve. + /// is . public object Fetch(Type objectType) { - try - { - return DoFetchAsync(objectType, null).Result; - } - catch (AggregateException ex) - { - throw ex.InnerException; - } + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + + return ExecuteWithAggregateExceptionHandling( + () => DoFetchAsync(objectType).Result + ); } /// @@ -145,16 +147,17 @@ public object Fetch(Type objectType) /// /// Criteria parameters passed from caller. /// - public object Fetch(Type objectType, params object[] parameters) + /// or is . + public object Fetch(Type objectType, params object?[] parameters) { - try - { - return DoFetchAsync(objectType, parameters).Result; - } - catch (AggregateException ex) - { - throw ex.InnerException; - } + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); + + return ExecuteWithAggregateExceptionHandling( + () => DoFetchAsync(objectType, parameters).Result + ); } /// @@ -171,14 +174,18 @@ public async Task FetchAsync() /// /// Criteria parameters passed from caller. /// - public async Task FetchAsync(params object[] parameters) + /// is . + public async Task FetchAsync(params object?[] parameters) { + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); + return (T)await DoFetchAsync(typeof(T), parameters).ConfigureAwait(false); } - private async Task DoFetchAsync(Type objectType, params object[] parameters) + private async Task DoFetchAsync(Type objectType, params object?[] parameters) { - DataPortalTarget obj = null; + DataPortalTarget? obj = null; var eventArgs = new DataPortalEventArgs(null, objectType, parameters, DataPortalOperations.Fetch); try { @@ -203,10 +210,9 @@ private async Task DoFetchAsync(Type objectType, params object[] paramet { // ignore exceptions from the exception handler } - object outval = null; + object? outval = null; if (obj != null) outval = obj.Instance; - throw new Csla.DataPortalException( - "ChildDataPortal.Fetch " + Properties.Resources.FailedOnServer, ex, outval); + throw new Csla.DataPortalException("ChildDataPortal.Fetch " + Properties.Resources.FailedOnServer, ex, outval); } //finally //{ @@ -218,16 +224,14 @@ private async Task DoFetchAsync(Type objectType, params object[] paramet /// Update a business object. /// /// Business object to update. - public void Update(object obj) + public void Update(object? obj) { - try - { - DoUpdateAsync(obj, false, null).Wait(); - } - catch (AggregateException ex) - { - throw ex.InnerException; - } + _ = ExecuteWithAggregateExceptionHandling(() => + { + DoUpdateAsync(obj, false).Wait(); + return default!; + } + ); } /// @@ -237,25 +241,26 @@ public void Update(object obj) /// /// Parameters passed to method. /// - public void Update(object obj, params object[] parameters) + /// is . + public void Update(object? obj, params object?[] parameters) { - try - { - DoUpdateAsync(obj, false, parameters).Wait(); - } - catch (AggregateException ex) - { - throw ex.InnerException; - } + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); + _ = ExecuteWithAggregateExceptionHandling(() => + { + DoUpdateAsync(obj, false, parameters).Wait(); + return default!; + } + ); } /// /// Update a business object. /// /// Business object to update. - public Task UpdateAsync(object obj) + public Task UpdateAsync(object? obj) { - return DoUpdateAsync(obj, false, null); + return DoUpdateAsync(obj, false); } /// @@ -265,8 +270,12 @@ public Task UpdateAsync(object obj) /// /// Parameters passed to method. /// - public Task UpdateAsync(object obj, params object[] parameters) + /// is . + public Task UpdateAsync(object? obj, params object?[] parameters) { + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); + return DoUpdateAsync(obj, false, parameters); } @@ -274,9 +283,9 @@ public Task UpdateAsync(object obj, params object[] parameters) /// Update a business object. Include objects which are not dirty. /// /// Business object to update. - public void UpdateAll(object obj) + public void UpdateAll(object? obj) { - DoUpdateAsync(obj, true, null).Wait(); + DoUpdateAsync(obj, true).Wait(); } /// @@ -286,8 +295,12 @@ public void UpdateAll(object obj) /// /// Parameters passed to method. /// - public void UpdateAll(object obj, params object[] parameters) + /// is . + public void UpdateAll(object? obj, params object?[] parameters) { + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); + DoUpdateAsync(obj, true, parameters).Wait(); } @@ -295,9 +308,9 @@ public void UpdateAll(object obj, params object[] parameters) /// Update a business object. Include objects which are not dirty. /// /// Business object to update. - public Task UpdateAllAsync(object obj) + public Task UpdateAllAsync(object? obj) { - return DoUpdateAsync(obj, true, null); + return DoUpdateAsync(obj, true); } /// @@ -307,12 +320,16 @@ public Task UpdateAllAsync(object obj) /// /// Parameters passed to method. /// - public Task UpdateAllAsync(object obj, params object[] parameters) + /// is . + public Task UpdateAllAsync(object? obj, params object?[] parameters) { + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); + return DoUpdateAsync(obj, true, parameters); } - private async Task DoUpdateAsync(object obj, bool bypassIsDirtyTest, params object[] parameters) + private async Task DoUpdateAsync(object? obj, bool bypassIsDirtyTest, params object?[] parameters) { if (obj == null) return; @@ -330,30 +347,48 @@ private async Task DoUpdateAsync(object obj, bool bypassIsDirtyTest, params obje try { - lb.Child_OnDataPortalInvoke( - new DataPortalEventArgs(null, objectType, obj, operation)); + lb.Child_OnDataPortalInvoke(new DataPortalEventArgs(null, objectType, obj, operation)); await lb.UpdateChildAsync(parameters).ConfigureAwait(false); - lb.Child_OnDataPortalInvokeComplete( - new DataPortalEventArgs(null, objectType, obj, operation)); + lb.Child_OnDataPortalInvokeComplete(new DataPortalEventArgs(null, objectType, obj, operation)); } catch (Exception ex) { try { - lb?.Child_OnDataPortalException( - new DataPortalEventArgs(null, objectType, obj, operation), ex); + lb.Child_OnDataPortalException(new DataPortalEventArgs(null, objectType, obj, operation), ex); } catch { // ignore exceptions from the exception handler } - throw new Csla.DataPortalException( - "ChildDataPortal.Update " + Properties.Resources.FailedOnServer, ex, obj); + throw new Csla.DataPortalException("ChildDataPortal.Update " + Properties.Resources.FailedOnServer, ex, obj); } //finally //{ // ApplicationContext.DataPortalActivator.FinalizeInstance(lb.Instance); //} } + + private object ExecuteWithAggregateExceptionHandling(Func operation) + { + try + { + return operation(); + } + catch (AggregateException ex) + { + if (ex.InnerException is not null) + { + throw ex.InnerException; + } + + if (ex.InnerExceptions.Count > 0) + { + throw ex.InnerExceptions[0]; + } + + throw new Exception(ex.Message) { Source = ex.Source }; + } + } } } \ No newline at end of file diff --git a/Source/Csla/Server/ChildDataPortalFactory.cs b/Source/Csla/Server/ChildDataPortalFactory.cs index 2b2139e240..53ce7d0c47 100644 --- a/Source/Csla/Server/ChildDataPortalFactory.cs +++ b/Source/Csla/Server/ChildDataPortalFactory.cs @@ -6,6 +6,8 @@ // Implements a data portal service //----------------------------------------------------------------------- +using Microsoft.Extensions.DependencyInjection; + namespace Csla.Server { /// @@ -18,12 +20,13 @@ public class ChildDataPortalFactory : IChildDataPortalFactory /// Creates an instance of the type. /// /// Current ServiceProvider + /// is . public ChildDataPortalFactory(IServiceProvider serviceProvider) { - ServiceProvider = serviceProvider; + ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); } - private IServiceProvider ServiceProvider { get; set; } + private IServiceProvider ServiceProvider { get; } /// /// Get a client-side data portal instance. @@ -31,7 +34,7 @@ public ChildDataPortalFactory(IServiceProvider serviceProvider) /// Root business object type public IChildDataPortal GetPortal() { - return (IChildDataPortal)ServiceProvider.GetService(typeof(IChildDataPortal)); + return ServiceProvider.GetRequiredService>(); } } -} +} \ No newline at end of file diff --git a/Source/Csla/Server/Dashboard/Activity.cs b/Source/Csla/Server/Dashboard/Activity.cs index 0c4250f095..df92ee05ec 100644 --- a/Source/Csla/Server/Dashboard/Activity.cs +++ b/Source/Csla/Server/Dashboard/Activity.cs @@ -10,8 +10,12 @@ public class Activity /// Creates an instance of the type. /// /// InterceptArgs object from the data portal + /// is . public Activity(InterceptArgs result) { + if (result is null) + throw new ArgumentNullException(nameof(result)); + ObjectType = result.ObjectType; Operation = result.Operation; Runtime = result.Runtime; @@ -35,6 +39,6 @@ public Activity(InterceptArgs result) /// Gets the exception (if any) resulting from the /// call. Only valid upon call completion. /// - public Exception Exception { get; private set; } + public Exception? Exception { get; private set; } } } diff --git a/Source/Csla/Server/Dashboard/Dashboard.cs b/Source/Csla/Server/Dashboard/Dashboard.cs index 49defcd55a..52b10976e3 100644 --- a/Source/Csla/Server/Dashboard/Dashboard.cs +++ b/Source/Csla/Server/Dashboard/Dashboard.cs @@ -40,7 +40,7 @@ public Dashboard() /// public int RecentActivityCount { get; set; } = 100; - private void ProcessCompleteQueue(object state) + private void ProcessCompleteQueue(object? state) { if (_completeQueue.IsEmpty) return; @@ -48,7 +48,7 @@ private void ProcessCompleteQueue(object state) _timerComplete.Change(Timeout.Infinite, Timeout.Infinite); try { - while (_completeQueue.TryDequeue(out InterceptArgs result)) + while (_completeQueue.TryDequeue(out InterceptArgs? result)) { if (result.Exception != null) Interlocked.Add(ref _failedCalls, 1); @@ -66,7 +66,7 @@ private void ProcessCompleteQueue(object state) } } - private void ProcessInitializeQueue(object state) + private void ProcessInitializeQueue(object? state) { if (_initializeQueue.IsEmpty) return; @@ -132,14 +132,22 @@ public List GetRecentActivity() return _recentActivity.ToList(); } + /// void IDashboard.InitializeCall(InterceptArgs e) { + if (e is null) + throw new ArgumentNullException(nameof(e)); + LastCall = DateTimeOffset.Now; _initializeQueue.Enqueue(e); } + /// void IDashboard.CompleteCall(InterceptArgs e) { + if (e is null) + throw new ArgumentNullException(nameof(e)); + _completeQueue.Enqueue(e); } diff --git a/Source/Csla/Server/Dashboard/IDashboard.cs b/Source/Csla/Server/Dashboard/IDashboard.cs index f9fca66c2f..b0108ec07b 100644 --- a/Source/Csla/Server/Dashboard/IDashboard.cs +++ b/Source/Csla/Server/Dashboard/IDashboard.cs @@ -46,12 +46,14 @@ public interface IDashboard : IDisposable /// has been inititalized. /// /// Interceptor arguments + /// is . void InitializeCall(InterceptArgs e); /// /// Called by the data portal to indicate a call /// has been completed. /// /// Interceptor arguments + /// is . void CompleteCall(InterceptArgs e); } } \ No newline at end of file diff --git a/Source/Csla/Server/DataPortal.cs b/Source/Csla/Server/DataPortal.cs index af505bcf13..2fad8f705f 100644 --- a/Source/Csla/Server/DataPortal.cs +++ b/Source/Csla/Server/DataPortal.cs @@ -6,10 +6,13 @@ // Implements the server-side DataPortal //----------------------------------------------------------------------- +using System.Diagnostics.CodeAnalysis; using System.Security.Principal; using Csla.Configuration; using Csla.Properties; +using Csla.Reflection; using Csla.Server.Dashboard; +using Microsoft.Extensions.DependencyInjection; namespace Csla.Server { @@ -28,15 +31,15 @@ public class DataPortal : IDataPortalServer /// /// Gets the data portal dashboard instance. /// - private IDashboard Dashboard { get; set; } - private DataPortalOptions DataPortalOptions { get; set; } - private IAuthorizeDataPortal Authorizer { get; set; } - private InterceptorManager InterceptorManager { get; set; } - private IObjectFactoryLoader FactoryLoader { get; set; } - private IDataPortalActivator Activator { get; set; } - private IDataPortalExceptionInspector ExceptionInspector { get; set; } - private DataPortalExceptionHandler DataPortalExceptionHandler { get; set; } - private SecurityOptions SecurityOptions { get; set; } + private IDashboard Dashboard { get; } + private DataPortalOptions DataPortalOptions { get; } + private IAuthorizeDataPortal Authorizer { get; } + private InterceptorManager InterceptorManager { get; } + private IObjectFactoryLoader FactoryLoader { get; } + private IDataPortalActivator Activator { get; } + private IDataPortalExceptionInspector ExceptionInspector { get; } + private DataPortalExceptionHandler DataPortalExceptionHandler { get; } + private SecurityOptions SecurityOptions { get; } /// /// Creates an instance of the type. @@ -51,6 +54,7 @@ public class DataPortal : IDataPortalServer /// /// /// + /// Any parameter is . public DataPortal( ApplicationContext applicationContext, IDashboard dashboard, @@ -63,16 +67,18 @@ public DataPortal( DataPortalExceptionHandler exceptionHandler, SecurityOptions securityOptions) { - _applicationContext = applicationContext; - Dashboard = dashboard; + if (options is null) + throw new ArgumentNullException(nameof(options)); + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); + Dashboard = dashboard ?? throw new ArgumentNullException(nameof(dashboard)); DataPortalOptions = options.DataPortalOptions; - Authorizer = authorizer; - InterceptorManager = interceptors; - FactoryLoader = factoryLoader; - Activator = activator; - ExceptionInspector = exceptionInspector; - DataPortalExceptionHandler = exceptionHandler; - SecurityOptions = securityOptions; + Authorizer = authorizer ?? throw new ArgumentNullException(nameof(authorizer)); + InterceptorManager = interceptors ?? throw new ArgumentNullException(nameof(interceptors)); + FactoryLoader = factoryLoader ?? throw new ArgumentNullException(nameof(factoryLoader)); + Activator = activator ?? throw new ArgumentNullException(nameof(activator)); + ExceptionInspector = exceptionInspector ?? throw new ArgumentNullException(nameof(exceptionInspector)); + DataPortalExceptionHandler = exceptionHandler ?? throw new ArgumentNullException(nameof(exceptionHandler)); + SecurityOptions = securityOptions ?? throw new ArgumentNullException(nameof(securityOptions)); } #region Data Access @@ -96,48 +102,38 @@ private IDataPortalServer GetServicedComponentPortal(TransactionalAttribute tran } #endif - private Reflection.ServiceProviderMethodCaller serviceProviderMethodCaller; + private Reflection.ServiceProviderMethodCaller? serviceProviderMethodCaller; private Reflection.ServiceProviderMethodCaller ServiceProviderMethodCaller { get { if (serviceProviderMethodCaller == null) - serviceProviderMethodCaller = (Reflection.ServiceProviderMethodCaller)_applicationContext.CreateInstanceDI(typeof(Reflection.ServiceProviderMethodCaller)); + serviceProviderMethodCaller = _applicationContext.CreateInstanceDI(); return serviceProviderMethodCaller; } } - /// - /// Create a new business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. - public async Task Create( - Type objectType, object criteria, DataPortalContext context, bool isSync) + /// + public async Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + try { SetContext(context); await AuthorizeRequestAsync(new AuthorizeRequest(objectType, criteria, DataPortalOperations.Create), CancellationToken.None); - await InitializeAsync(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Operation = DataPortalOperations.Create, IsSync = isSync }); + await InitializeAsync(new InterceptArgs(objectType, criteria, DataPortalOperations.Create, isSync)); - DataPortalResult result; - DataPortalMethodInfo method; - - Reflection.ServiceProviderMethodInfo serviceProviderMethodInfo; - if (criteria is EmptyCriteria) - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); - else - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, GetCriteriaArray(criteria)); - serviceProviderMethodInfo.PrepForInvocation(); - method = serviceProviderMethodInfo.DataPortalMethodInfo; + var method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType, criteria); + DataPortalResult result; IDataPortalServer portal; switch (method.TransactionalAttribute.TransactionType) { @@ -171,35 +167,26 @@ public async Task Create( result = await portal.Create(objectType, criteria, context, isSync).ConfigureAwait(false); break; } - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Result = result, Operation = DataPortalOperations.Create, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, result, DataPortalOperations.Create, isSync)); return result; } catch (DataPortalException ex) { - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = ex, Operation = DataPortalOperations.Create, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, ex, DataPortalOperations.Create, isSync)); throw; } - catch (AggregateException ex) - { - Exception error = null; - if (ex.InnerExceptions.Count > 0) - error = ex.InnerExceptions[0].InnerException; - else - error = ex; - var fex = NewDataPortalException( - _applicationContext, "DataPortal.Create " + Resources.FailedOnServer, - DataPortalExceptionHandler.InspectException(objectType, criteria, "DataPortal.Create", error), - null, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = fex, Operation = DataPortalOperations.Create, IsSync = isSync }); - throw fex; - } catch (Exception ex) { + if (ex is AggregateException aggregateEx && aggregateEx.InnerExceptions.Count > 0) + { + ex = aggregateEx.InnerExceptions[0].InnerException ?? aggregateEx; + } + var fex = NewDataPortalException( _applicationContext, "DataPortal.Create " + Resources.FailedOnServer, DataPortalExceptionHandler.InspectException(objectType, criteria, "DataPortal.Create", ex), null, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = fex, Operation = DataPortalOperations.Create, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, fex, DataPortalOperations.Create, isSync)); throw fex; } finally @@ -208,17 +195,16 @@ public async Task Create( } } - /// - /// Get an existing business object. - /// - /// Type of business object to retrieve. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (typeof(Core.ICommandObject).IsAssignableFrom(objectType)) { return await Execute(objectType, criteria, context, isSync); @@ -230,20 +216,11 @@ public async Task Fetch(Type objectType, object criteria, Data await AuthorizeRequestAsync(new AuthorizeRequest(objectType, criteria, DataPortalOperations.Fetch), CancellationToken.None); - await InitializeAsync(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Operation = DataPortalOperations.Fetch, IsSync = isSync }); + await InitializeAsync(new InterceptArgs(objectType, criteria, DataPortalOperations.Fetch, isSync)); - DataPortalResult result; - DataPortalMethodInfo method; - - Reflection.ServiceProviderMethodInfo serviceProviderMethodInfo; - if (criteria is EmptyCriteria) - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); - else - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, GetCriteriaArray(criteria)); - - serviceProviderMethodInfo.PrepForInvocation(); - method = serviceProviderMethodInfo.DataPortalMethodInfo; + var method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType, criteria); + DataPortalResult result; IDataPortalServer portal; switch (method.TransactionalAttribute.TransactionType) { @@ -276,35 +253,26 @@ public async Task Fetch(Type objectType, object criteria, Data result = await portal.Fetch(objectType, criteria, context, isSync).ConfigureAwait(false); break; } - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Result = result, Operation = DataPortalOperations.Fetch, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, result, DataPortalOperations.Fetch, isSync)); return result; } catch (DataPortalException ex) { - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = ex, Operation = DataPortalOperations.Fetch, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, ex, DataPortalOperations.Fetch, isSync)); throw; } - catch (AggregateException ex) - { - Exception error = null; - if (ex.InnerExceptions.Count > 0) - error = ex.InnerExceptions[0].InnerException; - else - error = ex; - var fex = NewDataPortalException( - _applicationContext, "DataPortal.Fetch " + Resources.FailedOnServer, - DataPortalExceptionHandler.InspectException(objectType, criteria, "DataPortal.Fetch", error), - null, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = fex, Operation = DataPortalOperations.Fetch, IsSync = isSync }); - throw fex; - } catch (Exception ex) { + if (ex is AggregateException aggregateException && aggregateException.InnerExceptions.Count > 0) + { + ex = aggregateException.InnerExceptions[0].InnerException ?? ex; + } + var fex = NewDataPortalException( _applicationContext, "DataPortal.Fetch " + Resources.FailedOnServer, DataPortalExceptionHandler.InspectException(objectType, criteria, "DataPortal.Fetch", ex), null, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = fex, Operation = DataPortalOperations.Fetch, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, fex, DataPortalOperations.Fetch, isSync)); throw fex; } finally @@ -330,20 +298,11 @@ private async Task Execute(Type objectType, object criteria, D await AuthorizeRequestAsync(new AuthorizeRequest(objectType, criteria, DataPortalOperations.Execute), CancellationToken.None); - await InitializeAsync(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Operation = DataPortalOperations.Execute, IsSync = isSync }); + await InitializeAsync(new InterceptArgs(objectType, criteria, DataPortalOperations.Execute, isSync)); - DataPortalResult result; - DataPortalMethodInfo method; - - Reflection.ServiceProviderMethodInfo serviceProviderMethodInfo; - if (criteria is EmptyCriteria) - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); - else - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, GetCriteriaArray(criteria)); - - serviceProviderMethodInfo.PrepForInvocation(); - method = serviceProviderMethodInfo.DataPortalMethodInfo; + var method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType, criteria); + DataPortalResult result; IDataPortalServer portal; switch (method.TransactionalAttribute.TransactionType) { @@ -376,35 +335,26 @@ private async Task Execute(Type objectType, object criteria, D result = await portal.Fetch(objectType, criteria, context, isSync).ConfigureAwait(false); break; } - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Result = result, Operation = DataPortalOperations.Execute, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, result, DataPortalOperations.Execute, isSync)); return result; } catch (DataPortalException ex) { - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = ex, Operation = DataPortalOperations.Execute, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, ex, DataPortalOperations.Execute, isSync)); throw; } - catch (AggregateException ex) - { - Exception error = null; - if (ex.InnerExceptions.Count > 0) - error = ex.InnerExceptions[0].InnerException; - else - error = ex; - var fex = NewDataPortalException( - _applicationContext, "DataPortal.Execute " + Resources.FailedOnServer, - DataPortalExceptionHandler.InspectException(objectType, criteria, "DataPortal.Execute", error), - null, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = fex, Operation = DataPortalOperations.Execute, IsSync = isSync }); - throw fex; - } catch (Exception ex) { + if (ex is AggregateException aggregateException && aggregateException.InnerExceptions.Count > 0) + { + ex = aggregateException.InnerExceptions[0].InnerException ?? ex; + } + var fex = NewDataPortalException( _applicationContext, "DataPortal.Execute " + Resources.FailedOnServer, DataPortalExceptionHandler.InspectException(objectType, criteria, "DataPortal.Execute", ex), null, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = fex, Operation = DataPortalOperations.Execute, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, fex, DataPortalOperations.Execute, isSync)); throw fex; } finally @@ -413,40 +363,34 @@ private async Task Execute(Type objectType, object criteria, D } } - /// - /// Update a business object. - /// - /// Business object to update. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] + /// public async Task Update(object obj, DataPortalContext context, bool isSync) { - Type objectType = null; - DataPortalOperations operation = DataPortalOperations.Update; + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + + var objectType = obj.GetType(); + var operation = DataPortalOperations.Update; try { SetContext(context); - objectType = obj.GetType(); - if (obj is Core.ICommandObject) operation = DataPortalOperations.Execute; await AuthorizeRequestAsync(new AuthorizeRequest(objectType, obj, operation), CancellationToken.None); - await InitializeAsync(new InterceptArgs { ObjectType = objectType, Parameter = obj, Operation = operation, IsSync = isSync }); + await InitializeAsync(new InterceptArgs(objectType, obj, operation, isSync)); - DataPortalResult result; DataPortalMethodInfo method; var factoryInfo = ObjectFactoryAttribute.GetObjectFactoryAttribute(objectType); if (factoryInfo != null) { string methodName; - var factoryLoader = _applicationContext.CurrentServiceProvider.GetService(typeof(IObjectFactoryLoader)) as IObjectFactoryLoader; - var factoryType = factoryLoader?.GetFactoryType(factoryInfo.FactoryTypeName); + var factoryLoader = _applicationContext.CurrentServiceProvider.GetRequiredService(); + var factoryType = factoryLoader.GetFactoryType(factoryInfo.FactoryTypeName); if (obj is Core.BusinessBase bbase) { if (bbase.IsDeleted) @@ -462,27 +406,23 @@ public async Task Update(object obj, DataPortalContext context } else { - Reflection.ServiceProviderMethodInfo serviceProviderMethodInfo; if (obj is Core.BusinessBase bbase) { if (bbase.IsDeleted) - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); - else - if (bbase.IsNew) - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); + method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType); + else if (bbase.IsNew) + method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType); else - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); + method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType); } else if (obj is Core.ICommandObject) - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); + method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType); else - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); - - serviceProviderMethodInfo.PrepForInvocation(); - method = serviceProviderMethodInfo.DataPortalMethodInfo; + method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType); } context.TransactionalType = method.TransactionalAttribute.TransactionType; + DataPortalResult result; IDataPortalServer portal; switch (method.TransactionalAttribute.TransactionType) { @@ -515,35 +455,26 @@ public async Task Update(object obj, DataPortalContext context result = await portal.Update(obj, context, isSync).ConfigureAwait(false); break; } - Complete(new InterceptArgs { ObjectType = objectType, Parameter = obj, Result = result, Operation = operation, IsSync = isSync }); + Complete(new InterceptArgs(objectType, obj, result, operation, isSync)); return result; } catch (DataPortalException ex) { - Complete(new InterceptArgs { ObjectType = objectType, Parameter = obj, Exception = ex, Operation = operation, IsSync = isSync }); + Complete(new InterceptArgs(objectType, obj, ex, operation, isSync)); throw; } - catch (AggregateException ex) - { - Exception error = null; - if (ex.InnerExceptions.Count > 0) - error = ex.InnerExceptions[0].InnerException; - else - error = ex; - var fex = NewDataPortalException( - _applicationContext, "DataPortal.Update " + Resources.FailedOnServer, - DataPortalExceptionHandler.InspectException(obj.GetType(), obj, null, "DataPortal.Update", error), - obj, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = obj, Exception = fex, Operation = operation, IsSync = isSync }); - throw fex; - } catch (Exception ex) { + if (ex is AggregateException aggregateException && aggregateException.InnerExceptions.Count > 0) + { + ex = aggregateException.InnerExceptions[0].InnerException ?? ex; + } + var fex = NewDataPortalException( _applicationContext, "DataPortal.Update " + Resources.FailedOnServer, DataPortalExceptionHandler.InspectException(obj.GetType(), obj, null, "DataPortal.Update", ex), obj, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = obj, Exception = fex, Operation = operation, IsSync = isSync }); + Complete(new InterceptArgs(objectType, obj, fex, operation, isSync)); throw fex; } finally @@ -552,44 +483,37 @@ public async Task Update(object obj, DataPortalContext context } } - /// - /// Delete a business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + try { SetContext(context); await AuthorizeRequestAsync(new AuthorizeRequest(objectType, criteria, DataPortalOperations.Delete), CancellationToken.None); - await InitializeAsync(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Operation = DataPortalOperations.Delete, IsSync = isSync }); + await InitializeAsync(new InterceptArgs(objectType, criteria, DataPortalOperations.Delete, isSync)); DataPortalResult result; DataPortalMethodInfo method; var factoryInfo = ObjectFactoryAttribute.GetObjectFactoryAttribute(objectType); if (factoryInfo != null) { - var factoryLoader = _applicationContext.CurrentServiceProvider.GetService(typeof(IObjectFactoryLoader)) as IObjectFactoryLoader; - var factoryType = factoryLoader?.GetFactoryType(factoryInfo.FactoryTypeName); + var factoryLoader = _applicationContext.CurrentServiceProvider.GetRequiredService(); + var factoryType = factoryLoader.GetFactoryType(factoryInfo.FactoryTypeName); string methodName = factoryInfo.DeleteMethodName; method = DataPortalMethodCache.GetMethodInfo(factoryType, methodName, criteria); } else { - Reflection.ServiceProviderMethodInfo serviceProviderMethodInfo; - if (criteria is EmptyCriteria) - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, null); - else - serviceProviderMethodInfo = ServiceProviderMethodCaller.FindDataPortalMethod(objectType, GetCriteriaArray(criteria)); - serviceProviderMethodInfo.PrepForInvocation(); - method = serviceProviderMethodInfo.DataPortalMethodInfo; + method = ServiceProviderMethodCaller.GetDataPortalMethodInfoFor(objectType, criteria); } IDataPortalServer portal; @@ -624,35 +548,26 @@ public async Task Delete(Type objectType, object criteria, Dat result = await portal.Delete(objectType, criteria, context, isSync).ConfigureAwait(false); break; } - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Result = result, Operation = DataPortalOperations.Delete, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, result, DataPortalOperations.Delete, isSync)); return result; } catch (DataPortalException ex) { - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = ex, Operation = DataPortalOperations.Delete, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, ex, DataPortalOperations.Delete, isSync)); throw; } - catch (AggregateException ex) - { - Exception error = null; - if (ex.InnerExceptions.Count > 0) - error = ex.InnerExceptions[0].InnerException; - else - error = ex; - var fex = NewDataPortalException( - _applicationContext, "DataPortal.Delete " + Resources.FailedOnServer, - DataPortalExceptionHandler.InspectException(objectType, criteria, "DataPortal.Delete", error), - null, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = fex, Operation = DataPortalOperations.Delete, IsSync = isSync }); - throw fex; - } catch (Exception ex) { + if (ex is AggregateException aggregateException && aggregateException.InnerExceptions.Count > 0) + { + ex = aggregateException.InnerExceptions[0].InnerException ?? ex; + } + var fex = NewDataPortalException( _applicationContext, "DataPortal.Delete " + Resources.FailedOnServer, DataPortalExceptionHandler.InspectException(objectType, criteria, "DataPortal.Delete", ex), null, DataPortalOptions); - Complete(new InterceptArgs { ObjectType = objectType, Parameter = criteria, Exception = fex, Operation = DataPortalOperations.Delete, IsSync = isSync }); + Complete(new InterceptArgs(objectType, criteria, fex, DataPortalOperations.Delete, isSync)); throw fex; } finally @@ -715,10 +630,7 @@ private void SetPrincipal(DataPortalContext context) // When using platform-supplied security, Principal must be null if (context.Principal != null) { - Security.SecurityException ex = - new Security.SecurityException(Resources.NoPrincipalAllowedException); - //ex.Action = System.Security.Permissions.SecurityAction.Deny; - throw ex; + throw new Security.SecurityException(Resources.NoPrincipalAllowedException); } if (SecurityOptions.AuthenticationType == "Windows") { @@ -731,11 +643,7 @@ private void SetPrincipal(DataPortalContext context) // We expect some Principal object to be available if (context.Principal == null) { - Security.SecurityException ex = - new Security.SecurityException( - Resources.BusinessPrincipalException + " Nothing"); - //ex.Action = System.Security.Permissions.SecurityAction.Deny; - throw ex; + throw new Security.SecurityException(Resources.BusinessPrincipalException + " Nothing"); } _applicationContext.User = context.Principal; } @@ -758,7 +666,7 @@ private void ClearContext(DataPortalContext context) if (!context.IsRemotePortal) return; _applicationContext.Clear(); if (SecurityOptions.FlowSecurityPrincipalFromClient) - _applicationContext.User = null; + _applicationContext.SetUnauthenticatedUser(); } #endregion @@ -768,15 +676,15 @@ private async Task AuthorizeRequestAsync(AuthorizeRequest clientRequest, Cancell await Authorizer.AuthorizeAsync(clientRequest, ct); } - internal static DataPortalException NewDataPortalException( - ApplicationContext applicationContext, string message, Exception innerException, object businessObject, DataPortalOptions dataPortalOptions) +#if NET8_0_OR_GREATER + [DoesNotReturn] +#endif + internal static DataPortalException NewDataPortalException(ApplicationContext applicationContext, string message, Exception innerException, object? businessObject, DataPortalOptions dataPortalOptions) { if (!dataPortalOptions.DataPortalServerOptions.DataPortalReturnObjectOnException) businessObject = null; - throw new DataPortalException( - message, - innerException, new DataPortalResult(applicationContext, businessObject)); + throw new DataPortalException(message, innerException, new DataPortalResult(applicationContext, businessObject, null)); } /// @@ -807,8 +715,8 @@ public static object GetCriteriaFromArray(params object[] criteria) /// Converts a single serializable criteria value /// into an array of type object. /// - /// Single serializble criteria value - public static object[] GetCriteriaArray(object criteria) + /// Single serializable criteria value + public static object?[]? GetCriteriaArray(object? criteria) { if (criteria == null) return null; diff --git a/Source/Csla/Server/DataPortalBroker.cs b/Source/Csla/Server/DataPortalBroker.cs index 21a5ac57f7..7a8e54f05c 100644 --- a/Source/Csla/Server/DataPortalBroker.cs +++ b/Source/Csla/Server/DataPortalBroker.cs @@ -6,6 +6,8 @@ // Allows interception of DataPortal call //----------------------------------------------------------------------- +using System.Xml; + namespace Csla.Server { /// @@ -18,9 +20,10 @@ public class DataPortalBroker : IDataPortalServer /// /// /// + /// is . public DataPortalBroker(DataPortalSelector dataPortalSelector) { - DataPortalSelector = dataPortalSelector; + DataPortalSelector = dataPortalSelector ?? throw new ArgumentNullException(nameof(dataPortalSelector)); } private DataPortalSelector DataPortalSelector { get; set; } @@ -28,19 +31,18 @@ public DataPortalBroker(DataPortalSelector dataPortalSelector) /// Gets or sets a reference to a implementation of /// IDataPortalServer to be used. /// - public static IDataPortalServer DataPortalServer { get; set; } + public static IDataPortalServer? DataPortalServer { get; set; } - /// - /// Create a new business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (DataPortalServer != null) { return DataPortalServer.Create(objectType, criteria, context, isSync); @@ -51,17 +53,16 @@ public Task Create(Type objectType, object criteria, DataPorta } } - /// - /// Get an existing business object. - /// - /// Type of business object to retrieve. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (DataPortalServer != null) { return DataPortalServer.Fetch(objectType, criteria, context, isSync); @@ -72,16 +73,14 @@ public Task Fetch(Type objectType, object criteria, DataPortal } } - /// - /// Update a business object. - /// - /// Business object to update. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public Task Update(object obj, DataPortalContext context, bool isSync) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (DataPortalServer != null) { return DataPortalServer.Update(obj, context, isSync); @@ -92,17 +91,16 @@ public Task Update(object obj, DataPortalContext context, bool } } - /// - /// Delete a business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (DataPortalServer != null) { return DataPortalServer.Delete(objectType, criteria, context, isSync); diff --git a/Source/Csla/Server/DataPortalContext.cs b/Source/Csla/Server/DataPortalContext.cs index 883fbbc62a..2327b8cd3e 100644 --- a/Source/Csla/Server/DataPortalContext.cs +++ b/Source/Csla/Server/DataPortalContext.cs @@ -10,6 +10,7 @@ using Csla.Core; using Csla.Configuration; using Csla.Serialization; +using Csla.Serialization.Mobile; namespace Csla.Server { @@ -23,13 +24,13 @@ public class DataPortalContext : Serialization.Mobile.IMobileObject, IUseApplica [NonSerialized] private TransactionalTypes _transactionalType; [NonSerialized] - private ObjectFactoryAttribute _factoryInfo; + private ObjectFactoryAttribute? _factoryInfo; /// /// The current principal object /// if CSLA security is being used. /// - public IPrincipal Principal { get; private set; } + public IPrincipal? Principal { get; private set; } /// /// Returns true if the @@ -50,7 +51,7 @@ public class DataPortalContext : Serialization.Mobile.IMobileObject, IUseApplica /// public string ClientUICulture { get; private set; } - internal IContextDictionary ClientContext { get; private set; } + internal IContextDictionary? ClientContext { get; private set; } /// /// Gets the current transactional type. Only valid @@ -69,7 +70,7 @@ public TransactionalTypes TransactionalType /// data portal methods after the attribute /// value has been determined. /// - public ObjectFactoryAttribute FactoryInfo + public ObjectFactoryAttribute? FactoryInfo { get { return _factoryInfo; } internal set { _factoryInfo = value; } @@ -85,9 +86,10 @@ public ObjectFactoryAttribute FactoryInfo /// /// ApplicationContext instance. /// Indicates whether the DataPortal is remote. + /// is . public DataPortalContext(ApplicationContext applicationContext, bool isRemotePortal) { - _applicationContext = applicationContext; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); Principal = GetPrincipal(applicationContext, isRemotePortal); IsRemotePortal = isRemotePortal; ClientCulture = Thread.CurrentThread.CurrentCulture.Name; @@ -104,23 +106,27 @@ public DataPortalContext(ApplicationContext applicationContext, bool isRemotePor /// Client context. /// Client culture. /// Client UI culture. + /// , , , or is . public DataPortalContext(ApplicationContext applicationContext, IPrincipal principal, bool isRemotePortal, string clientCulture, string clientUICulture, IContextDictionary clientContext) { - _applicationContext = applicationContext; - Principal = principal; - ClientContext = clientContext; - ClientCulture = clientCulture; - ClientUICulture = clientUICulture; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); + Principal = principal ?? throw new ArgumentNullException(nameof(principal)); + ClientContext = clientContext ?? throw new ArgumentNullException(nameof(clientContext)); + ClientCulture = clientCulture ?? throw new ArgumentNullException(nameof(clientCulture)); + ClientUICulture = clientUICulture ?? throw new ArgumentNullException(nameof(clientUICulture)); IsRemotePortal = isRemotePortal; } /// /// Default constructor for use by SerializationFormatterFactory.GetFormatter(). /// + [Obsolete(MobileFormatter.DefaultCtorObsoleteMessage, error: true)] +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. It's okay to suppress because it can't be used by user code public DataPortalContext() +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. { } - private IPrincipal GetPrincipal(ApplicationContext applicationContext, bool isRemotePortal) + private IPrincipal? GetPrincipal(ApplicationContext applicationContext, bool isRemotePortal) { var securityOptions = applicationContext.GetRequiredService(); if (isRemotePortal && !securityOptions.FlowSecurityPrincipalFromClient) @@ -150,10 +156,10 @@ void Serialization.Mobile.IMobileObject.GetChildren(Serialization.Mobile.Seriali void Serialization.Mobile.IMobileObject.SetState(Serialization.Mobile.SerializationInfo info) { - Principal = (IPrincipal)_applicationContext.GetRequiredService().Deserialize(info.GetValue("principal")); - ClientContext = (IContextDictionary)_applicationContext.GetRequiredService().Deserialize(info.GetValue("clientContext")); - ClientCulture = info.GetValue("clientCulture"); - ClientUICulture = info.GetValue("clientUICulture"); + Principal = (IPrincipal?)_applicationContext.GetRequiredService().Deserialize(info.GetValue("principal")!); + ClientContext = (IContextDictionary?)_applicationContext.GetRequiredService().Deserialize(info.GetValue("clientContext")!); + ClientCulture = info.GetValue("clientCulture")!; + ClientUICulture = info.GetValue("clientUICulture")!; IsRemotePortal = info.GetValue("isRemotePortal"); } diff --git a/Source/Csla/Server/DataPortalException.cs b/Source/Csla/Server/DataPortalException.cs index 37f32e0fed..6a71c19920 100644 --- a/Source/Csla/Server/DataPortalException.cs +++ b/Source/Csla/Server/DataPortalException.cs @@ -31,8 +31,7 @@ public class DataPortalException : Exception [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)")] public override string StackTrace { - get { return String.Format("{0}{1}{2}", - _innerStackTrace, Environment.NewLine, base.StackTrace); } + get { return String.Format("{0}{1}{2}", _innerStackTrace, Environment.NewLine, base.StackTrace); } } /// @@ -41,12 +40,18 @@ public override string StackTrace /// Text describing the exception. /// Inner exception. /// The data portal result object. - public DataPortalException( - string message, Exception ex, DataPortalResult result) + /// or is . + /// is , or only consists of white spaces. + public DataPortalException(string message, Exception ex, DataPortalResult result) : base(message, ex) { - _innerStackTrace = ex.StackTrace; - Result = result; + if (string.IsNullOrWhiteSpace(message)) + throw new ArgumentException(string.Format(Properties.Resources.StringNotNullOrWhiteSpaceException, nameof(message)), nameof(message)); + if (ex is null) + throw new ArgumentNullException(nameof(ex)); + + Result = result ?? throw new ArgumentNullException(nameof(result)); + _innerStackTrace = ex.StackTrace ?? ""; } } } \ No newline at end of file diff --git a/Source/Csla/Server/DataPortalExceptionHandler.cs b/Source/Csla/Server/DataPortalExceptionHandler.cs index 7c75695d9a..b31acaf0b9 100644 --- a/Source/Csla/Server/DataPortalExceptionHandler.cs +++ b/Source/Csla/Server/DataPortalExceptionHandler.cs @@ -3,9 +3,10 @@ // Copyright (c) Marimer LLC. All rights reserved. // Website: https://cslanet.com // -// This class provides a hoook for developers to add custom error handling in the DataPortal. +// This class provides a hook for developers to add custom error handling in the DataPortal. //----------------------------------------------------------------------- +using System.Diagnostics.CodeAnalysis; using Csla.Properties; using Csla.Reflection; @@ -24,9 +25,10 @@ public class DataPortalExceptionHandler /// Creates an instance of the type. /// /// + /// is . public DataPortalExceptionHandler(IDataPortalExceptionInspector exceptionInspector) { - ExceptionInspector = exceptionInspector; + ExceptionInspector = exceptionInspector ?? throw new ArgumentNullException(nameof(exceptionInspector)); } private IDataPortalExceptionInspector ExceptionInspector { get; set; } @@ -38,21 +40,30 @@ public DataPortalExceptionHandler(IDataPortalExceptionInspector exceptionInspect /// The criteria. /// Name of the method. /// The exception. + /// , or is . + /// is , or only consists of white spaces. public Exception InspectException(Type objectType, object criteria, string methodName, Exception ex) { - Exception handledException; + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (string.IsNullOrWhiteSpace(methodName)) + throw new ArgumentException(string.Format(Resources.StringNotNullOrWhiteSpaceException, nameof(methodName)), nameof(methodName)); + if (ex is null) + throw new ArgumentNullException(nameof(ex)); if (ex is CallMethodException) { - if (CallExceptionInspector(objectType, null, criteria, methodName, ex.InnerException, out handledException)) + if (CallExceptionInspector(objectType, null, criteria, methodName, ex.InnerException!, out var handledException)) { - ex = new CallMethodException(methodName + " " + Resources.MethodCallFailed, handledException); + return new CallMethodException(methodName + " " + Resources.MethodCallFailed, handledException!); } } else { - if (CallExceptionInspector(objectType, null, criteria, methodName, ex, out handledException)) + if (CallExceptionInspector(objectType, null, criteria, methodName, ex, out var handledException)) { - ex = handledException; + return handledException!; } } @@ -67,23 +78,24 @@ public Exception InspectException(Type objectType, object criteria, string metho /// The criteria. /// Name of the method. /// The exception. - public Exception InspectException(Type objectType, object businessObject, object criteria, string methodName, Exception ex) + /// or is . + /// is , or only consists of white spaces. + public Exception InspectException(Type objectType, object? businessObject, object? criteria, string methodName, Exception ex) { // The exception as parameter is always a CallMethodException containing the business exception as Inner exception - Exception handledException; if (ex is CallMethodException) { - if (CallExceptionInspector(objectType, businessObject, criteria, methodName, ex.InnerException, out handledException)) + if (CallExceptionInspector(objectType, businessObject, criteria, methodName, ex.InnerException!, out var handledException)) { // developer should only transform and if rethrows a new we will wrap as new CallMethodException - ex = new CallMethodException(methodName + " " + Resources.MethodCallFailed, handledException); + return new CallMethodException(methodName + " " + Resources.MethodCallFailed, handledException!); } } else { - if (CallExceptionInspector(objectType, null, criteria, methodName, ex, out handledException)) + if (CallExceptionInspector(objectType, null, criteria, methodName, ex, out var handledException)) { - ex = handledException; + return handledException!; } } return ex; @@ -101,7 +113,12 @@ public Exception InspectException(Type objectType, object businessObject, object /// /// true if new exception was thrown else false /// - private bool CallExceptionInspector(Type objectType, object businessObject, object criteria, string methodName, Exception exception, out Exception handledException) + private bool CallExceptionInspector(Type objectType, object? businessObject, object? criteria, string methodName, Exception exception, +#if NET8_0_OR_GREATER + [NotNullWhen(true)] +#endif + out Exception? handledException + ) { handledException = null; try diff --git a/Source/Csla/Server/DataPortalMethodCache.cs b/Source/Csla/Server/DataPortalMethodCache.cs index 450e2a07ad..65fae7b70f 100644 --- a/Source/Csla/Server/DataPortalMethodCache.cs +++ b/Source/Csla/Server/DataPortalMethodCache.cs @@ -11,78 +11,68 @@ using Csla.Runtime; #endif using Csla.Reflection; +using Csla.Properties; namespace Csla.Server { internal static class DataPortalMethodCache { #if NET8_0_OR_GREATER - private static Dictionary> _cache = []; + private static readonly Dictionary> _cache = []; #else - private static Dictionary _cache = []; + private static readonly Dictionary _cache = []; #endif public static DataPortalMethodInfo GetMethodInfo(Type objectType, string methodName, params object[] parameters) { - var key = new MethodCacheKey(objectType.FullName, methodName, MethodCaller.GetParameterTypes(parameters)); + var key = new MethodCacheKey(objectType.FullName!, methodName, MethodCaller.GetParameterTypes(parameters)); - DataPortalMethodInfo result = null; - - var found = false; + DataPortalMethodInfo result; #if NET8_0_OR_GREATER - try + if (_cache.TryGetValue(key, out var methodInfo)) { - found = _cache.TryGetValue(key, out var methodInfo); - - result = methodInfo?.Item2; + return methodInfo.Item2; } - catch - { /* failure will drop into !found block */ } - if (!found) + lock (_cache) { - lock (_cache) + if (_cache.TryGetValue(key, out methodInfo)) { - found = _cache.TryGetValue(key, out var methodInfo); - - result = methodInfo?.Item2; - - if (!found) - { - result = new DataPortalMethodInfo(MethodCaller.GetMethod(objectType, methodName, parameters)); - - var cacheInstance = AssemblyLoadContextManager.CreateCacheInstance(objectType, result, OnAssemblyLoadContextUnload); - - _cache.Add(key, cacheInstance); - } + return methodInfo.Item2; } + + var method = GetMethodOfCaller(objectType, methodName, parameters); + + result = new DataPortalMethodInfo(method); + var cacheInstance = AssemblyLoadContextManager.CreateCacheInstance(objectType, result, OnAssemblyLoadContextUnload); + _cache.Add(key, cacheInstance); } + #else - try - { - found = _cache.TryGetValue(key, out result); - } - catch - { /* failure will drop into !found block */ } + if (_cache.TryGetValue(key, out result)) + return result; - if (!found) + lock (_cache) { - lock (_cache) + if (!_cache.TryGetValue(key, out result)) { - if (!_cache.TryGetValue(key, out result)) - { - result = new DataPortalMethodInfo(MethodCaller.GetMethod(objectType, methodName, parameters)); + var method = GetMethodOfCaller(objectType, methodName, parameters); + result = new DataPortalMethodInfo(method); - _cache.Add(key, result); - } + _cache.Add(key, result); } - } + } #endif return result; } + private static System.Reflection.MethodInfo GetMethodOfCaller(Type objectType, string methodName, object[] parameters) + { + return MethodCaller.GetMethod(objectType, methodName, parameters) ?? throw new InvalidOperationException(string.Format(Resources.NoSuchMethod, $"{objectType.FullName}.{methodName}")); + } + #if NET8_0_OR_GREATER private static void OnAssemblyLoadContextUnload(AssemblyLoadContext context) { diff --git a/Source/Csla/Server/DataPortalMethodInfo.cs b/Source/Csla/Server/DataPortalMethodInfo.cs index 09475459a1..10b9a83d4f 100644 --- a/Source/Csla/Server/DataPortalMethodInfo.cs +++ b/Source/Csla/Server/DataPortalMethodInfo.cs @@ -10,35 +10,22 @@ namespace Csla.Server { internal class DataPortalMethodInfo { - public bool RunLocal { get; private set; } + public bool RunLocal { get; } #if !(ANDROID || IOS) - public TransactionalAttribute TransactionalAttribute { get; private set; } + public TransactionalAttribute TransactionalAttribute { get; } #else public TransactionalTypes TransactionalType { get; private set; } #endif - public DataPortalMethodInfo() - { -#if !(ANDROID || IOS) - TransactionalAttribute = new TransactionalAttribute(TransactionalTypes.Manual); -#else - TransactionalType = TransactionalTypes.Manual; -#endif - - } public DataPortalMethodInfo(System.Reflection.MethodInfo info) - : this() { - if (info != null) - { - RunLocal = IsRunLocal(info); + RunLocal = IsRunLocal(info); #if !(ANDROID || IOS) - TransactionalAttribute = GetTransactionalAttribute(info); + TransactionalAttribute = GetTransactionalAttribute(info); #else - TransactionalType = TransactionalTypes.Manual; + TransactionalType = TransactionalTypes.Manual; #endif - } } private static bool IsRunLocal(System.Reflection.MethodInfo method) @@ -53,9 +40,7 @@ private static TransactionalAttribute GetTransactionalAttribute(System.Reflectio TransactionalAttribute result; if (IsTransactionalMethod(method)) { - result = - (TransactionalAttribute)Attribute.GetCustomAttribute( - method, typeof(TransactionalAttribute)); + result = (TransactionalAttribute)Attribute.GetCustomAttribute(method, typeof(TransactionalAttribute))!; } else result = new TransactionalAttribute(TransactionalTypes.Manual); diff --git a/Source/Csla/Server/DataPortalResult.cs b/Source/Csla/Server/DataPortalResult.cs index e9cdc255e4..a7086fa980 100644 --- a/Source/Csla/Server/DataPortalResult.cs +++ b/Source/Csla/Server/DataPortalResult.cs @@ -30,7 +30,7 @@ public class DataPortalResult : EventArgs, Core.IUseApplicationContext public object? ReturnObject { get; private set; } /// - /// Error that occurred during the DataPotal call. + /// Error that occurred during the DataPortal call. /// This will be null if no errors occurred. /// public Exception? Error { get; private set; } @@ -74,7 +74,7 @@ public DataPortalResult(ApplicationContext applicationContext, object returnObje /// Object to return as part /// of the result. /// - /// Error that occurred during the DataPotal call. + /// Error that occurred during the DataPortal call. /// This will be null if no errors occurred. /// /// is . diff --git a/Source/Csla/Server/DataPortalSelector.cs b/Source/Csla/Server/DataPortalSelector.cs index 1080c78263..9e23211e89 100644 --- a/Source/Csla/Server/DataPortalSelector.cs +++ b/Source/Csla/Server/DataPortalSelector.cs @@ -16,6 +16,11 @@ namespace Csla.Server /// public class DataPortalSelector : IDataPortalServer { + private readonly ApplicationContext _applicationContext; + private SimpleDataPortal SimpleDataPortal { get; } + private FactoryDataPortal FactoryDataPortal { get; } + private Configuration.DataPortalOptions DataPortalOptions { get; } + /// /// /// @@ -23,28 +28,16 @@ public class DataPortalSelector : IDataPortalServer /// /// /// + /// , , or is . public DataPortalSelector(ApplicationContext applicationContext, SimpleDataPortal simpleDataPortal, FactoryDataPortal factoryDataPortal, Configuration.DataPortalOptions dataPortalOptions) { - _applicationContext = applicationContext; - SimpleDataPortal = simpleDataPortal; - FactoryDataPortal = factoryDataPortal; - DataPortalOptions = dataPortalOptions; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); + SimpleDataPortal = simpleDataPortal ?? throw new ArgumentNullException(nameof(simpleDataPortal)); + FactoryDataPortal = factoryDataPortal ?? throw new ArgumentNullException(nameof(factoryDataPortal)); + DataPortalOptions = dataPortalOptions ?? throw new ArgumentNullException(nameof(dataPortalOptions)); } - private ApplicationContext _applicationContext; - private SimpleDataPortal SimpleDataPortal { get; set; } - private FactoryDataPortal FactoryDataPortal { get; set; } - private Configuration.DataPortalOptions DataPortalOptions { get; set; } - - /// - /// Create a new business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { try @@ -71,15 +64,7 @@ public async Task Create(Type objectType, object criteria, Dat } } - /// - /// Get an existing business object. - /// - /// Type of business object to retrieve. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { try @@ -111,14 +96,7 @@ public async Task Fetch(Type objectType, object criteria, Data } } - /// - /// Update a business object. - /// - /// Business object to update. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Update(object obj, DataPortalContext context, bool isSync) { try @@ -145,15 +123,7 @@ public async Task Update(object obj, DataPortalContext context } } - /// - /// Delete a business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { try diff --git a/Source/Csla/Server/DataPortalTarget.cs b/Source/Csla/Server/DataPortalTarget.cs index f2b95d923f..458c35ebc3 100644 --- a/Source/Csla/Server/DataPortalTarget.cs +++ b/Source/Csla/Server/DataPortalTarget.cs @@ -154,7 +154,7 @@ internal void MarkOld() private async Task InvokeOperationAsync(object criteria, bool isSync) where T : DataPortalOperationAttribute { - object[] parameters = DataPortal.GetCriteriaArray(criteria); + object?[] parameters = DataPortal.GetCriteriaArray(criteria)!; await CallMethodTryAsyncDI(isSync, parameters).ConfigureAwait(false); } @@ -163,7 +163,7 @@ public Task CreateAsync(object criteria, bool isSync) return InvokeOperationAsync(criteria, isSync); } - public Task CreateChildAsync(params object[] parameters) + public Task CreateChildAsync(params object?[] parameters) { return CallMethodTryAsyncDI(false, parameters); } @@ -173,7 +173,7 @@ public Task FetchAsync(object criteria, bool isSync) return InvokeOperationAsync(criteria, isSync); } - public Task FetchChildAsync(params object[] parameters) + public Task FetchChildAsync(params object?[] parameters) { return CallMethodTryAsyncDI(false, parameters); } @@ -221,7 +221,7 @@ public async Task UpdateAsync(bool isSync) } } - public async Task UpdateChildAsync(params object[] parameters) + public async Task UpdateChildAsync(params object?[] parameters) { // tell the business object to update itself if (Instance is BusinessBase busObj) diff --git a/Source/Csla/Server/DefaultDataPortalActivator.cs b/Source/Csla/Server/DefaultDataPortalActivator.cs index 36fcee452c..4302f86a7b 100644 --- a/Source/Csla/Server/DefaultDataPortalActivator.cs +++ b/Source/Csla/Server/DefaultDataPortalActivator.cs @@ -18,31 +18,30 @@ namespace Csla.Server /// Creates an instance of the type. /// /// + /// is . public class DefaultDataPortalActivator(IServiceProvider serviceProvider) : IDataPortalActivator { /// /// Gets a reference to the current DI service provider. /// - protected IServiceProvider ServiceProvider { get; } = serviceProvider; + protected IServiceProvider ServiceProvider { get; } = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - /// - /// Gets a new instance of the requested type. - /// - /// Requested type (class or interface). - /// Param array for the constructor - /// Instance of requested type + /// public virtual object CreateInstance(Type requestedType, params object[] parameters) { - object result; + if (requestedType is null) + throw new ArgumentNullException(nameof(requestedType)); + if (parameters is null) + throw new ArgumentNullException(nameof(parameters)); + var realType = ResolveType(requestedType); - if (ServiceProvider != null) - result = ActivatorUtilities.CreateInstance(ServiceProvider, realType, parameters); - else - result = Activator.CreateInstance(realType, parameters); + object result = ActivatorUtilities.CreateInstance(ServiceProvider, realType, parameters); + if (result is IUseApplicationContext tmp) { tmp.ApplicationContext = ServiceProvider.GetRequiredService(); } + InitializeInstance(result); return result; } diff --git a/Source/Csla/Server/DefaultExceptionInspector.cs b/Source/Csla/Server/DefaultExceptionInspector.cs index 6969375903..3028ae6750 100644 --- a/Source/Csla/Server/DefaultExceptionInspector.cs +++ b/Source/Csla/Server/DefaultExceptionInspector.cs @@ -13,17 +13,8 @@ namespace Csla.Server /// internal class DefaultExceptionInspector : IDataPortalExceptionInspector { - /// - /// Inspects the exception that occurred during DataPortal call - /// If you want to transform to/return another Exception to the client - /// you must throw the new Exception in this method. - /// - /// Type of the object. - /// The business object , if available. - /// The criteria. - /// Name of the method. - /// The exception. - public void InspectException(Type objectType, object businessObject, object criteria, string methodName, Exception ex) + /// + public void InspectException(Type objectType, object? businessObject, object? criteria, string methodName, Exception ex) { } } -} +} \ No newline at end of file diff --git a/Source/Csla/Server/EmptyCriteria.cs b/Source/Csla/Server/EmptyCriteria.cs index 951fec6217..f64feba342 100644 --- a/Source/Csla/Server/EmptyCriteria.cs +++ b/Source/Csla/Server/EmptyCriteria.cs @@ -19,7 +19,7 @@ public sealed class EmptyCriteria : Core.MobileObject /// /// Creates a new instance of the type. /// - public EmptyCriteria() { } + private EmptyCriteria() { } /// /// Gets an instance of EmptyCriteria diff --git a/Source/Csla/Server/FactoryDataPortal.cs b/Source/Csla/Server/FactoryDataPortal.cs index aeb1d4b6ad..c95626b104 100644 --- a/Source/Csla/Server/FactoryDataPortal.cs +++ b/Source/Csla/Server/FactoryDataPortal.cs @@ -18,6 +18,11 @@ namespace Csla.Server /// public class FactoryDataPortal : IDataPortalServer { + private readonly ApplicationContext _applicationContext; + private IObjectFactoryLoader FactoryLoader { get; } + private IDataPortalExceptionInspector ExceptionInspector { get; } + private DataPortalOptions DataPortalOptions { get; } + /// /// Creates an instance of the type. /// @@ -25,19 +30,15 @@ public class FactoryDataPortal : IDataPortalServer /// /// /// + /// , , or is . public FactoryDataPortal(ApplicationContext applicationContext, IObjectFactoryLoader factoryLoader, IDataPortalExceptionInspector inspector, DataPortalOptions dataPortalOptions) { - _applicationContext = applicationContext; - FactoryLoader = factoryLoader; - ExceptionInspector = inspector; - DataPortalOptions = dataPortalOptions; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); + FactoryLoader = factoryLoader ?? throw new ArgumentNullException(nameof(factoryLoader)); + ExceptionInspector = inspector ?? throw new ArgumentNullException(nameof(inspector)); + DataPortalOptions = dataPortalOptions ?? throw new ArgumentNullException(nameof(dataPortalOptions)); } - private ApplicationContext _applicationContext; - private IObjectFactoryLoader FactoryLoader { get; set; } - private IDataPortalExceptionInspector ExceptionInspector { get; set; } - private DataPortalOptions DataPortalOptions { get; set; } - #region Method invokes private async Task InvokeMethod(string factoryTypeName, DataPortalOperations operation, string methodName, Type objectType, DataPortalContext context, bool isSync) @@ -46,7 +47,7 @@ private async Task InvokeMethod(string factoryTypeName, DataPo var eventArgs = new DataPortalEventArgs(context, objectType, null, operation); Reflection.MethodCaller.CallMethodIfImplemented(factory, "Invoke", eventArgs); - object result; + object? result; try { Utilities.ThrowIfAsyncMethodOnSyncClient(_applicationContext, isSync, factory, methodName); @@ -66,7 +67,7 @@ private async Task InvokeMethod(string factoryTypeName, DataPo factory, "InvokeError", new DataPortalEventArgs(context, objectType, null, operation, ex)); throw; } - return new DataPortalResult(_applicationContext, result); + return new DataPortalResult(_applicationContext, result, null); } private async Task InvokeMethod(string factoryTypeName, DataPortalOperations operation, string methodName, Type objectType, object e, DataPortalContext context, bool isSync) @@ -75,7 +76,7 @@ private async Task InvokeMethod(string factoryTypeName, DataPo var eventArgs = new DataPortalEventArgs(context, objectType, e, operation); Reflection.MethodCaller.CallMethodIfImplemented(factory, "Invoke", eventArgs); - object result; + object? result; try { Utilities.ThrowIfAsyncMethodOnSyncClient(_applicationContext, isSync, factory, methodName, e); @@ -94,24 +95,25 @@ private async Task InvokeMethod(string factoryTypeName, DataPo Reflection.MethodCaller.CallMethodIfImplemented(factory, "InvokeError", new DataPortalEventArgs(context, objectType, e, operation, ex)); throw; } - return new DataPortalResult(_applicationContext, result); + return new DataPortalResult(_applicationContext, result, null); } #endregion #region IDataPortalServer Members - /// - /// Create a new business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (context.FactoryInfo is null) + throw new ArgumentException($"Internal: No {nameof(DataPortalContext.FactoryInfo)} provided for the {nameof(FactoryDataPortal)}. This is an internal bug."); + try { DataPortalResult result; @@ -130,20 +132,21 @@ public async Task Create(Type objectType, object criteria, Dat } } - /// - /// Get an existing business object. - /// - /// Type of business object to retrieve. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (context.FactoryInfo is null) + throw new ArgumentException($"Internal: No {nameof(DataPortalContext.FactoryInfo)} provided for the {nameof(FactoryDataPortal)}. This is an internal bug."); + if (typeof(Core.ICommandObject).IsAssignableFrom(objectType)) { - return await Execute(objectType, criteria, context, isSync); + return await Execute(objectType, criteria, context, isSync, context.FactoryInfo); } try @@ -164,36 +167,37 @@ public async Task Fetch(Type objectType, object criteria, Data } } - private async Task Execute(Type objectType, object criteria, DataPortalContext context, bool isSync) + private async Task Execute(Type objectType, object criteria, DataPortalContext context, bool isSync, ObjectFactoryAttribute factoryInfo) { try { DataPortalResult result; if (criteria is EmptyCriteria) - result = await InvokeMethod(context.FactoryInfo.FactoryTypeName, DataPortalOperations.Execute, context.FactoryInfo.ExecuteMethodName, objectType, context, isSync).ConfigureAwait(false); + result = await InvokeMethod(factoryInfo.FactoryTypeName, DataPortalOperations.Execute, factoryInfo.ExecuteMethodName, objectType, context, isSync).ConfigureAwait(false); else - result = await InvokeMethod(context.FactoryInfo.FactoryTypeName, DataPortalOperations.Execute, context.FactoryInfo.ExecuteMethodName, objectType, criteria, context, isSync).ConfigureAwait(false); + result = await InvokeMethod(factoryInfo.FactoryTypeName, DataPortalOperations.Execute, factoryInfo.ExecuteMethodName, objectType, criteria, context, isSync).ConfigureAwait(false); return result; } catch (Exception ex) { throw DataPortal.NewDataPortalException( - _applicationContext, context.FactoryInfo.ExecuteMethodName + " " + Resources.FailedOnServer, - new DataPortalExceptionHandler(ExceptionInspector).InspectException(objectType, criteria, context.FactoryInfo.ExecuteMethodName, ex), + _applicationContext, factoryInfo.ExecuteMethodName + " " + Resources.FailedOnServer, + new DataPortalExceptionHandler(ExceptionInspector).InspectException(objectType, criteria, factoryInfo.ExecuteMethodName, ex), null, DataPortalOptions); } } - /// - /// Update a business object. - /// - /// Business object to update. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Update(object obj, DataPortalContext context, bool isSync) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (context.FactoryInfo is null) + throw new ArgumentException($"Internal: No {nameof(DataPortalContext.FactoryInfo)} provided for the {nameof(FactoryDataPortal)}. This is an internal bug."); + + string methodName = string.Empty; try { @@ -216,17 +220,18 @@ public async Task Update(object obj, DataPortalContext context } } - /// - /// Delete a business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// public async Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (context.FactoryInfo is null) + throw new ArgumentException($"Internal: No {nameof(DataPortalContext.FactoryInfo)} provided for the {nameof(FactoryDataPortal)}. This is an internal bug."); + try { DataPortalResult result; diff --git a/Source/Csla/Server/GenericBusinessException.cs b/Source/Csla/Server/GenericBusinessException.cs index e634b59e7f..afe25b556a 100644 --- a/Source/Csla/Server/GenericBusinessException.cs +++ b/Source/Csla/Server/GenericBusinessException.cs @@ -43,7 +43,7 @@ public class GenericBusinessException : Exception /// /// Initializes a new instance of the class. - /// Reads information for a NonSerializable excpeption into GenericBusinessException + /// Reads information for a NonSerializable exception into GenericBusinessException /// /// The wrapped exception. public GenericBusinessException(Exception wrappedException) @@ -52,7 +52,7 @@ public GenericBusinessException(Exception wrappedException) Source = wrappedException.Source; HelpLink = wrappedException.HelpLink; Data = wrappedException.Data; - StackTrace = wrappedException.StackTrace; + StackTrace = wrappedException.StackTrace ?? ""; TypeName = wrappedException.GetType().ToString(); } @@ -67,7 +67,7 @@ public GenericBusinessException(Exception wrappedException, Exception innerExcep Source = wrappedException.Source; HelpLink = wrappedException.HelpLink; Data = wrappedException.Data; - StackTrace = wrappedException.StackTrace; + StackTrace = wrappedException.StackTrace ?? ""; TypeName = wrappedException.GetType().ToString(); } diff --git a/Source/Csla/Server/Hosts/DataPortalChannel/CriteriaRequest.cs b/Source/Csla/Server/Hosts/DataPortalChannel/CriteriaRequest.cs index eb6d48c2f9..7ffd8a4971 100644 --- a/Source/Csla/Server/Hosts/DataPortalChannel/CriteriaRequest.cs +++ b/Source/Csla/Server/Hosts/DataPortalChannel/CriteriaRequest.cs @@ -6,10 +6,12 @@ // Message sent to the server //----------------------------------------------------------------------- +using Csla.Serialization.Mobile; + namespace Csla.Server.Hosts.DataPortalChannel { /// - /// Message sent to the WCF data portal. + /// Message sent to the data portal. /// [Serializable] public class CriteriaRequest : ReadOnlyBase @@ -24,18 +26,18 @@ public class CriteriaRequest : ReadOnlyBase /// Assembly qualified name of the /// business object type to create. /// + /// value is . public string TypeName { get { return GetProperty(TypeNameProperty); } - set { LoadProperty(TypeNameProperty, value); } + set { LoadProperty(TypeNameProperty, value ?? throw new ArgumentNullException(nameof(TypeName))); } } /// /// Serialized data for the criteria object. /// - public static readonly PropertyInfo CriteriaDataProperty = RegisterProperty(c => c.CriteriaData); + public static readonly PropertyInfo CriteriaDataProperty = RegisterProperty(nameof(CriteriaData)); -#nullable enable /// /// Serialized data for the criteria object. /// @@ -44,66 +46,95 @@ public byte[]? CriteriaData get { return GetProperty(CriteriaDataProperty); } set { LoadProperty(CriteriaDataProperty, value); } } -#nullable disable /// /// Serialized data for the principal object. /// - public static readonly PropertyInfo PrincipalProperty = RegisterProperty(c => c.Principal); + public static readonly PropertyInfo PrincipalProperty = RegisterProperty(nameof(Principal)); /// /// Serialized data for the principal object. /// + /// value is . public byte[] Principal { get { return GetProperty(PrincipalProperty); } - set { LoadProperty(PrincipalProperty, value); } + set { LoadProperty(PrincipalProperty, value ?? throw new ArgumentNullException(nameof(Principal))); } } /// /// Serialized data for the client context object. /// - public static readonly PropertyInfo ClientContextProperty = RegisterProperty(c => c.ClientContext); + public static readonly PropertyInfo ClientContextProperty = RegisterProperty(nameof(ClientContext)); /// /// Serialized data for the client context object. /// + /// value is . public byte[] ClientContext { get { return GetProperty(ClientContextProperty); } - set { LoadProperty(ClientContextProperty, value); } + set { LoadProperty(ClientContextProperty, value ?? throw new ArgumentNullException(nameof(ClientContext))); } } /// /// Serialized client culture. /// /// The client culture. - public static readonly PropertyInfo ClientCultureProperty = RegisterProperty(c => c.ClientCulture); + public static readonly PropertyInfo ClientCultureProperty = RegisterProperty(nameof(ClientCulture)); /// /// Serialized client culture. /// /// The client culture. + /// value is . public string ClientCulture { get { return GetProperty(ClientCultureProperty); } - set { LoadProperty(ClientCultureProperty, value); } + set { LoadProperty(ClientCultureProperty, value ?? throw new ArgumentNullException(nameof(ClientCulture))); } } /// /// Serialized client UI culture. /// /// The client UI culture. - public static readonly PropertyInfo ClientUICultureProperty = RegisterProperty(c => c.ClientUICulture); + public static readonly PropertyInfo ClientUICultureProperty = RegisterProperty(nameof(ClientUICulture)); /// /// Serialized client UI culture. /// /// The client UI culture. + /// value is . public string ClientUICulture { get { return GetProperty(ClientUICultureProperty); } - set { LoadProperty(ClientUICultureProperty, value); } + set { LoadProperty(ClientUICultureProperty, value ?? throw new ArgumentNullException(nameof(ClientUICulture))); } + } + + /// + /// Initializes a new instance of -object. + /// + /// + /// + /// + /// + /// , , or is . + public CriteriaRequest(byte[] principal, byte[] clientContext, string clientCulture, string clientUICulture) + { + Principal = principal ?? throw new ArgumentNullException(nameof(principal)); + ClientContext = clientContext ?? throw new ArgumentNullException(nameof(clientContext)); + ClientCulture = clientCulture ?? throw new ArgumentNullException(nameof(clientCulture)); + ClientUICulture = clientUICulture ?? throw new ArgumentNullException(nameof(clientUICulture)); + TypeName = string.Empty; + CriteriaData = null; + } + + /// + /// Initializes an empty instance for . + /// + [Obsolete(MobileFormatter.DefaultCtorObsoleteMessage, error: true)] + public CriteriaRequest() + { } } } \ No newline at end of file diff --git a/Source/Csla/Server/Hosts/DataPortalChannel/DataPortalErrorInfo.cs b/Source/Csla/Server/Hosts/DataPortalChannel/DataPortalErrorInfo.cs index fb3994133c..089a1c902b 100644 --- a/Source/Csla/Server/Hosts/DataPortalChannel/DataPortalErrorInfo.cs +++ b/Source/Csla/Server/Hosts/DataPortalChannel/DataPortalErrorInfo.cs @@ -6,6 +6,8 @@ // Message containing details about any //----------------------------------------------------------------------- +using Csla.Serialization.Mobile; + namespace Csla.Server.Hosts.DataPortalChannel { /// @@ -107,16 +109,20 @@ public DataPortalErrorInfo InnerError /// Creates an instance of the type. /// /// Current ApplicationContext - /// - /// The Exception to encapusulate. - /// + /// The Exception to encapsulate. + /// or is . public DataPortalErrorInfo(ApplicationContext applicationContext, Exception ex) { + if (applicationContext is null) + throw new ArgumentNullException(nameof(applicationContext)); + if (ex is null) + throw new ArgumentNullException(nameof(ex)); + ApplicationContext = applicationContext; - ExceptionTypeName = ex.GetType().FullName; + ExceptionTypeName = ex.GetType().FullName!; Message = ex.Message; - StackTrace = ex.StackTrace; - Source = ex.Source; + StackTrace = ex.StackTrace ?? string.Empty; + Source = ex.Source ?? string.Empty; if (ex.InnerException != null) InnerError = ApplicationContext.CreateInstance(ApplicationContext, ex.InnerException); } @@ -124,6 +130,7 @@ public DataPortalErrorInfo(ApplicationContext applicationContext, Exception ex) /// /// Creates an empty instance of the type. /// + [Obsolete(MobileFormatter.DefaultCtorObsoleteMessage, error: true)] public DataPortalErrorInfo() { } } diff --git a/Source/Csla/Server/Hosts/DataPortalChannel/DataPortalResponse.cs b/Source/Csla/Server/Hosts/DataPortalChannel/DataPortalResponse.cs index f24897b0dc..3df2cb12b3 100644 --- a/Source/Csla/Server/Hosts/DataPortalChannel/DataPortalResponse.cs +++ b/Source/Csla/Server/Hosts/DataPortalChannel/DataPortalResponse.cs @@ -6,6 +6,8 @@ // Response message for returning //----------------------------------------------------------------------- +using Csla.Serialization.Mobile; + namespace Csla.Server.Hosts.DataPortalChannel { /// @@ -37,10 +39,29 @@ public DataPortalErrorInfo? ErrorData /// /// Serialized business object data returned from the server (deserialize with MobileFormatter). /// + /// value is . public byte[] ObjectData { get { return GetProperty(ObjectDataProperty); } - set { LoadProperty(ObjectDataProperty, value); } + set { LoadProperty(ObjectDataProperty, value ?? throw new ArgumentNullException(nameof(ObjectData))); } + } + + /// + /// Initializes a new instance of -object. + /// + /// + /// is . + public DataPortalResponse(byte[] objectData) + { + ObjectData = objectData ?? throw new ArgumentNullException(nameof(objectData)); + } + + /// + /// Initializes an empty instance for . + /// + [Obsolete(MobileFormatter.DefaultCtorObsoleteMessage, error: true)] + public DataPortalResponse() + { } } } \ No newline at end of file diff --git a/Source/Csla/Server/Hosts/DataPortalChannel/UpdateRequest.cs b/Source/Csla/Server/Hosts/DataPortalChannel/UpdateRequest.cs index 5b81b60479..8e76a57006 100644 --- a/Source/Csla/Server/Hosts/DataPortalChannel/UpdateRequest.cs +++ b/Source/Csla/Server/Hosts/DataPortalChannel/UpdateRequest.cs @@ -6,6 +6,8 @@ // Request message for updating //----------------------------------------------------------------------- +using Csla.Serialization.Mobile; + namespace Csla.Server.Hosts.DataPortalChannel { /// @@ -37,10 +39,11 @@ public byte[]? ObjectData /// /// Serialized data for the principal object. /// + /// value is . public byte[] Principal { get { return GetProperty(PrincipalProperty); } - set { LoadProperty(PrincipalProperty, value); } + set { LoadProperty(PrincipalProperty, value ?? throw new ArgumentNullException(nameof(Principal))); } } /// @@ -51,10 +54,11 @@ public byte[] Principal /// /// Serialized data for the client context object. /// + /// value is . public byte[] ClientContext { get { return GetProperty(ClientContextProperty); } - set { LoadProperty(ClientContextProperty, value); } + set { LoadProperty(ClientContextProperty, value ?? throw new ArgumentNullException(nameof(ClientContext))); } } /// @@ -67,10 +71,11 @@ public byte[] ClientContext /// Serialized client culture. /// /// The client culture. + /// value is . public string ClientCulture { get { return GetProperty(ClientCultureProperty); } - set { LoadProperty(ClientCultureProperty, value); } + set { LoadProperty(ClientCultureProperty, value ?? throw new ArgumentNullException(nameof(ClientCulture))); } } /// @@ -83,10 +88,36 @@ public string ClientCulture /// Serialized client UI culture. /// /// The client UI culture. + /// value is . public string ClientUICulture { get { return GetProperty(ClientUICultureProperty); } - set { LoadProperty(ClientUICultureProperty, value); } + set { LoadProperty(ClientUICultureProperty, value ?? throw new ArgumentNullException(nameof(ClientUICulture))); } + } + + /// + /// Initializes a new instance of -object. + /// + /// + /// + /// + /// + /// , , or is . + public UpdateRequest(byte[] principal, byte[] clientContext, string clientCulture, string clientUICulture) + { + Principal = principal ?? throw new ArgumentNullException(nameof(principal)); + ClientContext = clientContext ?? throw new ArgumentNullException(nameof(clientContext)); + ClientCulture = clientCulture ?? throw new ArgumentNullException(nameof(clientCulture)); + ClientUICulture = clientUICulture ?? throw new ArgumentNullException(nameof(clientUICulture)); + ObjectData = null; + } + + /// + /// Initializes an empty instance for . + /// + [Obsolete(MobileFormatter.DefaultCtorObsoleteMessage, error: true)] + public UpdateRequest() + { } } } \ No newline at end of file diff --git a/Source/Csla/Server/IAuthorizeDataPortal.cs b/Source/Csla/Server/IAuthorizeDataPortal.cs index a9e72a01d3..3625540702 100644 --- a/Source/Csla/Server/IAuthorizeDataPortal.cs +++ b/Source/Csla/Server/IAuthorizeDataPortal.cs @@ -24,6 +24,7 @@ public interface IAuthorizeDataPortal /// /// The cancellation token. /// + /// is . Task AuthorizeAsync(AuthorizeRequest clientRequest, CancellationToken ct); } } \ No newline at end of file diff --git a/Source/Csla/Server/IDataPortalActivator.cs b/Source/Csla/Server/IDataPortalActivator.cs index c62cc2a68a..3a8aa21f9a 100644 --- a/Source/Csla/Server/IDataPortalActivator.cs +++ b/Source/Csla/Server/IDataPortalActivator.cs @@ -20,23 +20,27 @@ public interface IDataPortalActivator /// Requested business type (class or interface). /// Param array for the constructor /// Business object instance. + /// or is . object CreateInstance(Type requestedType, params object[] parameters); /// /// Initializes an existing business object instance. /// /// Reference to the business object. + /// is . void InitializeInstance(object obj); /// /// Finalizes an existing business object instance. Called /// after a data portal operation is complete. /// /// Reference to the business object. + /// is . void FinalizeInstance(object obj); /// /// Gets the actual business domain class type based on the /// requested type (which might be an interface). /// /// Type requested from the data portal. + /// is . Type ResolveType(Type requestedType); } } diff --git a/Source/Csla/Server/IDataPortalExceptionInspector.cs b/Source/Csla/Server/IDataPortalExceptionInspector.cs index f37708d781..23d6cf0795 100644 --- a/Source/Csla/Server/IDataPortalExceptionInspector.cs +++ b/Source/Csla/Server/IDataPortalExceptionInspector.cs @@ -15,16 +15,18 @@ namespace Csla.Server /// public interface IDataPortalExceptionInspector { - /// - /// Inspects the exception that occurred during DataPortal call - /// If you want to transform to/return another Exception to the client - /// you must throw the new Exception in this method. - /// - /// Type of the object. - /// The business object , if available. - /// The criteria. - /// Name of the method. - /// The exception. - void InspectException(Type objectType, object businessObject, object criteria, string methodName, Exception ex); + /// + /// Inspects the exception that occurred during DataPortal call + /// If you want to transform to/return another Exception to the client + /// you must throw the new Exception in this method. + /// + /// Type of the object. + /// The business object , if available. + /// The criteria. + /// Name of the method. + /// The exception. + /// or is . + /// is , or only consists of white spaces. + void InspectException(Type objectType, object? businessObject, object? criteria, string methodName, Exception ex); } } \ No newline at end of file diff --git a/Source/Csla/Server/IDataPortalServer.cs b/Source/Csla/Server/IDataPortalServer.cs index 83157141ed..ee81a3e556 100644 --- a/Source/Csla/Server/IDataPortalServer.cs +++ b/Source/Csla/Server/IDataPortalServer.cs @@ -23,8 +23,8 @@ public interface IDataPortalServer /// object passed to the server. /// /// True if the client-side proxy should synchronously invoke the server. - /// or is . - Task Create(Type objectType, object? criteria, DataPortalContext context, bool isSync); + /// , or is . + Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync); /// /// Get an existing business object. /// @@ -34,8 +34,8 @@ public interface IDataPortalServer /// object passed to the server. /// /// True if the client-side proxy should synchronously invoke the server. - /// or is . - Task Fetch(Type objectType, object? criteria, DataPortalContext context, bool isSync); + /// , or is . + Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync); /// /// Update a business object. /// @@ -55,7 +55,7 @@ public interface IDataPortalServer /// object passed to the server. /// /// True if the client-side proxy should synchronously invoke the server. - /// or is . - Task Delete(Type objectType, object? criteria, DataPortalContext context, bool isSync); + /// , or is . + Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync); } } \ No newline at end of file diff --git a/Source/Csla/Server/IInterceptDataPortal.cs b/Source/Csla/Server/IInterceptDataPortal.cs index 96836f783b..97749abe20 100644 --- a/Source/Csla/Server/IInterceptDataPortal.cs +++ b/Source/Csla/Server/IInterceptDataPortal.cs @@ -15,6 +15,7 @@ public interface IInterceptDataPortal /// authorization. /// /// + /// is . Task InitializeAsync(InterceptArgs e); /// /// Invoked at the end of each server-side @@ -22,6 +23,7 @@ public interface IInterceptDataPortal /// and exception scenarios. /// /// + /// is . void Complete(InterceptArgs e); } @@ -34,34 +36,34 @@ public class InterceptArgs /// /// Gets or sets the business object type. /// - public Type ObjectType { get; set; } + public Type ObjectType { get; } /// /// Gets or sets the criteria or business - /// object paramter provided to the + /// object parameter provided to the /// data portal from the client. /// - public object Parameter { get; set; } + public object? Parameter { get; } /// /// Gets or sets the business object /// resulting from the data portal /// operation. /// - public DataPortalResult Result { get; set; } + public DataPortalResult? Result { get; } /// /// Gets or sets the exception that occurred /// during data portal processing. /// - public Exception Exception { get; set; } + public Exception? Exception { get; } /// /// Gets or sets the data portal operation /// being performed. /// - public DataPortalOperations Operation { get; set; } + public DataPortalOperations Operation { get; } /// /// Gets or sets a value indicating whether /// the data portal was invoked synchronously. /// - public bool IsSync { get; set; } + public bool IsSync { get; } /// /// Gets or sets a value containing the elapsed @@ -69,5 +71,49 @@ public class InterceptArgs /// of operation). /// public TimeSpan Runtime { get; set; } + + /// + /// Initializes a new instance of -object. + /// + /// + /// + /// + /// + /// + /// or is . + public InterceptArgs(Type objectType, object? parameter, DataPortalResult result, DataPortalOperations operation, bool isSync) : this(objectType, parameter, operation, isSync) + { + Result = result ?? throw new ArgumentNullException(nameof(result)); + } + + /// + /// Initializes a new instance of -object. + /// + /// + /// + /// + /// + /// + /// or is . + public InterceptArgs(Type objectType, object? parameter, Exception exception, DataPortalOperations operation, bool isSync) : this(objectType, parameter, operation, isSync) + { + Exception = exception ?? throw new ArgumentNullException(nameof(exception)); + } + + /// + /// Initializes a new instance of -object. + /// + /// + /// + /// + /// + /// is . + public InterceptArgs(Type objectType, object? parameter, DataPortalOperations operation, bool isSync) + { + ObjectType = objectType ?? throw new ArgumentNullException(nameof(objectType)); + Parameter = parameter; + Operation = operation; + IsSync = isSync; + } } } diff --git a/Source/Csla/Server/IObjectFactoryLoader.cs b/Source/Csla/Server/IObjectFactoryLoader.cs index b895b2216f..d43a956bd9 100644 --- a/Source/Csla/Server/IObjectFactoryLoader.cs +++ b/Source/Csla/Server/IObjectFactoryLoader.cs @@ -23,6 +23,7 @@ public interface IObjectFactoryLoader /// Name of the factory to create, typically /// an assembly qualified type name. /// + /// is , or only consists of white spaces. Type GetFactoryType(string factoryName); /// /// Returns an ObjectFactory object. @@ -31,6 +32,7 @@ public interface IObjectFactoryLoader /// Name of the factory to create, typically /// an assembly qualified type name. /// + /// is , or only consists of white spaces. object GetFactory(string factoryName); } } \ No newline at end of file diff --git a/Source/Csla/Server/Interceptors/InterceptorManager.cs b/Source/Csla/Server/Interceptors/InterceptorManager.cs index 576b4baa54..9dc61f561f 100644 --- a/Source/Csla/Server/Interceptors/InterceptorManager.cs +++ b/Source/Csla/Server/Interceptors/InterceptorManager.cs @@ -6,10 +6,11 @@ // Cascades DataPortal interception requests to registered interceptors //----------------------------------------------------------------------- + namespace Csla.Server { /// - /// Manage dataportal interception using DI-registered implementations + /// Manage data portal interception using DI-registered implementations /// public class InterceptorManager { @@ -19,29 +20,33 @@ public class InterceptorManager /// Creation of the manager, including all interceptors registered with the DI container /// /// The IEnumerable of interceptors provided by DI + /// is . public InterceptorManager(IEnumerable interceptors) { + if (interceptors is null) + throw new ArgumentNullException(nameof(interceptors)); + _interceptors = new List(interceptors); } - /// - /// Cascade the initial interception request prior to the main DataPortal operation - /// - /// The interception arguments provided by the consumer + /// public async Task InitializeAsync(InterceptArgs e) { + if (e is null) + throw new ArgumentNullException(nameof(e)); + foreach (IInterceptDataPortal interceptor in _interceptors) { await interceptor.InitializeAsync(e); } } - /// - /// Cascade the final interception request after the main DataPortal operation has completed - /// - /// The interception arguments provided by the consumer + /// public void Complete(InterceptArgs e) { + if (e is null) + throw new ArgumentNullException(nameof(e)); + // Iterate backwards through interceptors, so that they appear to wrap one another, decorator-style for (int interceptorIndex = _interceptors.Count - 1; interceptorIndex > -1; interceptorIndex--) { diff --git a/Source/Csla/Server/Interceptors/ServerSide/RevalidatingInterceptor.cs b/Source/Csla/Server/Interceptors/ServerSide/RevalidatingInterceptor.cs index 7884bd6adb..f63614090b 100644 --- a/Source/Csla/Server/Interceptors/ServerSide/RevalidatingInterceptor.cs +++ b/Source/Csla/Server/Interceptors/ServerSide/RevalidatingInterceptor.cs @@ -23,9 +23,10 @@ public class RevalidatingInterceptor : IInterceptDataPortal /// Public constructor, intended to be executed by DI /// /// The context under which the DataPortal operation is executing + /// is . public RevalidatingInterceptor(ApplicationContext applicationContext) { - _applicationContext = applicationContext; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); } /// @@ -33,19 +34,23 @@ public RevalidatingInterceptor(ApplicationContext applicationContext) /// /// The interception arguments from the DataPortal /// + /// is . public async Task InitializeAsync(InterceptArgs e) { - ITrackStatus checkableObject; - if (_applicationContext.ExecutionLocation != ApplicationContext.ExecutionLocations.Server || _applicationContext.LogicalExecutionLocation != ApplicationContext.LogicalExecutionLocations.Server) { return; } - checkableObject = e.Parameter as ITrackStatus; - if (checkableObject is null) return; + if (e is null) + throw new ArgumentNullException(nameof(e)); + if (e.Parameter is not ITrackStatus checkableObject) + { + return; + } + await RevalidateObjectAsync(checkableObject); if (!checkableObject.IsValid) { @@ -53,10 +58,7 @@ public async Task InitializeAsync(InterceptArgs e) } } - /// - /// Interception handler run after a DataPortal operation - /// - /// The interception arguments from the DataPortal + /// public void Complete(InterceptArgs e) { } @@ -65,8 +67,13 @@ public void Complete(InterceptArgs e) /// Perform revalidation of business rules on any supporting type /// /// The parameter that was passed to the DataPortal as part of the operation - private async Task RevalidateObjectAsync(object parameter) + private async Task RevalidateObjectAsync(object? parameter) { + if (parameter is null) + { + return; + } + if (parameter is IEnumerable list) { // Handle the object being a collection @@ -90,7 +97,12 @@ private async Task RevalidateObjectAsync(object parameter) var properties = fieldHolder.FieldManager.GetRegisteredProperties(); foreach (var property in properties.Where(r=>r.IsChild && fieldHolder.FieldManager.FieldExists(r))) { - await RevalidateObjectAsync(fieldHolder.FieldManager.GetFieldData(property).Value); + var fieldData = fieldHolder.FieldManager.GetFieldData(property); + if (fieldData is null) + { + continue; + } + await RevalidateObjectAsync(fieldData.Value); } } else if (parameter is IManageProperties propertyHolder) diff --git a/Source/Csla/Server/MobileFactoryAttribute.cs b/Source/Csla/Server/MobileFactoryAttribute.cs index 7a7749f22b..d79f8fd1ac 100644 --- a/Source/Csla/Server/MobileFactoryAttribute.cs +++ b/Source/Csla/Server/MobileFactoryAttribute.cs @@ -23,7 +23,7 @@ public class MobileFactoryAttribute : Attribute /// Factory class must have a parameterless /// default constructor. /// - public string FactoryTypeName { get; private set; } + public string FactoryTypeName { get; } /// /// Name of the method to call for a create operation. /// @@ -31,7 +31,7 @@ public class MobileFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string CreateMethodName { get; private set; } + public string CreateMethodName { get; } /// /// Name of the method to call for a fetch operation. /// @@ -39,7 +39,7 @@ public class MobileFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string FetchMethodName { get; private set; } + public string FetchMethodName { get; } /// /// Name of the method to call for a update operation. /// @@ -47,7 +47,7 @@ public class MobileFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string UpdateMethodName { get; private set; } + public string UpdateMethodName { get; } /// /// Name of the method to call for a delete operation. /// @@ -55,7 +55,7 @@ public class MobileFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string DeleteMethodName { get; private set; } + public string DeleteMethodName { get; } /// /// Name of the method to call for a execute operation. /// @@ -63,7 +63,7 @@ public class MobileFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string ExecuteMethodName { get; private set; } + public string ExecuteMethodName { get; } /// /// Creates an instance of the attribute. @@ -75,14 +75,8 @@ public class MobileFactoryAttribute : Attribute /// The default names for the factory methods are /// Create(), Fetch(), Update() and Delete(). /// - public MobileFactoryAttribute(string factoryType) + public MobileFactoryAttribute(string factoryType) : this(factoryType, "Fetch") { - FactoryTypeName = factoryType; - CreateMethodName = "Create"; - FetchMethodName = "Fetch"; - UpdateMethodName = "Update"; - DeleteMethodName = "Delete"; - ExecuteMethodName = "Execute"; } /// @@ -91,19 +85,11 @@ public MobileFactoryAttribute(string factoryType) /// /// Assembly qualified type name of the factory object. /// - /// - /// Name of the method to call for a create operation. /// /// Name of the method to call for a fetch operation. /// - public MobileFactoryAttribute(string factoryType, string createMethod, string fetchMethod) + public MobileFactoryAttribute(string factoryType, string fetchMethod) : this(factoryType, "Create", fetchMethod) { - FactoryTypeName = factoryType; - CreateMethodName = createMethod; - FetchMethodName = fetchMethod; - UpdateMethodName = "Update"; - DeleteMethodName = "Delete"; - ExecuteMethodName = "Execute"; } /// @@ -112,17 +98,13 @@ public MobileFactoryAttribute(string factoryType, string createMethod, string fe /// /// Assembly qualified type name of the factory object. /// + /// + /// Name of the method to call for a create operation. /// /// Name of the method to call for a fetch operation. /// - public MobileFactoryAttribute(string factoryType, string fetchMethod) + public MobileFactoryAttribute(string factoryType, string createMethod, string fetchMethod) : this(factoryType, createMethod, fetchMethod, "Update", "Delete") { - FactoryTypeName = factoryType; - FetchMethodName = fetchMethod; - CreateMethodName = "Create"; - UpdateMethodName = "Update"; - DeleteMethodName = "Delete"; - ExecuteMethodName = "Execute"; } /// @@ -140,15 +122,8 @@ public MobileFactoryAttribute(string factoryType, string fetchMethod) /// Name of the method to call for a update operation. /// /// Name of the method to call for a delete operation. - public MobileFactoryAttribute( - string factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod) + public MobileFactoryAttribute(string factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod) : this(factoryType, createMethod, fetchMethod, updateMethod, deleteMethod, "Execute") { - FactoryTypeName = factoryType; - CreateMethodName = createMethod; - FetchMethodName = fetchMethod; - UpdateMethodName = updateMethod; - DeleteMethodName = deleteMethod; - ExecuteMethodName = "Execute"; } /// @@ -168,8 +143,7 @@ public MobileFactoryAttribute( /// Name of the method to call for a delete operation. /// /// Name of the method to call for a execute operation. - public MobileFactoryAttribute( - string factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod, string executeMethod) + public MobileFactoryAttribute(string factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod, string executeMethod) { FactoryTypeName = factoryType; CreateMethodName = createMethod; diff --git a/Source/Csla/Server/NullAuthorizer.cs b/Source/Csla/Server/NullAuthorizer.cs index 89401ffd79..58adaa21a6 100644 --- a/Source/Csla/Server/NullAuthorizer.cs +++ b/Source/Csla/Server/NullAuthorizer.cs @@ -13,16 +13,7 @@ namespace Csla.Server /// public class NullAuthorizer : IAuthorizeDataPortal { - /// - /// Checks authorization rules for the request. - /// - /// - /// Client request information. - /// - /// - /// The cancellation token. - /// - /// A task representing the asynchronous operation. + /// public Task AuthorizeAsync(AuthorizeRequest clientRequest, CancellationToken ct) { return Task.CompletedTask; diff --git a/Source/Csla/Server/ObjectFactory.cs b/Source/Csla/Server/ObjectFactory.cs index eb93591cf9..e883c09aba 100644 --- a/Source/Csla/Server/ObjectFactory.cs +++ b/Source/Csla/Server/ObjectFactory.cs @@ -39,8 +39,12 @@ public ObjectFactory(ApplicationContext? applicationContext) /// /// Object on which to operate. /// New value for IsReadOnly. + /// is . protected void SetIsReadOnly(object obj, bool value) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (obj is IReadOnlyBindingList list) list.IsReadOnly = value; } @@ -52,8 +56,12 @@ protected void SetIsReadOnly(object obj, bool value) /// /// Object on which to call the method. /// + /// is . protected void CheckRules(object obj) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (obj is IDataPortalTarget target) target.CheckRules(); else @@ -67,8 +75,12 @@ protected void CheckRules(object obj) /// /// Object on which to call the method. /// + /// is . protected async Task CheckRulesAsync(object obj) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (obj is IDataPortalTarget target) await target.CheckRulesAsync().ConfigureAwait(false); else @@ -80,8 +92,12 @@ protected async Task CheckRulesAsync(object obj) /// /// Object on which to call the method. /// void + /// is . protected async Task WaitForIdle(object obj) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + var cslaOptions = ApplicationContext.GetRequiredService(); await WaitForIdle(obj, TimeSpan.FromSeconds(cslaOptions.DefaultWaitForIdleTimeoutInSeconds).ToCancellationToken()).ConfigureAwait(false); } @@ -92,8 +108,12 @@ protected async Task WaitForIdle(object obj) /// Object on which to call the method. /// The cancellation token. /// void + /// is . protected async Task WaitForIdle(object obj, CancellationToken ct) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (obj is IDataPortalTarget target) { await target.WaitForIdle(ct).ConfigureAwait(false); @@ -115,8 +135,12 @@ protected async Task WaitForIdle(object obj, CancellationToken ct) /// /// Object on which to call the method. /// + /// is . protected void MarkOld(object obj) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (obj is IDataPortalTarget target) target.MarkOld(); else @@ -130,8 +154,12 @@ protected void MarkOld(object obj) /// /// Object on which to call the method. /// + /// is . protected void MarkNew(object obj) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (obj is IDataPortalTarget target) target.MarkNew(); else @@ -145,8 +173,12 @@ protected void MarkNew(object obj) /// /// Object on which to call the method. /// + /// is . protected void MarkAsChild(object obj) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (obj is IDataPortalTarget target) target.MarkAsChild(); else @@ -172,8 +204,14 @@ protected void MarkAsChild(object obj) /// Loading values does not cause validation rules to be /// invoked. /// - protected void LoadProperty

(object obj, PropertyInfo

propertyInfo, P newValue) + /// or is . + protected void LoadProperty

(object obj, PropertyInfo

propertyInfo, P? newValue) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (propertyInfo is null) + throw new ArgumentNullException(nameof(propertyInfo)); + if (obj is IManageProperties target) target.LoadProperty

(propertyInfo, newValue); else @@ -218,8 +256,14 @@ protected void LoadProperty(object obj, IPropertyInfo propertyInfo, object? newV /// /// No authorization checks occur when this method is called. /// - protected P ReadProperty

(object obj, PropertyInfo

propertyInfo) + /// or is . + protected P? ReadProperty

(object obj, PropertyInfo

propertyInfo) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (propertyInfo is null) + throw new ArgumentNullException(nameof(propertyInfo)); + if (obj is IManageProperties target) return target.ReadProperty(propertyInfo); else @@ -234,8 +278,14 @@ protected P ReadProperty

(object obj, PropertyInfo

propertyInfo) /// /// No authorization checks occur when this method is called. /// - protected object ReadProperty(object obj, IPropertyInfo propertyInfo) + /// or is . + protected object? ReadProperty(object obj, IPropertyInfo propertyInfo) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (propertyInfo is null) + throw new ArgumentNullException(nameof(propertyInfo)); + if (obj is IManageProperties target) return target.ReadProperty(propertyInfo); else @@ -257,8 +307,12 @@ protected object ReadProperty(object obj, IPropertyInfo propertyInfo) /// bypassing of normal authorization checks during /// property setting. /// + /// is . protected IDisposable BypassPropertyChecks(Csla.Core.BusinessBase businessObject) { + if (businessObject is null) + throw new ArgumentNullException(nameof(businessObject)); + return businessObject.BypassPropertyChecks; } @@ -268,8 +322,14 @@ protected IDisposable BypassPropertyChecks(Csla.Core.BusinessBase businessObject ///

/// Business object. /// Property info object. + /// or is . protected bool FieldExists(object obj, Csla.Core.IPropertyInfo property) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (property is null) + throw new ArgumentNullException(nameof(property)); + if (obj is IManageProperties target) return target.FieldExists(property); else @@ -280,10 +340,14 @@ protected bool FieldExists(object obj, Csla.Core.IPropertyInfo property) /// Gets the list of deleted items from /// an editable collection. ///
- /// Type of child objects in the colletion. + /// Type of child objects in the collection. /// Editable collection object. + /// is . protected Csla.Core.MobileList GetDeletedList(object obj) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (obj is IEditableCollection target) return (Csla.Core.MobileList)target.GetDeletedList(); else diff --git a/Source/Csla/Server/ObjectFactoryAttribute.cs b/Source/Csla/Server/ObjectFactoryAttribute.cs index 71c8849e08..fce8a6ed4a 100644 --- a/Source/Csla/Server/ObjectFactoryAttribute.cs +++ b/Source/Csla/Server/ObjectFactoryAttribute.cs @@ -32,7 +32,7 @@ public class ObjectFactoryAttribute : Attribute /// Factory class must have a parameterless /// default constructor. /// - public string FactoryTypeName { get; private set; } + public string FactoryTypeName { get; } /// /// Name of the method to call for a create operation. /// @@ -40,7 +40,7 @@ public class ObjectFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string CreateMethodName { get; private set; } + public string CreateMethodName { get; } /// /// Name of the method to call for a fetch operation. /// @@ -48,7 +48,7 @@ public class ObjectFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string FetchMethodName { get; private set; } + public string FetchMethodName { get; } /// /// Name of the method to call for a update operation. /// @@ -56,7 +56,7 @@ public class ObjectFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string UpdateMethodName { get; private set; } + public string UpdateMethodName { get; } /// /// Name of the method to call for a delete operation. /// @@ -64,7 +64,7 @@ public class ObjectFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string DeleteMethodName { get; private set; } + public string DeleteMethodName { get; } /// /// Name of the method to call for a Execute operation. /// @@ -72,7 +72,7 @@ public class ObjectFactoryAttribute : Attribute /// The appropriate overload of this method will be /// invoked based on the parameters passed from the client. /// - public string ExecuteMethodName { get; private set; } + public string ExecuteMethodName { get; } /// /// Creates an instance of the attribute. @@ -84,14 +84,9 @@ public class ObjectFactoryAttribute : Attribute /// The method names default to Create, Fetch, /// Update and Delete. /// - public ObjectFactoryAttribute(string factoryType) + /// is , or only consists of white spaces. + public ObjectFactoryAttribute(string factoryType) : this(factoryType, "Fetch") { - FactoryTypeName = factoryType; - CreateMethodName = "Create"; - FetchMethodName = "Fetch"; - UpdateMethodName = "Update"; - DeleteMethodName = "Delete"; - ExecuteMethodName = "Execute"; } /// @@ -100,19 +95,12 @@ public ObjectFactoryAttribute(string factoryType) /// /// Assembly qualified type name of the factory object. /// - /// - /// Name of the method to call for a create operation. /// /// Name of the method to call for a fetch operation. /// - public ObjectFactoryAttribute(string factoryType, string createMethod, string fetchMethod) + /// or is , or only consists of white spaces. + public ObjectFactoryAttribute(string factoryType, string fetchMethod) : this(factoryType, "Create", fetchMethod) { - FactoryTypeName = factoryType; - CreateMethodName = createMethod; - FetchMethodName = fetchMethod; - UpdateMethodName = "Update"; - DeleteMethodName = "Delete"; - ExecuteMethodName = "Execute"; } /// @@ -121,20 +109,16 @@ public ObjectFactoryAttribute(string factoryType, string createMethod, string fe /// /// Assembly qualified type name of the factory object. /// + /// + /// Name of the method to call for a create operation. /// /// Name of the method to call for a fetch operation. /// - public ObjectFactoryAttribute(string factoryType, string fetchMethod) + /// , or is , or only consists of white spaces. + public ObjectFactoryAttribute(string factoryType, string createMethod, string fetchMethod) : this(factoryType, createMethod, fetchMethod, "Update", "Delete") { - FactoryTypeName = factoryType; - FetchMethodName = fetchMethod; - CreateMethodName = "Create"; - UpdateMethodName = "Update"; - DeleteMethodName = "Delete"; - ExecuteMethodName = "Execute"; } - /// /// Creates an instance of the attribute. /// @@ -150,15 +134,9 @@ public ObjectFactoryAttribute(string factoryType, string fetchMethod) /// Name of the method to call for a update operation. /// /// Name of the method to call for a delete operation. - public ObjectFactoryAttribute( - string factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod) + /// , , , or is , or only consists of white spaces. + public ObjectFactoryAttribute(string factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod) : this(factoryType, createMethod, fetchMethod, updateMethod, deleteMethod, "Execute") { - FactoryTypeName = factoryType; - CreateMethodName = createMethod; - FetchMethodName = fetchMethod; - UpdateMethodName = updateMethod; - DeleteMethodName = deleteMethod; - ExecuteMethodName = "Execute"; } /// @@ -182,9 +160,22 @@ public ObjectFactoryAttribute( /// /// Name of the method to call for a Execute operation. /// - public ObjectFactoryAttribute( - string factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod, string executeMethod) + /// , , , , or is , or only consists of white spaces. + public ObjectFactoryAttribute(string factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod, string executeMethod) { + if (string.IsNullOrEmpty(factoryType)) + throw new ArgumentException(string.Format(Properties.Resources.StringNotNullOrWhiteSpaceException, nameof(factoryType)), nameof(factoryType)); + if (string.IsNullOrEmpty(createMethod)) + throw new ArgumentException(string.Format(Properties.Resources.StringNotNullOrWhiteSpaceException, nameof(createMethod)), nameof(createMethod)); + if (string.IsNullOrEmpty(fetchMethod)) + throw new ArgumentException(string.Format(Properties.Resources.StringNotNullOrWhiteSpaceException, nameof(fetchMethod)), nameof(fetchMethod)); + if (string.IsNullOrEmpty(updateMethod)) + throw new ArgumentException(string.Format(Properties.Resources.StringNotNullOrWhiteSpaceException, nameof(updateMethod)), nameof(updateMethod)); + if (string.IsNullOrEmpty(deleteMethod)) + throw new ArgumentException(string.Format(Properties.Resources.StringNotNullOrWhiteSpaceException, nameof(deleteMethod)), nameof(deleteMethod)); + if (string.IsNullOrEmpty(executeMethod)) + throw new ArgumentException(string.Format(Properties.Resources.StringNotNullOrWhiteSpaceException, nameof(executeMethod)), nameof(executeMethod)); + FactoryTypeName = factoryType; CreateMethodName = createMethod; FetchMethodName = fetchMethod; @@ -199,9 +190,11 @@ public ObjectFactoryAttribute( /// /// The type of factory class or interface. /// + /// is . public ObjectFactoryAttribute(Type factoryType) - : this(GetAssemblyQualifiedName(factoryType)) - { } + : this(GetAssemblyQualifiedName(factoryType ?? throw new ArgumentNullException(nameof(factoryType)))) + { + } /// /// Creates an instance of the attribute. @@ -212,8 +205,9 @@ public ObjectFactoryAttribute(Type factoryType) /// /// Name of the method to call for a fetch operation. /// + /// is . public ObjectFactoryAttribute(Type factoryType, string fetchMethod) - : this(GetAssemblyQualifiedName(factoryType), fetchMethod) + : this(GetAssemblyQualifiedName(factoryType ?? throw new ArgumentNullException(nameof(factoryType))), fetchMethod) { } /// @@ -228,8 +222,9 @@ public ObjectFactoryAttribute(Type factoryType, string fetchMethod) /// /// Name of the method to call for a fetch operation. /// + /// is . public ObjectFactoryAttribute(Type factoryType, string createMethod, string fetchMethod) - : this(GetAssemblyQualifiedName(factoryType), createMethod, fetchMethod) + : this(GetAssemblyQualifiedName(factoryType ?? throw new ArgumentNullException(nameof(factoryType))), createMethod, fetchMethod) { } /// @@ -250,8 +245,9 @@ public ObjectFactoryAttribute(Type factoryType, string createMethod, string fetc /// /// Name of the method to call for a delete operation. /// + /// is . public ObjectFactoryAttribute(Type factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod) - : this(GetAssemblyQualifiedName(factoryType), createMethod, fetchMethod, updateMethod, deleteMethod) + : this(GetAssemblyQualifiedName(factoryType ?? throw new ArgumentNullException(nameof(factoryType))), createMethod, fetchMethod, updateMethod, deleteMethod) { } /// @@ -275,8 +271,9 @@ public ObjectFactoryAttribute(Type factoryType, string createMethod, string fetc /// /// Name of the method to call for a Execute operation. /// + /// is . public ObjectFactoryAttribute(Type factoryType, string createMethod, string fetchMethod, string updateMethod, string deleteMethod, string executeMethod) - : this(GetAssemblyQualifiedName(factoryType), createMethod, fetchMethod, updateMethod, deleteMethod, executeMethod) + : this(GetAssemblyQualifiedName(factoryType ?? throw new ArgumentNullException(nameof(factoryType))), createMethod, fetchMethod, updateMethod, deleteMethod, executeMethod) { } /// @@ -287,7 +284,7 @@ private static string GetAssemblyQualifiedName(Type type) { if (type.IsGenericType) { - return type.AssemblyQualifiedName; + return type.AssemblyQualifiedName ?? string.Empty; } else { diff --git a/Source/Csla/Server/ObjectFactoryLoader.cs b/Source/Csla/Server/ObjectFactoryLoader.cs index f6b168e054..83cce1de50 100644 --- a/Source/Csla/Server/ObjectFactoryLoader.cs +++ b/Source/Csla/Server/ObjectFactoryLoader.cs @@ -16,17 +16,18 @@ namespace Csla.Server /// public class ObjectFactoryLoader : IObjectFactoryLoader { + private readonly ApplicationContext _applicationContext; + /// /// Creates an instance of the type. /// /// + /// is . public ObjectFactoryLoader(ApplicationContext applicationContext) { - _applicationContext = applicationContext; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); } - private ApplicationContext _applicationContext; - /// /// Gets the type of the object factory. /// @@ -36,9 +37,13 @@ public ObjectFactoryLoader(ApplicationContext applicationContext) /// the ObjectFactory attribute /// on the business object. /// + /// is , or only consists of white spaces. public Type GetFactoryType(string factoryName) { - return Type.GetType(factoryName); + if (factoryName is null) + throw new ArgumentException(string.Format(Resources.StringNotNullOrWhiteSpaceException, nameof(factoryName)), nameof(factoryName)); + + return Type.GetType(factoryName) ?? throw new InvalidOperationException(Resources.FactoryTypeNotFoundException); } /// @@ -55,13 +60,13 @@ public Type GetFactoryType(string factoryName) /// An instance of the type specified by the /// type name parameter. /// + /// is , or only consists of white spaces. public object GetFactory(string factoryName) { - var ft = GetFactoryType(factoryName); - if (ft == null) - throw new InvalidOperationException( - string.Format(Resources.FactoryTypeNotFoundException, factoryName)); - return _applicationContext.CreateInstanceDI(ft); + if (factoryName is null) + throw new ArgumentException(string.Format(Resources.StringNotNullOrWhiteSpaceException, nameof(factoryName)), nameof(factoryName)); + + return _applicationContext.CreateInstanceDI(GetFactoryType(factoryName)); } } } \ No newline at end of file diff --git a/Source/Csla/Server/SanitizingExceptionInspector.cs b/Source/Csla/Server/SanitizingExceptionInspector.cs index eb9ab678da..3aff661057 100644 --- a/Source/Csla/Server/SanitizingExceptionInspector.cs +++ b/Source/Csla/Server/SanitizingExceptionInspector.cs @@ -15,10 +15,9 @@ namespace Csla.Server /// Sanitizing implementation of exception inspector, for hiding /// sensitive information in exception details. /// - /// Only sanitizes exceptions from remote dataportals + /// Only sanitizes exceptions from remote data portals public class SanitizingExceptionInspector : IDataPortalExceptionInspector { - private readonly IHostEnvironment _hostEnvironment; private readonly ApplicationContext _applicationContext; private readonly ILogger _logger; @@ -29,11 +28,12 @@ public class SanitizingExceptionInspector : IDataPortalExceptionInspector /// The host environment in which we are operating /// The context of the current request /// The logger to which to log exceptions + /// , or is . public SanitizingExceptionInspector(IHostEnvironment environment, ApplicationContext applicationContext, ILogger logger) { - _hostEnvironment = environment; - _applicationContext = applicationContext; - _logger = logger; + _hostEnvironment = environment ?? throw new ArgumentNullException(nameof(environment)); + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// @@ -45,7 +45,7 @@ public SanitizingExceptionInspector(IHostEnvironment environment, ApplicationCon /// The criteria. /// Name of the method. /// The exception. - public void InspectException(Type objectType, object businessObject, object criteria, string methodName, Exception ex) + public void InspectException(Type objectType, object? businessObject, object? criteria, string methodName, Exception ex) { // Shortcut if we are not running on the server-side of a remote data portal operation if (_applicationContext.ExecutionLocation != ApplicationContext.ExecutionLocations.Server || @@ -55,6 +55,9 @@ public void InspectException(Type objectType, object businessObject, object crit // Shortcut if we are running in development (max. developer productivity) if (_hostEnvironment.IsDevelopment()) return; + + if (ex is null) + throw new ArgumentNullException(nameof(ex)); // Sanitize in all remaining scenarios string identifier = Guid.NewGuid().ToString(); @@ -63,4 +66,4 @@ public void InspectException(Type objectType, object businessObject, object crit throw new ServerException(identifier); } } -} +} \ No newline at end of file diff --git a/Source/Csla/Server/ServerException.cs b/Source/Csla/Server/ServerException.cs index 070a515aac..0d8fc73589 100644 --- a/Source/Csla/Server/ServerException.cs +++ b/Source/Csla/Server/ServerException.cs @@ -22,8 +22,12 @@ public class ServerException : Exception /// Constructor. /// /// The unique identifier for this exception + /// is . public ServerException(string identifier) : base(Properties.Resources.SanitizedServerSideDataPortalException) { + if (identifier is null) + throw new ArgumentNullException(nameof(identifier)); + Data.Add(IdentifierKey, identifier); } @@ -32,9 +36,15 @@ public ServerException(string identifier) : base(Properties.Resources.SanitizedS /// Use this identifier to look for the exception details in server logs /// public string RequestIdentifier - { get + { + get { - return Data[IdentifierKey].ToString(); + return Data[IdentifierKey] switch + { + string x => x, + null => string.Empty, + object x => x.ToString() ?? string.Empty + }; } } diff --git a/Source/Csla/Server/ServicedDataPortalReadCommitted.cs b/Source/Csla/Server/ServicedDataPortalReadCommitted.cs index b7906d4080..84c20d378f 100644 --- a/Source/Csla/Server/ServicedDataPortalReadCommitted.cs +++ b/Source/Csla/Server/ServicedDataPortalReadCommitted.cs @@ -20,16 +20,18 @@ namespace Csla.Server [ComVisible(true)] public class ServicedDataPortalReadCommitted : ServicedComponent, IDataPortalServer { + private DataPortalBroker portal { get; } + /// /// /// /// + /// is . public ServicedDataPortalReadCommitted(DataPortalBroker dataPortalBroker) { - portal = dataPortalBroker; + portal = dataPortalBroker ?? throw new ArgumentNullException(nameof(dataPortalBroker)); } - private DataPortalBroker portal { get; set; } /// /// Wraps a Create call in a ServicedComponent. @@ -48,9 +50,9 @@ public ServicedDataPortalReadCommitted(DataPortalBroker dataPortalBroker) /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . [AutoComplete(true)] - public Task Create( - Type objectType, object criteria, DataPortalContext context, bool isSync) + public Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { return portal.Create(objectType, criteria, context, isSync); } @@ -69,6 +71,7 @@ public Task Create( /// Object containing context data from client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . [AutoComplete(true)] public Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { @@ -88,6 +91,7 @@ public Task Fetch(Type objectType, object criteria, DataPortal /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A reference to the newly updated object. + /// or is . [AutoComplete(true)] public Task Update(object obj, DataPortalContext context, bool isSync) { @@ -107,6 +111,7 @@ public Task Update(object obj, DataPortalContext context, bool /// Object-specific criteria. /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. + /// , or is . [AutoComplete(true)] public Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { diff --git a/Source/Csla/Server/ServicedDataPortalReadUncommitted.cs b/Source/Csla/Server/ServicedDataPortalReadUncommitted.cs index 800c1ca7f2..ddca263d77 100644 --- a/Source/Csla/Server/ServicedDataPortalReadUncommitted.cs +++ b/Source/Csla/Server/ServicedDataPortalReadUncommitted.cs @@ -20,17 +20,18 @@ namespace Csla.Server [ComVisible(true)] public class ServicedDataPortalReadUncommitted : ServicedComponent, IDataPortalServer { + private DataPortalBroker portal { get; } + /// /// /// /// + /// is . public ServicedDataPortalReadUncommitted(DataPortalBroker dataPortalBroker) { - portal = dataPortalBroker; + portal = dataPortalBroker ?? throw new ArgumentNullException(nameof(dataPortalBroker)); } - private DataPortalBroker portal { get; set; } - /// /// Wraps a Create call in a ServicedComponent. /// @@ -48,9 +49,9 @@ public ServicedDataPortalReadUncommitted(DataPortalBroker dataPortalBroker) /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . [AutoComplete(true)] - public Task Create( - Type objectType, object criteria, DataPortalContext context, bool isSync) + public Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { return portal.Create(objectType, criteria, context, isSync); } @@ -69,6 +70,7 @@ public Task Create( /// Object containing context data from client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . [AutoComplete(true)] public Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { @@ -88,6 +90,7 @@ public Task Fetch(Type objectType, object criteria, DataPortal /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A reference to the newly updated object. + /// or is . [AutoComplete(true)] public Task Update(object obj, DataPortalContext context, bool isSync) { @@ -107,6 +110,7 @@ public Task Update(object obj, DataPortalContext context, bool /// Object-specific criteria. /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. + /// , or is . [AutoComplete(true)] public Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { diff --git a/Source/Csla/Server/ServicedDataPortalRepeatableRead.cs b/Source/Csla/Server/ServicedDataPortalRepeatableRead.cs index f684a05f68..9d024fb21e 100644 --- a/Source/Csla/Server/ServicedDataPortalRepeatableRead.cs +++ b/Source/Csla/Server/ServicedDataPortalRepeatableRead.cs @@ -20,17 +20,18 @@ namespace Csla.Server [ComVisible(true)] public class ServicedDataPortalRepeatableRead : ServicedComponent, IDataPortalServer { + private DataPortalBroker portal { get; } + /// /// /// /// + /// is . public ServicedDataPortalRepeatableRead(DataPortalBroker dataPortalBroker) { - portal = dataPortalBroker; + portal = dataPortalBroker ?? throw new ArgumentNullException(nameof(dataPortalBroker)); } - private DataPortalBroker portal { get; set; } - /// /// Wraps a Create call in a ServicedComponent. /// @@ -48,9 +49,9 @@ public ServicedDataPortalRepeatableRead(DataPortalBroker dataPortalBroker) /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . [AutoComplete(true)] - public Task Create( - Type objectType, object criteria, DataPortalContext context, bool isSync) + public Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { return portal.Create(objectType, criteria, context, isSync); } @@ -69,6 +70,7 @@ public Task Create( /// Object containing context data from client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . [AutoComplete(true)] public Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { @@ -88,6 +90,7 @@ public Task Fetch(Type objectType, object criteria, DataPortal /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A reference to the newly updated object. + /// or is . [AutoComplete(true)] public Task Update(object obj, DataPortalContext context, bool isSync) { @@ -107,6 +110,7 @@ public Task Update(object obj, DataPortalContext context, bool /// Object-specific criteria. /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. + /// , or is . [AutoComplete(true)] public Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { diff --git a/Source/Csla/Server/ServicedDataPortalSerializable.cs b/Source/Csla/Server/ServicedDataPortalSerializable.cs index c5761a037a..a8761eacdc 100644 --- a/Source/Csla/Server/ServicedDataPortalSerializable.cs +++ b/Source/Csla/Server/ServicedDataPortalSerializable.cs @@ -20,17 +20,18 @@ namespace Csla.Server [ComVisible(true)] public class ServicedDataPortalSerializable : ServicedComponent, IDataPortalServer { + private DataPortalBroker portal { get; } + /// /// /// /// + /// is . public ServicedDataPortalSerializable(DataPortalBroker dataPortalBroker) { - portal = dataPortalBroker; + portal = dataPortalBroker ?? throw new ArgumentNullException(nameof(dataPortalBroker)); } - private DataPortalBroker portal { get; set; } - /// /// Wraps a Create call in a ServicedComponent. /// @@ -48,10 +49,17 @@ public ServicedDataPortalSerializable(DataPortalBroker dataPortalBroker) /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . [AutoComplete(true)] - public Task Create( - Type objectType, object criteria, DataPortalContext context, bool isSync) + public Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + return portal.Create(objectType, criteria, context, isSync); } @@ -69,9 +77,17 @@ public Task Create( /// Object containing context data from client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . [AutoComplete(true)] public Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + return portal.Fetch(objectType, criteria, context, isSync); } @@ -88,9 +104,15 @@ public Task Fetch(Type objectType, object criteria, DataPortal /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A reference to the newly updated object. + /// or is . [AutoComplete(true)] public Task Update(object obj, DataPortalContext context, bool isSync) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + return portal.Update(obj, context, isSync); } @@ -107,9 +129,17 @@ public Task Update(object obj, DataPortalContext context, bool /// Object-specific criteria. /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. + /// , or is . [AutoComplete(true)] public Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + return portal.Delete(objectType, criteria, context, isSync); } } diff --git a/Source/Csla/Server/SimpleDataPortal.cs b/Source/Csla/Server/SimpleDataPortal.cs index ae08a56e67..eae8e6de0d 100644 --- a/Source/Csla/Server/SimpleDataPortal.cs +++ b/Source/Csla/Server/SimpleDataPortal.cs @@ -16,6 +16,11 @@ namespace Csla.Server /// public class SimpleDataPortal : IDataPortalServer { + private readonly ApplicationContext _applicationContext; + private IDataPortalActivator Activator { get; } + private IDataPortalExceptionInspector ExceptionInspector { get; } + private Configuration.DataPortalOptions DataPortalOptions { get; } + /// /// Creates an instance of the type /// @@ -23,34 +28,28 @@ public class SimpleDataPortal : IDataPortalServer /// /// /// + /// , , or is . public SimpleDataPortal(ApplicationContext applicationContext, IDataPortalActivator activator, IDataPortalExceptionInspector exceptionInspector, Configuration.DataPortalOptions dataPortalOptions) { - _applicationContext = applicationContext; - Activator = activator; - ExceptionInspector = exceptionInspector; - DataPortalOptions = dataPortalOptions; + _applicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); + Activator = activator ?? throw new ArgumentNullException(nameof(activator)); + ExceptionInspector = exceptionInspector ?? throw new ArgumentNullException(nameof(exceptionInspector)); + DataPortalOptions = dataPortalOptions ?? throw new ArgumentNullException(nameof(dataPortalOptions)); } - private ApplicationContext _applicationContext; - private IDataPortalActivator Activator { get; set; } - private IDataPortalExceptionInspector ExceptionInspector { get; set; } - private Configuration.DataPortalOptions DataPortalOptions { get; set; } - - /// - /// Create a new business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Csla.Server.DataPortalException.#ctor(System.String,System.Exception,Csla.Server.DataPortalResult)")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] - public async Task Create( - Type objectType, object criteria, DataPortalContext context, bool isSync) + public async Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { - DataPortalTarget obj = null; + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + + DataPortalTarget? obj = null; var eventArgs = new DataPortalEventArgs(context, objectType, criteria, DataPortalOperations.Create); try { @@ -73,8 +72,10 @@ public async Task Create( { // ignore exceptions from the exception handler } - object outval = null; - if (obj != null) outval = obj.Instance; + object? outval = null; + if (obj != null) + outval = obj.Instance; + throw DataPortal.NewDataPortalException( _applicationContext, "DataPortal.Create " + Resources.FailedOnServer, new DataPortalExceptionHandler(ExceptionInspector).InspectException(objectType, outval, criteria, "DataPortal.Create", ex), @@ -82,32 +83,30 @@ public async Task Create( } finally { - object reference = null; - if (obj != null) - reference = obj.Instance; - Activator.FinalizeInstance(reference); + if (obj?.Instance is not null) + Activator.FinalizeInstance(obj.Instance); + } } - /// - /// Get an existing business object. - /// - /// Type of business object to retrieve. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Csla.Server.DataPortalException.#ctor(System.String,System.Exception,Csla.Server.DataPortalResult)")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public async Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + if (typeof(Core.ICommandObject).IsAssignableFrom(objectType)) { return await Execute(objectType, criteria, context, isSync); } - DataPortalTarget obj = null; + DataPortalTarget? obj = null; var eventArgs = new DataPortalEventArgs(context, objectType, criteria, DataPortalOperations.Fetch); try { @@ -131,8 +130,9 @@ public async Task Fetch(Type objectType, object criteria, Data { // ignore exceptions from the exception handler } - object outval = null; - if (obj != null) outval = obj.Instance; + object? outval = null; + if (obj != null) + outval = obj.Instance; throw DataPortal.NewDataPortalException( _applicationContext, "DataPortal.Fetch " + Resources.FailedOnServer, new DataPortalExceptionHandler(ExceptionInspector).InspectException(objectType, outval, criteria, "DataPortal.Fetch", ex), @@ -140,16 +140,14 @@ public async Task Fetch(Type objectType, object criteria, Data } finally { - object reference = null; - if (obj != null) - reference = obj.Instance; - Activator.FinalizeInstance(reference); + if (obj?.Instance is not null) + Activator.FinalizeInstance(obj.Instance); } } private async Task Execute(Type objectType, object criteria, DataPortalContext context, bool isSync) { - DataPortalTarget obj = null; + DataPortalTarget? obj = null; var eventArgs = new DataPortalEventArgs(context, objectType, criteria, DataPortalOperations.Execute); try { @@ -173,7 +171,7 @@ private async Task Execute(Type objectType, object criteria, D { // ignore exceptions from the exception handler } - object outval = null; + object? outval = null; if (obj != null) outval = obj.Instance; throw DataPortal.NewDataPortalException( _applicationContext, "DataPortal.Execute " + Resources.FailedOnServer, @@ -182,25 +180,21 @@ private async Task Execute(Type objectType, object criteria, D } finally { - object reference = null; - if (obj != null) - reference = obj.Instance; - Activator.FinalizeInstance(reference); + if (obj?.Instance is not null) + Activator.FinalizeInstance(obj.Instance); } } - /// - /// Update a business object. - /// - /// Business object to update. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Csla.Server.DataPortalException.#ctor(System.String,System.Exception,Csla.Server.DataPortalResult)")] public async Task Update(object obj, DataPortalContext context, bool isSync) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + DataPortalOperations operation = DataPortalOperations.Update; Type objectType = obj.GetType(); var lb = _applicationContext.CreateInstanceDI(obj); @@ -235,10 +229,7 @@ public async Task Update(object obj, DataPortalContext context } finally { - object reference = null; - if (lb != null) - reference = lb.Instance; - Activator.FinalizeInstance(reference); + Activator.FinalizeInstance(lb.Instance); } } @@ -267,8 +258,7 @@ private async Task Execute(DataPortalTarget obj, DataPortalCon { // ignore exceptions from the exception handler } - object reference = null; - reference = obj.Instance ?? obj; + var reference = obj.Instance; throw DataPortal.NewDataPortalException( _applicationContext, "DataPortal.Execute " + Resources.FailedOnServer, new DataPortalExceptionHandler(ExceptionInspector).InspectException(reference.GetType(), reference, null, "DataPortal.Execute", ex), @@ -276,25 +266,15 @@ private async Task Execute(DataPortalTarget obj, DataPortalCon } finally { - object reference = null; - if (obj != null) - reference = obj.Instance; - Activator.FinalizeInstance(reference); + Activator.FinalizeInstance(obj.Instance); } } - /// - /// Delete a business object. - /// - /// Type of business object to create. - /// Criteria object describing business object. - /// - /// object passed to the server. - /// - /// True if the client-side proxy should synchronously invoke the server. + + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Csla.Server.DataPortalException.#ctor(System.String,System.Exception,Csla.Server.DataPortalResult)")] public async Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { - DataPortalTarget obj = null; + DataPortalTarget? obj = null; var eventArgs = new DataPortalEventArgs(context, objectType, criteria, DataPortalOperations.Delete); try { @@ -311,7 +291,7 @@ public async Task Delete(Type objectType, object criteria, Dat { try { - obj.OnDataPortalException(eventArgs, ex); + obj?.OnDataPortalException(eventArgs, ex); } catch { @@ -324,11 +304,9 @@ public async Task Delete(Type objectType, object criteria, Dat } finally { - object reference = null; - if (obj != null) - reference = obj.Instance; - Activator.FinalizeInstance(reference); + if (obj?.Instance is not null) + Activator.FinalizeInstance(obj.Instance); } } } -} +} \ No newline at end of file diff --git a/Source/Csla/Server/TransactionalDataPortal.cs b/Source/Csla/Server/TransactionalDataPortal.cs index a797582008..f08d488bd5 100644 --- a/Source/Csla/Server/TransactionalDataPortal.cs +++ b/Source/Csla/Server/TransactionalDataPortal.cs @@ -32,10 +32,11 @@ public class TransactionalDataPortal : IDataPortalServer /// /// The transactional attribute that defines transaction options to be used with transactions. /// + /// or is . public TransactionalDataPortal(DataPortalBroker dataPortalBroker, TransactionalAttribute transactionalAttribute) { - _portal = dataPortalBroker; - _transactionalAttribute = transactionalAttribute; + _portal = dataPortalBroker ?? throw new ArgumentNullException(nameof(dataPortalBroker)); + _transactionalAttribute = transactionalAttribute ?? throw new ArgumentNullException(nameof(transactionalAttribute)); } /// @@ -57,8 +58,8 @@ public TransactionalDataPortal(DataPortalBroker dataPortalBroker, TransactionalA /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. - public async Task Create( - Type objectType, object criteria, DataPortalContext context, bool isSync) + /// , or is . + public async Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) { using TransactionScope tr = CreateTransactionScope(); var result = await _portal.Create(objectType, criteria, context, isSync).ConfigureAwait(false); @@ -101,7 +102,7 @@ private IsolationLevel GetIsolationLevel(TransactionIsolationLevel transactionIs } /// - /// Called by the client-side DataProtal to retrieve an object. + /// Called by the client-side DataPortal to retrieve an object. /// /// /// This method delegates to @@ -116,8 +117,16 @@ private IsolationLevel GetIsolationLevel(TransactionIsolationLevel transactionIs /// Object containing context data from client. /// True if the client-side proxy should synchronously invoke the server. /// A populated business object. + /// , or is . public async Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + using TransactionScope tr = CreateTransactionScope(); var result = await _portal.Fetch(objectType, criteria, context, isSync).ConfigureAwait(false); tr.Complete(); @@ -139,8 +148,14 @@ public async Task Fetch(Type objectType, object criteria, Data /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. /// A reference to the newly updated object. + /// or is . public async Task Update(object obj, DataPortalContext context, bool isSync) { + if (obj is null) + throw new ArgumentNullException(nameof(obj)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + using TransactionScope tr = CreateTransactionScope(); var result = await _portal.Update(obj, context, isSync).ConfigureAwait(false); tr.Complete(); @@ -162,8 +177,16 @@ public async Task Update(object obj, DataPortalContext context /// Object-specific criteria. /// Context data from the client. /// True if the client-side proxy should synchronously invoke the server. + /// , or is . public async Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (criteria is null) + throw new ArgumentNullException(nameof(criteria)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + using TransactionScope tr = CreateTransactionScope(); var result = await _portal.Delete(objectType, criteria, context, isSync).ConfigureAwait(false); tr.Complete(); diff --git a/docs/Upgrading to CSLA 9.md b/docs/Upgrading to CSLA 9.md index 14f8761cd6..b46f2a53f8 100644 --- a/docs/Upgrading to CSLA 9.md +++ b/docs/Upgrading to CSLA 9.md @@ -226,4 +226,11 @@ Supporting nullable types means that some APIs have changed to support nullable * `AddAspNetCore()` configuration method now adds the necessary services to support resolving `CslaModelBinder` from the DI container * `Csla.Serialization.Mobile.SerializationInfo` * Now has a constructor requiring `int referenceId` and `string typeName` - * Property `ReferenceId` and `TypeName` property set removed and replaced by the constructor \ No newline at end of file + * Property `ReferenceId` and `TypeName` property set removed and replaced by the constructor +* `Csla.Server.InterceptArgs` + * Now as two new constructors requiring necessary parameters + * Property set for required parameters removed +* `Csla.Server.EmptyCriteria` + * Public constructor removed (now private). Instead use `Csla.Server.EmptyCriteria.Instance`. +* `Csla.Server.ObjectFactory` + * Protected methods now guard against `null` objects \ No newline at end of file