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
87 changes: 87 additions & 0 deletions .github/scripts/start-ncache.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
#
# Brings up a single-node NCache Open Source server in Docker and starts the cache
# that the sample connects to. Exports NCACHE_SERVER and NCACHE_CACHE_NAME through
# $GITHUB_ENV so the test step can reach the cache.
#
# Requires NCACHE_REGISTRATION_KEY and NCACHE_REGISTRATION_EMAIL: NCache Open Source
# refuses to start a cache until it has been registered, and the key is only accepted
# together with the email address it was issued to.

set -euo pipefail

IMAGE="${NCACHE_IMAGE:-alachisoft/ncache:latest-oss}"
CONTAINER="${NCACHE_CONTAINER:-ncache}"
CACHE_NAME="${NCACHE_CACHE_NAME:-demoCache}"
TOOLS="/opt/ncache/bin/tools"

: "${NCACHE_REGISTRATION_KEY:?NCACHE_REGISTRATION_KEY is not set}"
: "${NCACHE_REGISTRATION_EMAIL:?NCACHE_REGISTRATION_EMAIL is not set}"

docker pull "$IMAGE"

# Host networking, not a published port. NCache binds to one address and hands that
# same address back to connecting clients, so a client that reached the server on a
# forwarded port is still redirected to the container's own address and fails.
docker run -d --name "$CONTAINER" --network host "$IMAGE"

echo "Waiting for the NCache management service..."
for attempt in $(seq 1 30); do
if docker exec "$CONTAINER" "$TOOLS/get-caches" >/dev/null 2>&1; then
break
fi

if [ "$attempt" -eq 30 ]; then
echo "::error::NCache management service did not come up in time."
docker logs "$CONTAINER"
exit 1
fi

sleep 2
done

# The tools report failures on stdout and do not always exit non-zero, so match on
# the success message rather than trusting the exit code.
echo "Registering NCache Open Source..."
registration=$(docker exec "$CONTAINER" "$TOOLS/register-ncacheopensource" \
-Key "$NCACHE_REGISTRATION_KEY" \
-Email "$NCACHE_REGISTRATION_EMAIL" \
-FirstName "${NCACHE_REGISTRATION_FIRSTNAME:-GitHub}" \
-LastName "${NCACHE_REGISTRATION_LASTNAME:-Actions}" \
-Company "${NCACHE_REGISTRATION_COMPANY:-Alachisoft}" 2>&1 || true)
echo "$registration"

if ! printf '%s' "$registration" | grep -qi "has been registered"; then
echo "::error::NCache registration failed. Check NCACHE_REGISTRATION_KEY and NCACHE_REGISTRATION_EMAIL."
exit 1
fi

echo "Starting cache '$CACHE_NAME'..."
start=$(docker exec "$CONTAINER" "$TOOLS/start-cache" -Name "$CACHE_NAME" 2>&1 || true)
echo "$start"

if ! printf '%s' "$start" | grep -qi "successfully started"; then
echo "::error::Failed to start cache '$CACHE_NAME'."
docker logs "$CONTAINER"
exit 1
fi

docker exec "$CONTAINER" "$TOOLS/get-caches"

# The container's startup script rewrites the .ncconf files with whichever address
# NCache bound to, and that is the address a client has to use. Read it back instead
# of assuming localhost.
server=$(docker exec "$CONTAINER" \
sed -n 's/.*local-server-ip="\([^"]*\)".*/\1/p' /opt/ncache/config/client.ncconf | head -n 1 | tr -d '\r')

if [ -z "$server" ]; then
echo "::error::Could not read local-server-ip from client.ncconf."
exit 1
fi

echo "NCache is listening on ${server}:9800"

{
echo "NCACHE_SERVER=${server}"
echo "NCACHE_CACHE_NAME=${CACHE_NAME}"
} >>"$GITHUB_ENV"
68 changes: 68 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Build

on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:

# A new push to the same PR/branch cancels the run that is still in progress.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
build:
name: Build solution
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
# The wrapper multi-targets netstandard2.0/net462 and the sample targets
# net8.0; the net462 reference assemblies come from NuGet during restore.
dotnet-version: 8.0.x

- name: Build
run: dotnet build NCache.OSS.StackExchange.Redis.sln --configuration Release

test:
name: Unit tests
# Linux, because the tests need a live NCache server and the NCache image is a
# Linux container. Windows runners cannot run it.
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

- name: Start NCache
env:
NCACHE_REGISTRATION_KEY: ${{ secrets.NCACHE_REGISTRATION_KEY }}
NCACHE_REGISTRATION_EMAIL: ${{ secrets.NCACHE_REGISTRATION_EMAIL }}
run: bash .github/scripts/start-ncache.sh

# The sample picks up NCACHE_SERVER and NCACHE_CACHE_NAME from the environment
# and returns a non-zero exit code when any test fails or throws.
- name: Run tests
run: dotnet run --project NCache.StackExchange.Redis.Sample/NCache.OSS.StackExchange.Redis.Sample.csproj --configuration Release

- name: NCache server logs
if: failure()
run: docker logs ncache

- name: Stop NCache
if: always()
run: docker rm --force ncache
1 change: 1 addition & 0 deletions NCache.StackExchange.Redis.Sample/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ internal static void PrintDataCacheException(Exception ex)
Console.Write("\n\n");
}

Program.erroredTests++;
Console.ForegroundColor = color;
}

Expand Down
55 changes: 49 additions & 6 deletions NCache.StackExchange.Redis.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using NCache.OSS.StackExchange.Redis.Sample.LockOperations;
using NCache.OSS.StackExchange.Redis.Sample.PubSubOperations;
using System;
using System.Collections.Generic;
using System.Configuration;

namespace BasicUsageStackExchangeRedis
Expand All @@ -19,30 +20,52 @@ class Program

public static int successfulTests = 0;
public static int failedTests = 0;
public static int erroredTests = 0;
public static INCacheDatabase db;
public static string myObjectForCaching = "This is my Object";
public static ICache cache;
public static ConnectionMultiplexer mux;

static void Main(string[] args)
static int Main(string[] args)
{
try
{
InitializeClient();
RunAllTests();

Console.WriteLine($"Test completed successfully.\nSuccessful Tests: {successfulTests}\nFailed Tests: {failedTests}\nPress any key to exit...");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
return 1;
}

Console.WriteLine($"Test run completed.\nSuccessful Tests: {successfulTests}\nFailed Tests: {failedTests}\nErrored Tests: {erroredTests}");

WaitForKeyPress();

return failedTests + erroredTests == 0 ? 0 : 1;
}

// for CI pipeline
private static void WaitForKeyPress()
{
if (Console.IsInputRedirected || Environment.GetEnvironmentVariable("CI") == "true")
{
return;
}

Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}

private static void InitializeClient()
{
string cacheName = ConfigurationManager.AppSettings["CacheId"];
string cacheName = Environment.GetEnvironmentVariable("NCACHE_CACHE_NAME");

if (string.IsNullOrWhiteSpace(cacheName))
{
cacheName = ConfigurationManager.AppSettings["CacheId"];
}

Microsoft.Extensions.Logging.ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
Expand All @@ -51,14 +74,34 @@ private static void InitializeClient()
.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Debug);
});

mux = ConnectionMultiplexer.Connect(cacheName);
mux = ConnectionMultiplexer.Connect(cacheName, GetConnectionOptions());

cache = mux.GetNCacheInterface(cacheName);
cache.Clear();

db = mux.GetDatabase();
}

private static CacheConnectionOptions GetConnectionOptions()
{
string server = Environment.GetEnvironmentVariable("NCACHE_SERVER");

if (string.IsNullOrWhiteSpace(server))
{
return null;
}

if (!int.TryParse(Environment.GetEnvironmentVariable("NCACHE_PORT"), out int port))
{
port = 9800;
}

return new CacheConnectionOptions
{
ServerList = new List<ServerInfo> { new ServerInfo(server, port) }
};
}

private static void RunAllTests()
{
RunKeyOperations();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
<PackageReference Include="System.Buffers" Version="4.6.1" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<!-- Supplies the .NET Framework reference assemblies so the net462 target builds
on any machine, instead of only where a matching targeting pack is installed.
Build-time only: it is not a dependency of the published package. -->
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<None Update="client.ncconf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# NCache OSS Wrapper for StackExchange.Redis

[![Build](https://github.com/Alachisoft/Redis-Wrapper/actions/workflows/build.yml/badge.svg)](https://github.com/Alachisoft/Redis-Wrapper/actions/workflows/build.yml)

A compatibility layer that enables .NET applications to use [NCache](https://www.alachisoft.com/ncache/) as a drop-in replacement for Redis with the familiar StackExchange.Redis API.

## Package Versions
Expand Down Expand Up @@ -92,6 +94,14 @@ ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>

The application acts as a test suite, targeting all APIs defined within the wrapper. Simply plug in your cache and run Program.cs.

It prints a pass/fail/error tally and exits with a non-zero code if anything failed or threw, so it can be used as a build gate. By default it reads the cache name from `App.config` and the server list from `client.ncconf`; these environment variables override that, which is how the workflow points it at a cache it starts itself:

| Variable | Purpose |
|---|---|
| `NCACHE_CACHE_NAME` | Cache to connect to, instead of the `CacheId` app setting |
| `NCACHE_SERVER` | Cache server address, instead of the `client.ncconf` server list |
| `NCACHE_PORT` | Client port, defaults to `9800` |

## License

Copyright © 2005-2026 Alachisoft. All rights reserved.
Expand Down
Loading