Skip to content

Commit 2d7ffb1

Browse files
Merge pull request #3658 from rockfordlhotka/v8-work
Change cache API to be a single operation
2 parents 6de739b + 9deb29b commit 2d7ffb1

11 files changed

+231
-194
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.9.34511.98
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataPortalCacheExample", "DataPortalCacheExample\DataPortalCacheExample.csproj", "{A3852023-B5D7-41CC-898A-1FC6D994D837}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Csla", "..\..\Source\Csla\Csla.csproj", "{5D480CFE-5F9F-4D84-9B13-22F2BB7E384C}"
9+
EndProject
10+
Global
11+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Debug|Any CPU = Debug|Any CPU
13+
Release|Any CPU = Release|Any CPU
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{A3852023-B5D7-41CC-898A-1FC6D994D837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17+
{A3852023-B5D7-41CC-898A-1FC6D994D837}.Debug|Any CPU.Build.0 = Debug|Any CPU
18+
{A3852023-B5D7-41CC-898A-1FC6D994D837}.Release|Any CPU.ActiveCfg = Release|Any CPU
19+
{A3852023-B5D7-41CC-898A-1FC6D994D837}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{5D480CFE-5F9F-4D84-9B13-22F2BB7E384C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{5D480CFE-5F9F-4D84-9B13-22F2BB7E384C}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{5D480CFE-5F9F-4D84-9B13-22F2BB7E384C}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{5D480CFE-5F9F-4D84-9B13-22F2BB7E384C}.Release|Any CPU.Build.0 = Release|Any CPU
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
GlobalSection(ExtensibilityGlobals) = postSolution
29+
SolutionGuid = {376D1082-C234-4347-B2E4-CCFE3EFC9D68}
30+
EndGlobalSection
31+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System.Text;
2+
using Csla;
3+
using Csla.DataPortalClient;
4+
using Csla.Server;
5+
using Microsoft.Extensions.Caching.Memory;
6+
7+
namespace DataPortalCacheExample
8+
{
9+
public class DataPortalCache(IMemoryCache cache) : IDataPortalCache
10+
{
11+
private readonly IMemoryCache _cache = cache;
12+
13+
public async Task<DataPortalResult> GetDataPortalResultAsync(Type objectType, object criteria, DataPortalOperations operation, Func<Task<DataPortalResult>> portal)
14+
{
15+
if (operation == DataPortalOperations.Create && objectType == typeof(ReferenceData))
16+
{
17+
// this operation + type is cached
18+
return await GetResultAsync(objectType, criteria, operation, portal);
19+
}
20+
else
21+
{
22+
// the result isn't cached
23+
return await portal();
24+
}
25+
}
26+
27+
private async Task<DataPortalResult> GetResultAsync(Type objectType, object criteria, DataPortalOperations operation, Func<Task<DataPortalResult>> portal)
28+
{
29+
DataPortalResult? result;
30+
var key = GetKey(objectType, criteria, operation);
31+
result = await _cache.GetOrCreateAsync(key, async (v) =>
32+
{
33+
var obj = await portal();
34+
v.AbsoluteExpiration = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(5);
35+
return obj;
36+
});
37+
if (result != null)
38+
return result;
39+
else
40+
return await portal();
41+
}
42+
43+
private static string GetKey(Type objectType, object criteria, DataPortalOperations operation)
44+
{
45+
var builder = new StringBuilder();
46+
// requested type
47+
builder.Append(objectType.FullName);
48+
builder.Append('|');
49+
50+
// criteria values (each criteria has 'valid' ToString)
51+
var criteriaList = Csla.Server.DataPortal.GetCriteriaArray(criteria);
52+
foreach (var item in criteriaList)
53+
{
54+
builder.Append(item.ToString());
55+
builder.Append('|');
56+
}
57+
58+
// operation
59+
builder.Append(operation.ToString());
60+
return builder.ToString();
61+
}
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
12+
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\..\..\Source\Csla\Csla.csproj" />
17+
</ItemGroup>
18+
19+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Csla;
2+
using Csla.Configuration;
3+
using DataPortalCacheExample;
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
var services = new ServiceCollection();
7+
// use standard memory cache
8+
services.AddMemoryCache();
9+
// use CSLA with client-side data portal cache
10+
services.AddCsla(o => o
11+
.DataPortal(o => o
12+
.ClientSideDataPortal(o => o
13+
.DataPortalCacheType = typeof(DataPortalCache))));
14+
var provider = services.BuildServiceProvider();
15+
16+
// test code
17+
var portal = provider.GetRequiredService<IDataPortal<ReferenceData>>();
18+
var obj = portal.Create(1);
19+
Console.WriteLine($"{obj} from data portal");
20+
obj = portal.Create(1);
21+
Console.WriteLine($"{obj} from data portal");
22+
obj = portal.Create(2);
23+
Console.WriteLine($"{obj} from data portal");
24+
obj = portal.Create(2);
25+
Console.WriteLine($"{obj} from data portal");
26+
27+
Console.WriteLine($"{Environment.NewLine}Waiting...");
28+
await Task.Delay(TimeSpan.FromSeconds(6));
29+
30+
obj = portal.Create(1);
31+
Console.WriteLine($"{obj} from data portal");
32+
obj = portal.Create(2);
33+
Console.WriteLine($"{obj} from data portal");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Csla;
2+
3+
namespace DataPortalCacheExample
4+
{
5+
[Serializable]
6+
public class ReferenceData : BusinessBase<ReferenceData>
7+
{
8+
public static readonly PropertyInfo<int> IdProperty = RegisterProperty<int>(nameof(Id));
9+
public int Id
10+
{
11+
get => GetProperty(IdProperty);
12+
set => SetProperty(IdProperty, value);
13+
}
14+
public static readonly PropertyInfo<string> ValueProperty = RegisterProperty<string>(nameof(Value));
15+
public string Value
16+
{
17+
get => GetProperty(ValueProperty);
18+
set => SetProperty(ValueProperty, value);
19+
}
20+
21+
public override string ToString() => $"{Id}:{Value}";
22+
23+
[Create]
24+
private void Create(int id)
25+
{
26+
using (BypassPropertyChecks)
27+
Id = id;
28+
// return a unique value on each create
29+
Value = Guid.NewGuid().ToString();
30+
Console.WriteLine($"{this} !!! new item created");
31+
}
32+
}
33+
}

Source/Csla/Configuration/ConfigurationExtensions.cs

-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ public static IServiceCollection AddCsla(this IServiceCollection services, Actio
5050
RegisterContextManager(services);
5151
if (cslaOptions.ContextManagerType != null)
5252
services.AddScoped(typeof(Csla.Core.IContextManager), cslaOptions.ContextManagerType);
53-
services.TryAddScoped<IDataPortalCache, DataPortalCacheDefault>();
5453

5554
// Runtime Info defaults
5655
services.TryAddScoped(typeof(IRuntimeInfo), typeof(RuntimeInfo));

Source/Csla/Configuration/Fluent/DataPortalClientOptions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ public DataPortalClientOptions(DataPortalOptions dataPortalOptions)
4242
/// Gets or sets the type that implements
4343
/// IDataPortalCache for client-side caching.
4444
/// </summary>
45-
public Type DataPortalCacheType { get; set; } = typeof(DataPortalCacheDefault);
45+
public Type DataPortalCacheType { get; set; } = typeof(DataPortalNoCache);
4646
}
4747
}

Source/Csla/DataPortalClient/DataPortalCacheDefault.cs

-60
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="IDataPortalCache.cs" company="Marimer LLC">
3+
// Copyright (c) Marimer LLC. All rights reserved.
4+
// Website: https://cslanet.com
5+
// </copyright>
6+
// <summary>Null implementation of a client-side cache service</summary>
7+
//-----------------------------------------------------------------------
8+
using System;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using Csla.Server;
12+
13+
namespace Csla.DataPortalClient
14+
{
15+
/// <summary>
16+
/// Null implementation of a client-side cache service.
17+
/// </summary>
18+
public class DataPortalNoCache : IDataPortalCache
19+
{
20+
/// <summary>
21+
/// Always invokes the data portal delegate with
22+
/// no caching.
23+
/// </summary>
24+
/// <param name="objectType">Type of domain object to retrieve</param>
25+
/// <param name="criteria">Criteria for domain type being retrieved</param>
26+
/// <param name="operation">Data portal operation</param>
27+
/// <param name="portal">Data portal delegate</param>
28+
public async Task<DataPortalResult> GetDataPortalResultAsync(Type objectType, object criteria, DataPortalOperations operation, Func<Task<Server.DataPortalResult>> portal)
29+
=> await portal();
30+
}
31+
}

Source/Csla/DataPortalClient/IDataPortalCache.cs

+10-27
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,22 @@ namespace Csla.DataPortalClient
2020
public interface IDataPortalCache
2121
{
2222
/// <summary>
23-
/// Try to get result from cache.
23+
/// Get result from cache or data portal.
2424
/// </summary>
2525
/// <param name="objectType">Type of domain object to retrieve</param>
2626
/// <param name="criteria">Criteria for domain type being retrieved</param>
2727
/// <param name="operation">Data portal operation</param>
28-
/// <returns>null if not in cache</returns>
29-
Task<Server.DataPortalResult?> GetDataPortalResultAsync(Type objectType, object criteria, DataPortalOperations operation);
30-
/// <summary>
31-
/// Add object to cache.
32-
/// </summary>
33-
/// <param name="objectType">Type of domain object to add</param>
34-
/// <param name="criteria">Criteria for domain type being added</param>
35-
/// <param name="operation">Data portal operation</param>
36-
/// <param name="result">Data portal result to cache</param>
37-
/// <returns></returns>
38-
Task AddDataPortalResultAsync(Type objectType, object criteria, DataPortalOperations operation, Server.DataPortalResult result);
39-
/// <summary>
40-
/// Gets a value indicating whether the domain type
41-
/// can be cached.
42-
/// </summary>
43-
/// <param name="objectType">Type of domain object to add</param>
44-
/// <param name="criteria">Criteria for domain type being added</param>
45-
/// <param name="operation">Data portal operation</param>
46-
/// <returns></returns>
47-
bool IsCacheable(Type objectType, object criteria, DataPortalOperations operation);
48-
/// <summary>
49-
/// Gets a semaphore used by the data portal to only allow a single
50-
/// consumer/thread to get/add an item to the cache at a time.
51-
/// </summary>
28+
/// <param name="portal">Data portal delegate</param>
5229
/// <remarks>
53-
/// This semaphore must be a `new SemaphoreSlim(1)`
30+
/// The data portal invokes this method for each operation. The cache
31+
/// implementation may choose to return a result from the cache,
32+
/// or return a result by invoking the data portal delegate.
5433
/// </remarks>
55-
SemaphoreSlim Semaphore { get; }
34+
Task<Server.DataPortalResult> GetDataPortalResultAsync(
35+
Type objectType,
36+
object criteria,
37+
DataPortalOperations operation,
38+
Func<Task<Server.DataPortalResult>> portal);
5639
}
5740
}
5841
#nullable disable

0 commit comments

Comments
 (0)