This is an automated email from the ASF dual-hosted git repository.
bbender pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-native.git
The following commit(s) were added to refs/heads/develop by this push:
new f379f6d GEODE-9359: Add NetCore SessionState support (#834)
f379f6d is described below
commit f379f6dd546c916be4d7f9e66c9842ff05448fb9
Author: Michael Martell <[email protected]>
AuthorDate: Thu Aug 12 08:25:36 2021 -0700
GEODE-9359: Add NetCore SessionState support (#834)
- fix formatting
- externalize log-level and log-file
- use auto implemented properties
- Use var everywhere
- Don't use private assets
---
.../NetCore.Session.IntegrationTests.csproj | 23 ++
.../Properties/launchSettings.json | 8 +
.../SessionStateIntegrationTests.cs | 175 ++++++++++++
.../GeodeCacheServiceCollectionExtensions.cs | 40 +++
.../GeodeSessionStateCacheOptions.cs | 31 +++
netcore/NetCore.Session/NetCore.Session.csproj | 17 ++
netcore/NetCore.Session/NetCoreSessionState.cs | 299 +++++++++++++++++++++
netcore/geode-dotnet-core.sln | 18 ++
8 files changed, 611 insertions(+)
diff --git
a/netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
b/netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
new file mode 100644
index 0000000..e0420cc
--- /dev/null
+++
b/netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+
+ <IsPackable>false</IsPackable>
+
+ <Platforms>x64</Platforms>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
+ <PackageReference Include="xunit" Version="2.4.1" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
+ <PackageReference Include="coverlet.collector" Version="1.2.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\NetCore.Session\NetCore.Session.csproj" />
+ <ProjectReference Include="..\NetCore\NetCore.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git
a/netcore/NetCore.Session.IntegrationTests/Properties/launchSettings.json
b/netcore/NetCore.Session.IntegrationTests/Properties/launchSettings.json
new file mode 100644
index 0000000..5d4ca80
--- /dev/null
+++ b/netcore/NetCore.Session.IntegrationTests/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Apache.Geode.Session.IntegrationTests": {
+ "commandName": "Project",
+ "nativeDebugging": true
+ }
+ }
+}
diff --git
a/netcore/NetCore.Session.IntegrationTests/SessionStateIntegrationTests.cs
b/netcore/NetCore.Session.IntegrationTests/SessionStateIntegrationTests.cs
new file mode 100644
index 0000000..d34002c
--- /dev/null
+++ b/netcore/NetCore.Session.IntegrationTests/SessionStateIntegrationTests.cs
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System;
+using System.Text;
+using Xunit;
+using Apache.Geode.Client;
+using System.Linq;
+using Microsoft.Extensions.Caching.Distributed;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session.IntegrationTests {
+ public class SessionStateIntegrationTests {
+
+ [Fact]
+ public void SetGet() {
+ var ssCacheOptions = new GeodeSessionStateCacheOptions();
+ ssCacheOptions.Host = "localhost";
+ ssCacheOptions.Port = 10334;
+ ssCacheOptions.RegionName = "exampleRegion";
+
+ using var ssCache = new GeodeSessionStateCache(ssCacheOptions);
+
+ var options = new DistributedCacheEntryOptions();
+ var localTime = DateTime.Now.AddDays(1);
+ var dateAndOffset =
+ new DateTimeOffset(localTime,
TimeZoneInfo.Local.GetUtcOffset(localTime));
+ options.AbsoluteExpiration = dateAndOffset;
+ var testValue = new byte[] { 1, 2, 3, 4, 5 };
+ ssCache.Set("testKey", testValue, options);
+ var value = ssCache.Get("testKey");
+ Assert.True(testValue.SequenceEqual(value));
+ }
+
+ [Fact]
+ public void Refresh() {
+ var ssCacheOptions = new GeodeSessionStateCacheOptions();
+ ssCacheOptions.Host = "localhost";
+ ssCacheOptions.Port = 10334;
+ ssCacheOptions.RegionName = "exampleRegion";
+
+ using var ssCache = new GeodeSessionStateCache(ssCacheOptions);
+
+ var options = new DistributedCacheEntryOptions();
+ var numSeconds = 20;
+ options.SlidingExpiration = new TimeSpan(0, 0, numSeconds);
+ var testValue = new byte[] { 1, 2, 3, 4, 5 };
+
+ // Set a value
+ ssCache.Set("testKey", testValue, options);
+
+ // Wait half a timeout then refresh
+ System.Threading.Thread.Sleep(numSeconds / 2 * 1000);
+ ssCache.Refresh("testKey");
+
+ // Wait beyond the original expiration
+ System.Threading.Thread.Sleep(numSeconds / 2 * 1000 + 1);
+
+ // Ensure it's not expired
+ var value = ssCache.Get("testKey");
+ Assert.True(testValue.SequenceEqual(value));
+ }
+
+ [Fact]
+ public void SetWithAbsoluteExpiration() {
+ var ssCacheOptions = new GeodeSessionStateCacheOptions();
+ ssCacheOptions.Host = "localhost";
+ ssCacheOptions.Port = 10334;
+ ssCacheOptions.RegionName = "exampleRegion";
+
+ using var ssCache = new GeodeSessionStateCache(ssCacheOptions);
+
+ var options = new DistributedCacheEntryOptions();
+ options.AbsoluteExpiration = DateTime.Now.AddSeconds(5);
+ ssCache.Set("testKey", Encoding.UTF8.GetBytes("testValue"), options);
+ System.Threading.Thread.Sleep(6000);
+ var value = ssCache.Get("testKey");
+ Assert.Null(value);
+ }
+
+ [Fact]
+ public void Remove() {
+ var ssCacheOptions = new GeodeSessionStateCacheOptions();
+ ssCacheOptions.Host = "localhost";
+ ssCacheOptions.Port = 10334;
+ ssCacheOptions.RegionName = "exampleRegion";
+
+ using var ssCache = new GeodeSessionStateCache(ssCacheOptions);
+
+ var options = new DistributedCacheEntryOptions();
+ var localTime = DateTime.Now.AddDays(1);
+ var dateAndOffset =
+ new DateTimeOffset(localTime,
TimeZoneInfo.Local.GetUtcOffset(localTime));
+ options.AbsoluteExpiration = dateAndOffset;
+ var testValue = new byte[] { 1, 2, 3, 4, 5 };
+ ssCache.Set("testKey", testValue, options);
+ var value = ssCache.Get("testKey");
+
+ ssCache.Remove("testKey");
+ value = ssCache.Get("testKey");
+ Assert.Null(value);
+ }
+
+ [Fact]
+ public void SetGetRemoveAsync() {
+ var ssCacheOptions = new GeodeSessionStateCacheOptions();
+ ssCacheOptions.Host = "localhost";
+ ssCacheOptions.Port = 10334;
+ ssCacheOptions.RegionName = "exampleRegion";
+
+ using var ssCache = new GeodeSessionStateCache(ssCacheOptions);
+
+ var options = new DistributedCacheEntryOptions();
+ var localTime = DateTime.Now.AddDays(1);
+ var dateAndOffset =
+ new DateTimeOffset(localTime,
TimeZoneInfo.Local.GetUtcOffset(localTime));
+ options.AbsoluteExpiration = dateAndOffset;
+
+ var testValue1 = new byte[] { 1, 2, 3, 4, 5 };
+ var testValue2 = new byte[] { 11, 12, 13, 14, 15 };
+ var testValue3 = new byte[] { 21, 22, 23, 24, 25 };
+ var testValue4 = new byte[] { 31, 32, 33, 34, 35 };
+ var testValue5 = new byte[] { 41, 42, 43, 44, 45 };
+
+ var set1 = ssCache.SetAsync("testKey1", testValue1, options);
+ var set2 = ssCache.SetAsync("testKey2", testValue2, options);
+ var set3 = ssCache.SetAsync("testKey3", testValue3, options);
+ var set4 = ssCache.SetAsync("testKey4", testValue4, options);
+ var set5 = ssCache.SetAsync("testKey5", testValue5, options);
+
+ Task.WaitAll(set1, set2, set3, set4, set5);
+
+ var value1 = ssCache.GetAsync("testKey1");
+ var value2 = ssCache.GetAsync("testKey2");
+ var value3 = ssCache.GetAsync("testKey3");
+ var value4 = ssCache.GetAsync("testKey4");
+ var value5 = ssCache.GetAsync("testKey5");
+
+ Task.WaitAll(value1, value2, value3, value4, value5);
+
+ Assert.True(testValue1.SequenceEqual(value1.Result));
+ Assert.True(testValue2.SequenceEqual(value2.Result));
+ Assert.True(testValue3.SequenceEqual(value3.Result));
+ Assert.True(testValue4.SequenceEqual(value4.Result));
+ Assert.True(testValue5.SequenceEqual(value5.Result));
+
+ var rm1 = ssCache.RemoveAsync("testKey1");
+ var rm2 = ssCache.RemoveAsync("testKey2");
+ var rm3 = ssCache.RemoveAsync("testKey3");
+ var rm4 = ssCache.RemoveAsync("testKey4");
+ var rm5 = ssCache.RemoveAsync("testKey5");
+
+ Task.WaitAll(rm1, rm2, rm3, rm4, rm5);
+
+ Assert.Null(ssCache.Get("testKey1"));
+ Assert.Null(ssCache.Get("testKey2"));
+ Assert.Null(ssCache.Get("testKey3"));
+ Assert.Null(ssCache.Get("testKey4"));
+ Assert.Null(ssCache.Get("testKey5"));
+ }
+ }
+}
diff --git a/netcore/NetCore.Session/GeodeCacheServiceCollectionExtensions.cs
b/netcore/NetCore.Session/GeodeCacheServiceCollectionExtensions.cs
new file mode 100644
index 0000000..113d079
--- /dev/null
+++ b/netcore/NetCore.Session/GeodeCacheServiceCollectionExtensions.cs
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Apache.Geode.Session {
+ public static class GeodeCacheServiceCollectionExtensions {
+ public static IServiceCollection AddGeodeSessionStateCache(
+ this IServiceCollection services,
Action<GeodeSessionStateCacheOptions> setupAction) {
+ if (services == null) {
+ throw new ArgumentNullException(nameof(services));
+ }
+
+ if (setupAction == null) {
+ throw new ArgumentNullException(nameof(setupAction));
+ }
+
+ services.AddOptions();
+ services.Add(ServiceDescriptor.Singleton<IDistributedCache,
GeodeSessionStateCache>());
+ services.Configure(setupAction);
+
+ return services;
+ }
+ }
+}
diff --git a/netcore/NetCore.Session/GeodeSessionStateCacheOptions.cs
b/netcore/NetCore.Session/GeodeSessionStateCacheOptions.cs
new file mode 100644
index 0000000..d07e068
--- /dev/null
+++ b/netcore/NetCore.Session/GeodeSessionStateCacheOptions.cs
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Microsoft.Extensions.Options;
+
+namespace Apache.Geode.Session {
+ public class GeodeSessionStateCacheOptions :
IOptions<GeodeSessionStateCacheOptions> {
+ public string RegionName { get; set; }
+ public string Host { get; set; }
+ public int Port { get; set; }
+ public string LogLevel { get; set; } = "none";
+ public string LogFile { get; set; } = "GeodeSessionStateCache.log";
+
+ GeodeSessionStateCacheOptions
IOptions<GeodeSessionStateCacheOptions>.Value {
+ get { return this; }
+ }
+ }
+}
diff --git a/netcore/NetCore.Session/NetCore.Session.csproj
b/netcore/NetCore.Session/NetCore.Session.csproj
new file mode 100644
index 0000000..29e13a3
--- /dev/null
+++ b/netcore/NetCore.Session/NetCore.Session.csproj
@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+ <Platforms>x64</Platforms>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.Caching.Abstractions"
Version="3.1.3" />
+ <PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.3" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\NetCore\NetCore.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/netcore/NetCore.Session/NetCoreSessionState.cs
b/netcore/NetCore.Session/NetCoreSessionState.cs
new file mode 100644
index 0000000..95b0434
--- /dev/null
+++ b/netcore/NetCore.Session/NetCoreSessionState.cs
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+ public class GeodeSessionStateValue {
+ public GeodeSessionStateValue() {}
+ public GeodeSessionStateValue(byte[] value) {
+ FromByteArray(value);
+ }
+
+ public byte[] Value { get; set; }
+ public DateTime LastAccessTimeUtc { get; set; }
+ public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+ public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+ public byte[] ToByteArray() {
+ var neededBytes = 3 * sizeof(long) + Value.Length;
+ var byteArray = new byte[neededBytes];
+ var byteIndex = 0;
+
+ Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray,
byteIndex,
+ sizeof(long));
+ byteIndex += sizeof(long);
+
+ Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray,
byteIndex,
+ sizeof(long));
+ byteIndex += sizeof(long);
+
+ Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray,
byteIndex,
+ sizeof(long));
+ byteIndex += sizeof(long);
+
+ Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+ return byteArray;
+ }
+
+ public void FromByteArray(byte[] data) {
+ var byteIndex = 0;
+
+ LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data,
byteIndex));
+ byteIndex += sizeof(long);
+
+ ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data,
byteIndex));
+ byteIndex += sizeof(long);
+
+ SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data,
byteIndex));
+ byteIndex += sizeof(long);
+
+ Value = new byte[data.Length - byteIndex];
+ Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+ }
+ }
+
+ public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+ private readonly IGeodeCache _cache;
+ private ILogger<GeodeSessionStateCache> _logger;
+ private static Region _region;
+ private string _logLevel;
+ private string _logFile;
+ private string _regionName;
+ private readonly SemaphoreSlim _connectLock = new
SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+ public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions>
optionsAccessor) {
+ var host = optionsAccessor.Value.Host;
+ var port = optionsAccessor.Value.Port;
+ _regionName = optionsAccessor.Value.RegionName;
+ _logLevel = optionsAccessor.Value.LogLevel;
+ _logFile = optionsAccessor.Value.LogFile;
+
+ _cache = CacheFactory.Create()
+ .SetProperty("log-level", _logLevel)
+ .SetProperty("log-file", _logFile)
+ .CreateCache();
+
+ _cache.PoolManager.CreatePoolFactory().AddLocator(host,
port).CreatePool("pool");
+
+ var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+ _region = regionFactory.CreateRegion(_regionName);
+ }
+
+ // Returns the SessionStateValue for key, or null if key doesn't exist
+ public GeodeSessionStateValue GetValueForKey(string key) {
+ var cacheValue = _region.GetByteArray(key);
+
+ if (cacheValue != null) {
+ return new GeodeSessionStateValue(cacheValue);
+ } else
+ return null;
+ }
+
+ public byte[] Get(string key) {
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ Connect();
+
+ // Check for nonexistent key
+ var ssValue = GetValueForKey(key);
+ if (ssValue == null) {
+ return null;
+ }
+
+ // Check for expired key
+ var nowUtc = DateTime.UtcNow;
+ if (ssValue.ExpirationTimeUtc != DateTime.MinValue &&
ssValue.ExpirationTimeUtc < nowUtc) {
+ return null;
+ }
+
+ // Check for stale key
+ if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+ nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+ return null;
+ }
+
+ // Update the times for sliding expirations
+ if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+ ssValue.LastAccessTimeUtc = nowUtc;
+ _region.PutByteArray(key, ssValue.ToByteArray());
+ }
+
+ return ssValue.Value;
+ }
+
+ public Task<byte[]> GetAsync(string key, CancellationToken token =
default(CancellationToken)) {
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ return Task.Factory.StartNew(() => Get(key), token);
+ }
+
+ public void Refresh(string key) {
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ Connect();
+
+ // Check for nonexistent key
+ var ssValue = GetValueForKey(key);
+ if (ssValue == null) {
+ return;
+ }
+
+ // Check for expired key
+ var nowUtc = DateTime.UtcNow;
+ if (ssValue.ExpirationTimeUtc != DateTime.MinValue &&
ssValue.ExpirationTimeUtc < nowUtc) {
+ return;
+ }
+
+ // Check for stale key
+ if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+ nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+ return;
+ }
+
+ // Update the times for sliding expirations
+ if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+ ssValue.LastAccessTimeUtc = nowUtc;
+ _region.PutByteArray(key, ssValue.ToByteArray());
+ }
+ }
+
+ public Task RefreshAsync(string key, CancellationToken token =
default(CancellationToken)) {
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ return Task.Factory.StartNew(() => Refresh(key), token);
+ }
+
+ public void Remove(string key) {
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ Connect();
+
+ _region.Remove(key);
+ }
+
+ public Task RemoveAsync(string key, CancellationToken token =
default(CancellationToken)) {
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ return Task.Factory.StartNew(() => Remove(key), token);
+ }
+
+ public void Set(string key, byte[] value, DistributedCacheEntryOptions
options) {
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ if (value == null) {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ if (options == null) {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ Connect();
+
+ var ssValue = new GeodeSessionStateValue();
+ ssValue.Value = value;
+
+ var nowUtc = DateTime.UtcNow;
+ ssValue.LastAccessTimeUtc = nowUtc;
+
+ // No need to check stale or expired data when setting an absolute
expiration.
+ // Think of if as setting a new key/value pair. Expired data will always
be cleaned up
+ // when the CleanupExpiredData job runs.
+
+ if (options.AbsoluteExpiration != null) {
+ var dto = options.AbsoluteExpiration.Value;
+ ssValue.ExpirationTimeUtc = dto.DateTime + dto.Offset;
+ }
+
+ // If AbsoluteExpiration and AbsoluteExpirationRelativeToNow are set,
use the latter.
+ if (options.AbsoluteExpirationRelativeToNow != null) {
+ var ts = options.AbsoluteExpirationRelativeToNow.Value;
+ ssValue.ExpirationTimeUtc = nowUtc + ts;
+ }
+
+ if (options.SlidingExpiration != null) {
+ ssValue.SpanUntilStale = options.SlidingExpiration.Value;
+ }
+
+ _region.PutByteArray(key, ssValue.ToByteArray());
+ return;
+ }
+
+ public Task SetAsync(string key, byte[] value,
DistributedCacheEntryOptions options,
+ CancellationToken token = default(CancellationToken))
{
+ if (key == null) {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ return Task.Factory.StartNew(() => Set(key, value, options), token);
+ }
+
+ private void Connect() {
+ if (_region != null) {
+ return;
+ }
+
+ _connectLock.Wait();
+ try {
+ using var regionFactory =
_cache.CreateRegionFactory(RegionShortcut.Proxy);
+ try {
+ _logger?.LogTrace("Create CacheRegion");
+ _region = regionFactory.CreateRegion(_regionName);
+ _logger?.LogTrace("CacheRegion created");
+ } catch (Exception e) {
+ _logger?.LogInformation(e, "Create CacheRegion failed... now trying
to get the region");
+ }
+ } finally {
+ // regionFactory?.Dispose();
+ _connectLock.Release();
+ }
+ }
+
+ protected override void DestroyContainedObject() {
+ _region?.Dispose();
+ _region = null;
+ }
+ }
+}
diff --git a/netcore/geode-dotnet-core.sln b/netcore/geode-dotnet-core.sln
index 830ad88..164413c 100644
--- a/netcore/geode-dotnet-core.sln
+++ b/netcore/geode-dotnet-core.sln
@@ -7,6 +7,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore",
"NetCore\NetCore.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test",
"NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session",
"{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session",
"NetCore.Session\NetCore.Session.csproj",
"{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") =
"NetCore.Session.IntegrationTests",
"NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj",
"{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -21,10 +27,22 @@ Global
{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}.Debug|x64.Build.0 =
Debug|x64
{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}.Release|x64.ActiveCfg =
Release|x64
{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}.Release|x64.Build.0 =
Release|x64
+ {B88C58EB-B144-403B-85F7-7A5B45E643E3}.Debug|x64.ActiveCfg =
Debug|x64
+ {B88C58EB-B144-403B-85F7-7A5B45E643E3}.Debug|x64.Build.0 =
Debug|x64
+ {B88C58EB-B144-403B-85F7-7A5B45E643E3}.Release|x64.ActiveCfg =
Release|x64
+ {B88C58EB-B144-403B-85F7-7A5B45E643E3}.Release|x64.Build.0 =
Release|x64
+ {94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}.Debug|x64.ActiveCfg =
Debug|x64
+ {94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}.Debug|x64.Build.0 =
Debug|x64
+ {94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}.Release|x64.ActiveCfg =
Release|x64
+ {94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}.Release|x64.Build.0 =
Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {B88C58EB-B144-403B-85F7-7A5B45E643E3} =
{520C96EC-F929-4365-8D78-CC5785419B62}
+ {94D2CD59-A5F3-4504-BF01-0A3B95CE12B5} =
{520C96EC-F929-4365-8D78-CC5785419B62}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B30A49F0-1C96-4D6C-A222-0088B1D7FBBE}
EndGlobalSection