Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Source/Csla/Core/FieldManager/FieldDataManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,7 @@ public static void ForceStaticFieldInit([DynamicallyAccessedMembers(DynamicallyA
private static void OnAssemblyLoadContextUnload(AssemblyLoadContext context)
{
lock (_consolidatedLists)
AssemblyLoadContextManager.RemoveFromCache((IDictionary<Type, Tuple<string?, Type>?>)_consolidatedLists, context);
AssemblyLoadContextManager.RemoveFromCache((IDictionary<Type, Tuple<string?, List<IPropertyInfo>>?>)_consolidatedLists, context);
}
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Csla/Core/FieldManager/PropertyInfoManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ private static void OnAssemblyLoadContextUnload(AssemblyLoadContext context)
var cache = PropertyInfoCache;

lock (cache)
AssemblyLoadContextManager.RemoveFromCache((IDictionary<string, Tuple<string?, DynamicMemberHandle>?>)cache, context);
AssemblyLoadContextManager.RemoveFromCache((IDictionary<Type, Tuple<string?, PropertyInfoList>?>)cache, context, true);
}
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Csla/Core/UndoableHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private static bool NotUndoableField(FieldInfo field)
private static void OnAssemblyLoadContextUnload(AssemblyLoadContext context)
{
lock (_undoableFieldCache)
AssemblyLoadContextManager.RemoveFromCache((IDictionary<string, Tuple<string?, DynamicMemberHandle>?>)_undoableFieldCache, context);
AssemblyLoadContextManager.RemoveFromCache((IDictionary<Type, Tuple<string?, List<DynamicMemberHandle>>?>)_undoableFieldCache, context);
}
#endif
}
Expand Down
4 changes: 2 additions & 2 deletions Source/Csla/Reflection/MethodCaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1581,13 +1581,13 @@ private static bool IsAsyncMethod(object obj, string method, bool hasParameters,
private static void OnMethodAssemblyLoadContextUnload(AssemblyLoadContext context)
{
lock (_methodCache)
AssemblyLoadContextManager.RemoveFromCache((IDictionary<string, Tuple<string?, DynamicMemberHandle>?>)_methodCache, context);
AssemblyLoadContextManager.RemoveFromCache((IDictionary<MethodCacheKey, Tuple<string?, DynamicMethodHandle>?>)_methodCache, context);
}

private static void OnMemberAssemblyLoadContextUnload(AssemblyLoadContext context)
{
lock (_memberCache)
AssemblyLoadContextManager.RemoveFromCache((IDictionary<string, Tuple<string?, DynamicMemberHandle>?>)_memberCache, context);
AssemblyLoadContextManager.RemoveFromCache((IDictionary<MethodCacheKey, Tuple<string?, DynamicMemberHandle>?>)_memberCache, context);
}
#endif
}
Expand Down
79 changes: 45 additions & 34 deletions Source/Csla/Reflection/ServiceProviderMethodCaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,18 @@ public bool TryFindDataPortalMethod<T>([DynamicallyAccessedMembers(DynamicallyAc

var typeOfOperation = typeof(T);

var cacheKey = GetCacheKeyName(targetType, typeOfOperation, criteria, useLegacyMethods);
// Resolve the factory type (if any) up front so it can participate in the cache key.
// In production a business type maps to exactly one factory, but a custom
// IObjectFactoryLoader can resolve the same FactoryTypeName to different types. The
// method cache is process-wide and keyed by business type, so the resolved factory
// type must be part of the key to avoid invoking a delegate compiled for a different
// factory.
var factoryInfo = ObjectFactoryAttribute.GetObjectFactoryAttribute(targetType);
Type? factoryType = null;
if (factoryInfo != null && !TryGetFactoryType(factoryInfo, _applicationContext, throwOnError, out factoryType))
return null;

var cacheKey = GetCacheKeyName(targetType, typeOfOperation, criteria, useLegacyMethods, factoryType);

#if NET8_0_OR_GREATER
if (_methodCache.TryGetValue(cacheKey, out var unloadableCachedMethodInfo))
Expand All @@ -139,32 +150,27 @@ public bool TryFindDataPortalMethod<T>([DynamicallyAccessedMembers(DynamicallyAc
}

var candidates = new List<ScoredMethodInfo>();
var factoryInfo = ObjectFactoryAttribute.GetObjectFactoryAttribute(targetType);
if (factoryInfo != null)
{
if (!TryGetFactoryType(factoryInfo, _applicationContext, throwOnError, out var factoryType))
{
return null;
}

var factoryWalkType = factoryType;
var ftList = new List<System.Reflection.MethodInfo>();
var level = 0;
while (factoryType != null)
while (factoryWalkType != null)
{
ftList.Clear();
if (typeOfOperation == typeof(CreateAttribute))
ftList.AddRange(factoryType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.CreateMethodName));
ftList.AddRange(factoryWalkType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.CreateMethodName));
else if (typeOfOperation == typeof(FetchAttribute))
ftList.AddRange(factoryType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.FetchMethodName));
ftList.AddRange(factoryWalkType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.FetchMethodName));
else if (typeOfOperation == typeof(DeleteAttribute))
ftList.AddRange(factoryType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.DeleteMethodName));
ftList.AddRange(factoryWalkType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.DeleteMethodName));
else if (typeOfOperation == typeof(ExecuteAttribute))
ftList.AddRange(factoryType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.ExecuteMethodName));
ftList.AddRange(factoryWalkType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.ExecuteMethodName));
else if (typeOfOperation == typeof(CreateChildAttribute))
ftList.AddRange(factoryType.GetMethods(_factoryBindingAttr).Where(m => m.Name == "Child_Create"));
ftList.AddRange(factoryWalkType.GetMethods(_factoryBindingAttr).Where(m => m.Name == "Child_Create"));
else
ftList.AddRange(factoryType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.UpdateMethodName));
factoryType = factoryType.BaseType;
ftList.AddRange(factoryWalkType.GetMethods(_factoryBindingAttr).Where(m => m.Name == factoryInfo.UpdateMethodName));
factoryWalkType = factoryWalkType.BaseType;
candidates.AddRange(ftList.Select(r => new ScoredMethodInfo { MethodInfo = r, Score = level }));
level--;
}
Expand All @@ -173,22 +179,6 @@ public bool TryFindDataPortalMethod<T>([DynamicallyAccessedMembers(DynamicallyAc
var ftlist = targetType.GetMethods(_bindingAttr).Where(m => m.Name == "Child_Create");
candidates.AddRange(ftlist.Select(r => new ScoredMethodInfo { MethodInfo = r, Score = 0 }));
}

static bool TryGetFactoryType(ObjectFactoryAttribute factoryAttribute, ApplicationContext context, bool throwOnError, [NotNullWhen(true)] out Type? factoryType)
{
try
{
var factoryLoader = context.CurrentServiceProvider.GetRequiredService<IObjectFactoryLoader>();
factoryType = factoryLoader.GetFactoryType(factoryAttribute.FactoryTypeName);
}
catch when (!throwOnError)
{
factoryType = null;
return false;
}

return factoryType is not null;
}
}
else // not using factory types
{
Expand Down Expand Up @@ -225,6 +215,22 @@ static bool TryGetFactoryType(ObjectFactoryAttribute factoryAttribute, Applicati
}
}

static bool TryGetFactoryType(ObjectFactoryAttribute factoryAttribute, ApplicationContext context, bool throwOnError, [NotNullWhen(true)] out Type? factoryType)
{
try
{
var factoryLoader = context.CurrentServiceProvider.GetRequiredService<IObjectFactoryLoader>();
factoryType = factoryLoader.GetFactoryType(factoryAttribute.FactoryTypeName);
}
catch when (!throwOnError)
{
factoryType = null;
return false;
}

return factoryType is not null;
}

ScoredMethodInfo? result = null;

if (candidates.Any())
Expand Down Expand Up @@ -431,10 +437,11 @@ private static int CalculateParameterScore(ParameterInfo methodParam, object? c)
return 0;
}

private static string GetCacheKeyName(Type targetType, Type operationType, object?[]? criteria, bool useLegacyMethods)
private static string GetCacheKeyName(Type targetType, Type operationType, object?[]? criteria, bool useLegacyMethods, Type? factoryType = null)
{
var legacy = useLegacyMethods ? "" : "|nolegacy";
return $"{targetType.FullName}.[{operationType.Name.Replace("Attribute", "")}]{GetCriteriaTypeNames(criteria)}{legacy}";
var factory = factoryType is null ? "" : $"|{factoryType.FullName}";
return $"{targetType.FullName}.[{operationType.Name.Replace("Attribute", "")}]{GetCriteriaTypeNames(criteria)}{legacy}{factory}";
}

private static string GetCriteriaTypeNames(object?[]? criteria)
Expand Down Expand Up @@ -615,7 +622,11 @@ private static ParameterInfo[] GetDIParameters(System.Reflection.MethodInfo meth
}
else if (method.IsAsyncTaskObject)
{
return await ((Task<object>)method.DynamicMethod!(obj, plist)).ConfigureAwait(false);
// The method returns Task<T>; Task<T> is invariant so it cannot be cast to
// Task<object>. Use the conversion helper to await it and extract the result.
var returnValue = method.DynamicMethod!(obj, plist);
var convertedTask = (Task<object?>)method.ConvertToTaskObjectMethod!.Invoke(null, [returnValue])!;
return await convertedTask.ConfigureAwait(false);
}
else
{
Expand Down
7 changes: 7 additions & 0 deletions Source/Csla/Reflection/ServiceProviderMethodInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public class ServiceProviderMethodInfo
/// </summary>
public bool IsAsyncTaskObject { get; set; }
/// <summary>
/// Gets the helper method used to convert a Task of T
/// return value into a Task of object
/// </summary>
internal System.Reflection.MethodInfo? ConvertToTaskObjectMethod { get; private set; }
/// <summary>
/// Gets the DataPortalInfo for the method
/// </summary>
internal DataPortalMethodInfo? DataPortalMethodInfo { get; private set; }
Expand Down Expand Up @@ -118,6 +123,8 @@ public void PrepForInvocation()
}
IsAsyncTask = (MethodInfo.ReturnType == typeof(Task));
IsAsyncTaskObject = (MethodInfo.ReturnType.IsGenericType && (MethodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)));
if (IsAsyncTaskObject)
ConvertToTaskObjectMethod = TaskConversionHelper.CreateTaskObjectConversionMethodInfo(MethodInfo.ReturnType.GetGenericArguments()[0]);
DataPortalMethodInfo = new DataPortalMethodInfo(MethodInfo);

Initialized = true;
Expand Down
2 changes: 1 addition & 1 deletion Source/Csla/Server/DataPortalMethodCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ private static System.Reflection.MethodInfo GetMethodOfCaller([DynamicallyAccess
private static void OnAssemblyLoadContextUnload(AssemblyLoadContext context)
{
lock (_cache)
AssemblyLoadContextManager.RemoveFromCache((IDictionary<string, Tuple<string?, DynamicMemberHandle>?>)_cache, context);
AssemblyLoadContextManager.RemoveFromCache((IDictionary<MethodCacheKey, Tuple<string?, DataPortalMethodInfo>?>)_cache, context);
}
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Csla/Server/DataPortalTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ public Task DeleteAsync(object criteria, bool isSync, string? operationName = nu
#if NET8_0_OR_GREATER
private static void OnAssemblyLoadContextUnload(AssemblyLoadContext context)
{
AssemblyLoadContextManager.RemoveFromCache((IDictionary<string, Tuple<string?, DynamicMemberHandle>?>)_methodNameList, context, true);
AssemblyLoadContextManager.RemoveFromCache((IDictionary<Type, Tuple<string?, DataPortalMethodNames>?>)_methodNameList, context, true);
}
#endif
}
Expand Down
Loading
Loading