Commit 2712ccf9 authored by Marc Gravell's avatar Marc Gravell

Merge branch 'netcore' of https://github.com/jeremymeng/StackExchange.Redis into jeremymeng-netcore

parents 9a4e4087 53d5028b
.hg .hg
bin bin
bin.snk bin.snk
obj obj
*.suo *.suo
*.user *.user
*.nupkg *.nupkg
packages/NuGet.CommandLine.* packages/NuGet.CommandLine.*
*.sln.docstates *.sln.docstates
_ReSharper.* _ReSharper.*
Mono/ Mono/
*.sln.ide *.sln.ide
*.rdb *.rdb
*.aof *.aof
*.orig *.orig
redis-cli.exe redis-cli.exe
Redis Configs/*.dat Redis Configs/*.dat
RedisQFork*.dat RedisQFork*.dat
StackExchange.Redis.*.zip StackExchange.Redis.*.zip
\ No newline at end of file .vs/
*.lock.json
\ No newline at end of file
...@@ -35,9 +35,17 @@ static void MassiveBulkOpsAsync(int AsyncOpsQty, bool preserveOrder, bool withCo ...@@ -35,9 +35,17 @@ static void MassiveBulkOpsAsync(int AsyncOpsQty, bool preserveOrder, bool withCo
var conn = muxer.GetDatabase(); var conn = muxer.GetDatabase();
muxer.Wait(conn.PingAsync()); muxer.Wait(conn.PingAsync());
int number = 0;
Action<Task> nonTrivial = delegate Action<Task> nonTrivial = delegate
{ {
#if !DNXCORE50
Thread.SpinWait(5); Thread.SpinWait(5);
#else
for (int i = 0; i < 50; i++)
{
number++;
}
#endif
}; };
var watch = Stopwatch.StartNew(); var watch = Stopwatch.StartNew();
for (int i = 0; i <= AsyncOpsQty; i++) for (int i = 0; i <= AsyncOpsQty; i++)
......
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>9d83baba-a92e-495f-bf63-deb4f6b09355</ProjectGuid>
<RootNamespace>BasicTest_dnxcore50</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
\ No newline at end of file
{
"version": "1.0.0-*",
"description": "StackExchange.Redis.BasicTest dnxcore50",
"authors": [ "jeremymeng" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"compile": [
"../BasicTest/Program.cs"
],
"dependencies": {
"StackExchange.Redis": "1.1.0-*"
},
"commands": {
"run": "BasicTest_dnxcore50"
},
"configurations": {
"Debug": {
"compilationOptions": {
"define": [ "DEBUG", "TRACE" ]
}
},
"Release": {
"compilationOptions": {
"define": [ "TRACE" ]
}
}
},
"frameworks": {
"dnxcore50": {
"dependencies": {
"System.Console": "4.0.0-beta-23409"
}
}
}
}
...@@ -122,25 +122,36 @@ public void CanOpenSecuredConnection() ...@@ -122,25 +122,36 @@ public void CanOpenSecuredConnection()
} }
} }
[Test, ExpectedException(typeof(RedisConnectionException))] [Test]
public void CanNotOpenNonsenseConnection_IP() public void CanNotOpenNonsenseConnection_IP()
{ {
var log = new StringWriter(); Assert.Throws<RedisConnectionException>(() =>
try { {
using (var conn = ConnectionMultiplexer.Connect(Config.LocalHost + ":6500")) { } var log = new StringWriter();
} finally { try {
Console.WriteLine(log); using (var conn = ConnectionMultiplexer.Connect(Config.LocalHost + ":6500")) { }
} }
finally {
Console.WriteLine(log);
}
});
} }
[Test, ExpectedException(typeof(RedisConnectionException))]
[Test]
public void CanNotOpenNonsenseConnection_DNS() public void CanNotOpenNonsenseConnection_DNS()
{ {
var log = new StringWriter(); Assert.Throws<RedisConnectionException>(() =>
try { {
using (var conn = ConnectionMultiplexer.Connect("doesnot.exist.ds.aasd981230d.com:6500", log)) { } var log = new StringWriter();
} finally { try
Console.WriteLine(log); {
} using (var conn = ConnectionMultiplexer.Connect("doesnot.exist.ds.aasd981230d.com:6500", log)) { }
}
finally
{
Console.WriteLine(log);
}
});
} }
[Test] [Test]
......
...@@ -55,28 +55,33 @@ public void ExecuteWithEmptyStartingPoint() ...@@ -55,28 +55,33 @@ public void ExecuteWithEmptyStartingPoint()
} }
} }
[Test, ExpectedException(typeof(RedisServerException), ExpectedMessage = "WRONGTYPE Operation against a key holding the wrong kind of value")] [Test]
public void ExecuteWithNonHashStartingPoint() public void ExecuteWithNonHashStartingPoint()
{ {
using (var muxer = Config.GetUnsecuredConnection()) Assert.Throws<RedisConnectionException>(() =>
{ {
var conn = muxer.GetDatabase(0); using (var muxer = Config.GetUnsecuredConnection())
var task = new { priority = 3 }; {
conn.KeyDeleteAsync("item:1"); var conn = muxer.GetDatabase(0);
conn.StringSetAsync("item:1", "not a hash"); var task = new { priority = 3 };
conn.HashSetAsync("item:1", "priority", task.priority.ToString()); conn.KeyDeleteAsync("item:1");
conn.StringSetAsync("item:1", "not a hash");
conn.HashSetAsync("item:1", "priority", task.priority.ToString());
var taskResult = conn.HashGetAsync("item:1", "priority"); var taskResult = conn.HashGetAsync("item:1", "priority");
try try
{ {
conn.Wait(taskResult); conn.Wait(taskResult);
Assert.Fail(); Assert.Fail();
} catch(AggregateException ex) }
{ catch (AggregateException ex)
throw ex.InnerExceptions[0]; {
throw ex.InnerExceptions[0];
}
} }
} },
message: "WRONGTYPE Operation against a key holding the wrong kind of value");
} }
} }
} }
...@@ -44,9 +44,9 @@ ...@@ -44,9 +44,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=3.0.5797.27534, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <HintPath>..\packages\NUnit.3.0.0\lib\net45\nunit.framework.dll</HintPath>
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
......
...@@ -100,7 +100,6 @@ where Attribute.IsDefined(method, typeof(TestAttribute)) ...@@ -100,7 +100,6 @@ where Attribute.IsDefined(method, typeof(TestAttribute))
foreach (var test in tests) foreach (var test in tests)
{ {
var expectedFail = Attribute.GetCustomAttribute(test, typeof(ExpectedExceptionAttribute)) as ExpectedExceptionAttribute;
Console.Write(test.Name + ": "); Console.Write(test.Name + ": ");
Exception err = null; Exception err = null;
...@@ -131,35 +130,6 @@ where Attribute.IsDefined(method, typeof(TestAttribute)) ...@@ -131,35 +130,6 @@ where Attribute.IsDefined(method, typeof(TestAttribute))
err = ((AggregateException)err).InnerExceptions[0]; err = ((AggregateException)err).InnerExceptions[0];
} }
if (expectedFail != null)
{
if (err == null)
{
err = new NUnit.Framework.AssertionException("failed to fail");
}
else
{
int issues = 0;
if (expectedFail.ExpectedException != null && !expectedFail.ExpectedException.IsAssignableFrom(err.GetType()))
{
issues++;
}
if (expectedFail.ExpectedExceptionName != null && err.GetType().FullName != expectedFail.ExpectedExceptionName)
{
issues++;
}
if (expectedFail.ExpectedMessage != null && err.Message != expectedFail.ExpectedMessage)
{
issues++;
}
if (issues == 0) err = null;
else
{
err = new InvalidOperationException("Failed in a different way", err);
}
}
}
if (err == null) if (err == null)
{ {
Console.WriteLine("pass"); Console.WriteLine("pass");
......
...@@ -264,21 +264,26 @@ public void NonAsciiScripts() ...@@ -264,21 +264,26 @@ public void NonAsciiScripts()
} }
} }
[Test, ExpectedException(typeof(RedisServerException), ExpectedMessage = "oops")] [Test]
public void ScriptThrowsError() public void ScriptThrowsError()
{ {
using (var muxer = GetScriptConn()) Assert.Throws<RedisServerException>(() =>
{ {
var conn = muxer.GetDatabase(0); using (var muxer = GetScriptConn())
var result = conn.ScriptEvaluateAsync("return redis.error_reply('oops')", null, null);
try
{ {
conn.Wait(result); var conn = muxer.GetDatabase(0);
} catch(AggregateException ex) var result = conn.ScriptEvaluateAsync("return redis.error_reply('oops')", null, null);
{ try
throw ex.InnerExceptions[0]; {
conn.Wait(result);
}
catch (AggregateException ex)
{
throw ex.InnerExceptions[0];
}
} }
} },
message: "oops");
} }
[Test] [Test]
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="NUnit" version="2.6.4" targetFramework="net45" /> <package id="NUnit" version="3.0.0" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BookSleeve; #if FEATURE_BOOKSLEEVE
using NUnit.Framework; using BookSleeve;
using StackExchange.Redis.KeyspaceIsolation; #endif
namespace StackExchange.Redis.Tests using NUnit.Framework;
{ using StackExchange.Redis.KeyspaceIsolation;
[TestFixture] namespace StackExchange.Redis.Tests
public class BasicOpsTests : TestBase {
{ [TestFixture]
[Test] public class BasicOpsTests : TestBase
[TestCase(true)] {
[TestCase(false)] [Test]
public void PingOnce(bool preserveOrder) [TestCase(true)]
{ [TestCase(false)]
using (var muxer = Create()) public void PingOnce(bool preserveOrder)
{ {
muxer.PreserveAsyncOrder = preserveOrder; using (var muxer = Create())
var conn = muxer.GetDatabase(); {
muxer.PreserveAsyncOrder = preserveOrder;
var task = conn.PingAsync(); var conn = muxer.GetDatabase();
var duration = muxer.Wait(task);
Console.WriteLine("Ping took: " + duration); var task = conn.PingAsync();
Assert.IsTrue(duration.TotalMilliseconds > 0); var duration = muxer.Wait(task);
} Console.WriteLine("Ping took: " + duration);
} Assert.IsTrue(duration.TotalMilliseconds > 0);
}
[Test] }
[TestCase(true)]
[TestCase(false)] [Test]
public void RapidDispose(bool preserverOrder) [TestCase(true)]
{ [TestCase(false)]
RedisKey key = Me(); public void RapidDispose(bool preserverOrder)
using (var primary = Create()) {
{ RedisKey key = Me();
var conn = primary.GetDatabase(); using (var primary = Create())
conn.KeyDelete(key); {
var conn = primary.GetDatabase();
for (int i = 0; i < 10; i++) conn.KeyDelete(key);
{
using (var secondary = Create(fail: true)) for (int i = 0; i < 10; i++)
{ {
secondary.GetDatabase().StringIncrement(key, flags: CommandFlags.FireAndForget); using (var secondary = Create(fail: true))
} {
} secondary.GetDatabase().StringIncrement(key, flags: CommandFlags.FireAndForget);
}
Assert.AreEqual(10, (int)conn.StringGet(key)); }
}
} Assert.AreEqual(10, (int)conn.StringGet(key));
}
[Test] }
[TestCase(true)]
[TestCase(false)] [Test]
public void PingMany(bool preserveOrder) [TestCase(true)]
{ [TestCase(false)]
using (var muxer = Create()) public void PingMany(bool preserveOrder)
{ {
muxer.PreserveAsyncOrder = preserveOrder; using (var muxer = Create())
var conn = muxer.GetDatabase(); {
var tasks = new Task<TimeSpan>[10000]; muxer.PreserveAsyncOrder = preserveOrder;
var conn = muxer.GetDatabase();
for (int i = 0; i < tasks.Length; i++) var tasks = new Task<TimeSpan>[10000];
{
tasks[i] = conn.PingAsync(); for (int i = 0; i < tasks.Length; i++)
} {
muxer.WaitAll(tasks); tasks[i] = conn.PingAsync();
Assert.IsTrue(tasks[0].Result.TotalMilliseconds > 0); }
Assert.IsTrue(tasks[tasks.Length - 1].Result.TotalMilliseconds > 0); muxer.WaitAll(tasks);
} Assert.IsTrue(tasks[0].Result.TotalMilliseconds > 0);
} Assert.IsTrue(tasks[tasks.Length - 1].Result.TotalMilliseconds > 0);
}
[Test] }
[ExpectedException(typeof(ArgumentException), ExpectedMessage = @"A null key is not valid in this context")]
public void GetWithNullKey() [Test]
{ public void GetWithNullKey()
using (var muxer = Create()) {
{ using (var muxer = Create())
var db = muxer.GetDatabase(); {
string key = null; var db = muxer.GetDatabase();
db.StringGet(key); string key = null;
} Assert.Throws<ArgumentException>(
} () => db.StringGet(key),
"A null key is not valid in this context");
[Test] }
[ExpectedException(typeof(ArgumentException), ExpectedMessage = @"A null key is not valid in this context")] }
public void SetWithNullKey()
{ [Test]
using (var muxer = Create()) public void SetWithNullKey()
{ {
var db = muxer.GetDatabase(); using (var muxer = Create())
string key = null, value = "abc"; {
db.StringSet(key, value); var db = muxer.GetDatabase();
} string key = null, value = "abc";
} Assert.Throws<ArgumentException>(
() => db.StringSet(key, value),
[Test] "A null key is not valid in this context");
public void SetWithNullValue() }
{ }
using (var muxer = Create())
{ [Test]
var db = muxer.GetDatabase(); public void SetWithNullValue()
string key = Me(), value = null; {
db.KeyDelete(key, CommandFlags.FireAndForget); using (var muxer = Create())
{
db.StringSet(key, "abc", flags: CommandFlags.FireAndForget); var db = muxer.GetDatabase();
Assert.IsTrue(db.KeyExists(key)); string key = Me(), value = null;
db.StringSet(key, value); db.KeyDelete(key, CommandFlags.FireAndForget);
var actual = (string)db.StringGet(key); db.StringSet(key, "abc", flags: CommandFlags.FireAndForget);
Assert.IsNull(actual); Assert.IsTrue(db.KeyExists(key));
Assert.IsFalse(db.KeyExists(key)); db.StringSet(key, value);
}
} var actual = (string)db.StringGet(key);
Assert.IsNull(actual);
[Test] Assert.IsFalse(db.KeyExists(key));
public void SetWithDefaultValue() }
{ }
using (var muxer = Create())
{ [Test]
var db = muxer.GetDatabase(); public void SetWithDefaultValue()
string key = Me(); {
var value = default(RedisValue); // this is kinda 0... ish using (var muxer = Create())
db.KeyDelete(key, CommandFlags.FireAndForget); {
var db = muxer.GetDatabase();
db.StringSet(key, "abc", flags: CommandFlags.FireAndForget); string key = Me();
Assert.IsTrue(db.KeyExists(key)); var value = default(RedisValue); // this is kinda 0... ish
db.StringSet(key, value); db.KeyDelete(key, CommandFlags.FireAndForget);
var actual = (string)db.StringGet(key); db.StringSet(key, "abc", flags: CommandFlags.FireAndForget);
Assert.IsNull(actual); Assert.IsTrue(db.KeyExists(key));
Assert.IsFalse(db.KeyExists(key)); db.StringSet(key, value);
}
} var actual = (string)db.StringGet(key);
Assert.IsNull(actual);
[Test] Assert.IsFalse(db.KeyExists(key));
public void SetWithZeroValue() }
{ }
using (var muxer = Create())
{ [Test]
var db = muxer.GetDatabase(); public void SetWithZeroValue()
string key = Me(); {
long value = 0; using (var muxer = Create())
db.KeyDelete(key, CommandFlags.FireAndForget); {
var db = muxer.GetDatabase();
db.StringSet(key, "abc", flags: CommandFlags.FireAndForget); string key = Me();
Assert.IsTrue(db.KeyExists(key)); long value = 0;
db.StringSet(key, value); db.KeyDelete(key, CommandFlags.FireAndForget);
var actual = (string)db.StringGet(key); db.StringSet(key, "abc", flags: CommandFlags.FireAndForget);
Assert.AreEqual("0", actual); Assert.IsTrue(db.KeyExists(key));
Assert.IsTrue(db.KeyExists(key)); db.StringSet(key, value);
}
} var actual = (string)db.StringGet(key);
Assert.AreEqual("0", actual);
[Test] Assert.IsTrue(db.KeyExists(key));
[TestCase(true)] }
[TestCase(false)] }
public void GetSetAsync(bool preserveOrder)
{ [Test]
using (var muxer = Create()) [TestCase(true)]
{ [TestCase(false)]
muxer.PreserveAsyncOrder = preserveOrder; public void GetSetAsync(bool preserveOrder)
var conn = muxer.GetDatabase(); {
using (var muxer = Create())
RedisKey key = Me(); {
var d0 = conn.KeyDeleteAsync(key); muxer.PreserveAsyncOrder = preserveOrder;
var d1 = conn.KeyDeleteAsync(key); var conn = muxer.GetDatabase();
var g1 = conn.StringGetAsync(key);
var s1 = conn.StringSetAsync(key, "123"); RedisKey key = Me();
var g2 = conn.StringGetAsync(key); var d0 = conn.KeyDeleteAsync(key);
var d2 = conn.KeyDeleteAsync(key); var d1 = conn.KeyDeleteAsync(key);
var g1 = conn.StringGetAsync(key);
muxer.Wait(d0); var s1 = conn.StringSetAsync(key, "123");
Assert.IsFalse(muxer.Wait(d1)); var g2 = conn.StringGetAsync(key);
Assert.IsNull((string)muxer.Wait(g1)); var d2 = conn.KeyDeleteAsync(key);
Assert.IsTrue(muxer.Wait(g1).IsNull);
muxer.Wait(s1); muxer.Wait(d0);
Assert.AreEqual("123", (string)muxer.Wait(g2)); Assert.IsFalse(muxer.Wait(d1));
Assert.AreEqual(123, (int)muxer.Wait(g2)); Assert.IsNull((string)muxer.Wait(g1));
Assert.IsFalse(muxer.Wait(g2).IsNull); Assert.IsTrue(muxer.Wait(g1).IsNull);
Assert.IsTrue(muxer.Wait(d2)); muxer.Wait(s1);
} Assert.AreEqual("123", (string)muxer.Wait(g2));
} Assert.AreEqual(123, (int)muxer.Wait(g2));
Assert.IsFalse(muxer.Wait(g2).IsNull);
[Test] Assert.IsTrue(muxer.Wait(d2));
[TestCase(true)] }
[TestCase(false)] }
public void GetSetSync(bool preserveOrder)
{ [Test]
using (var muxer = Create()) [TestCase(true)]
{ [TestCase(false)]
muxer.PreserveAsyncOrder = preserveOrder; public void GetSetSync(bool preserveOrder)
var conn = muxer.GetDatabase(); {
using (var muxer = Create())
RedisKey key = Me(); {
conn.KeyDelete(key); muxer.PreserveAsyncOrder = preserveOrder;
var d1 = conn.KeyDelete(key); var conn = muxer.GetDatabase();
var g1 = conn.StringGet(key);
conn.StringSet(key, "123"); RedisKey key = Me();
var g2 = conn.StringGet(key); conn.KeyDelete(key);
var d2 = conn.KeyDelete(key); var d1 = conn.KeyDelete(key);
var g1 = conn.StringGet(key);
Assert.IsFalse(d1); conn.StringSet(key, "123");
Assert.IsNull((string)g1); var g2 = conn.StringGet(key);
Assert.IsTrue(g1.IsNull); var d2 = conn.KeyDelete(key);
Assert.AreEqual("123", (string)g2); Assert.IsFalse(d1);
Assert.AreEqual(123, (int)g2); Assert.IsNull((string)g1);
Assert.IsFalse(g2.IsNull); Assert.IsTrue(g1.IsNull);
Assert.IsTrue(d2);
} Assert.AreEqual("123", (string)g2);
} Assert.AreEqual(123, (int)g2);
Assert.IsFalse(g2.IsNull);
[Test] Assert.IsTrue(d2);
[TestCase(true, true)] }
[TestCase(true, false)] }
[TestCase(false, true)]
[TestCase(false, false)] [Test]
public void MassiveBulkOpsAsync(bool preserveOrder, bool withContinuation) [TestCase(true, true)]
{ [TestCase(true, false)]
#if DEBUG [TestCase(false, true)]
var oldAsyncCompletionCount = ConnectionMultiplexer.GetAsyncCompletionWorkerCount(); [TestCase(false, false)]
#endif public void MassiveBulkOpsAsync(bool preserveOrder, bool withContinuation)
using (var muxer = Create()) {
{ #if DEBUG
muxer.PreserveAsyncOrder = preserveOrder; var oldAsyncCompletionCount = ConnectionMultiplexer.GetAsyncCompletionWorkerCount();
RedisKey key = "MBOA"; #endif
var conn = muxer.GetDatabase(); using (var muxer = Create())
muxer.Wait(conn.PingAsync()); {
muxer.PreserveAsyncOrder = preserveOrder;
Action<Task> nonTrivial = delegate RedisKey key = "MBOA";
{ var conn = muxer.GetDatabase();
Thread.SpinWait(5); muxer.Wait(conn.PingAsync());
};
var watch = Stopwatch.StartNew(); #if DNXCORE50
for (int i = 0; i <= AsyncOpsQty; i++) int number = 0;
{ #endif
var t = conn.StringSetAsync(key, i); Action<Task> nonTrivial = delegate
if (withContinuation) t.ContinueWith(nonTrivial); {
} #if !DNXCORE50
int val = (int)muxer.Wait(conn.StringGetAsync(key)); Thread.SpinWait(5);
Assert.AreEqual(AsyncOpsQty, val); #else
watch.Stop(); for (int i = 0; i < 50; i++)
Console.WriteLine("{2}: Time for {0} ops: {1}ms ({3}, {4}); ops/s: {5}", AsyncOpsQty, watch.ElapsedMilliseconds, Me(), {
withContinuation ? "with continuation" : "no continuation", preserveOrder ? "preserve order" : "any order", number++;
AsyncOpsQty / watch.Elapsed.TotalSeconds); }
#if DEBUG #endif
Console.WriteLine("Async completion workers: " + (ConnectionMultiplexer.GetAsyncCompletionWorkerCount() - oldAsyncCompletionCount)); };
#endif var watch = Stopwatch.StartNew();
} for (int i = 0; i <= AsyncOpsQty; i++)
} {
var t = conn.StringSetAsync(key, i);
[Test] if (withContinuation) t.ContinueWith(nonTrivial);
[TestCase(false, false)] }
[TestCase(true, true)] int val = (int)muxer.Wait(conn.StringGetAsync(key));
[TestCase(true, false)] Assert.AreEqual(AsyncOpsQty, val);
public void GetWithExpiry(bool exists, bool hasExpiry) watch.Stop();
{ Console.WriteLine("{2}: Time for {0} ops: {1}ms ({3}, {4}); ops/s: {5}", AsyncOpsQty, watch.ElapsedMilliseconds, Me(),
using(var conn = Create()) withContinuation ? "with continuation" : "no continuation", preserveOrder ? "preserve order" : "any order",
{ AsyncOpsQty / watch.Elapsed.TotalSeconds);
var db = conn.GetDatabase(); #if DEBUG
RedisKey key = Me(); Console.WriteLine("Async completion workers: " + (ConnectionMultiplexer.GetAsyncCompletionWorkerCount() - oldAsyncCompletionCount));
db.KeyDelete(key); #endif
if (exists) }
{ }
if (hasExpiry)
db.StringSet(key, "val", TimeSpan.FromMinutes(5)); [Test]
else [TestCase(false, false)]
db.StringSet(key, "val"); [TestCase(true, true)]
} [TestCase(true, false)]
var async = db.StringGetWithExpiryAsync(key); public void GetWithExpiry(bool exists, bool hasExpiry)
var syncResult = db.StringGetWithExpiry(key); {
var asyncResult = db.Wait(async); using(var conn = Create())
{
if(exists) var db = conn.GetDatabase();
{ RedisKey key = Me();
Assert.AreEqual("val", (string)asyncResult.Value); db.KeyDelete(key);
Assert.AreEqual(hasExpiry, asyncResult.Expiry.HasValue); if (exists)
if (hasExpiry) Assert.IsTrue(asyncResult.Expiry.Value.TotalMinutes >= 4.9 && asyncResult.Expiry.Value.TotalMinutes <= 5); {
Assert.AreEqual("val", (string)syncResult.Value); if (hasExpiry)
Assert.AreEqual(hasExpiry, syncResult.Expiry.HasValue); db.StringSet(key, "val", TimeSpan.FromMinutes(5));
if (hasExpiry) Assert.IsTrue(syncResult.Expiry.Value.TotalMinutes >= 4.9 && syncResult.Expiry.Value.TotalMinutes <= 5); else
} db.StringSet(key, "val");
else }
{ var async = db.StringGetWithExpiryAsync(key);
Assert.IsTrue(asyncResult.Value.IsNull); var syncResult = db.StringGetWithExpiry(key);
Assert.IsFalse(asyncResult.Expiry.HasValue); var asyncResult = db.Wait(async);
Assert.IsTrue(syncResult.Value.IsNull);
Assert.IsFalse(syncResult.Expiry.HasValue); if(exists)
} {
} Assert.AreEqual("val", (string)asyncResult.Value);
} Assert.AreEqual(hasExpiry, asyncResult.Expiry.HasValue);
[Test] if (hasExpiry) Assert.IsTrue(asyncResult.Expiry.Value.TotalMinutes >= 4.9 && asyncResult.Expiry.Value.TotalMinutes <= 5);
[ExpectedException(typeof(RedisServerException), ExpectedMessage = "WRONGTYPE Operation against a key holding the wrong kind of value")] Assert.AreEqual("val", (string)syncResult.Value);
public void GetWithExpiryWrongTypeAsync() Assert.AreEqual(hasExpiry, syncResult.Expiry.HasValue);
{ if (hasExpiry) Assert.IsTrue(syncResult.Expiry.Value.TotalMinutes >= 4.9 && syncResult.Expiry.Value.TotalMinutes <= 5);
using (var conn = Create()) }
{ else
var db = conn.GetDatabase(); {
RedisKey key = Me(); Assert.IsTrue(asyncResult.Value.IsNull);
db.KeyDelete(key); Assert.IsFalse(asyncResult.Expiry.HasValue);
db.SetAdd(key, "abc"); Assert.IsTrue(syncResult.Value.IsNull);
try Assert.IsFalse(syncResult.Expiry.HasValue);
{ }
var async = db.Wait(db.StringGetWithExpiryAsync(key)); }
} }
catch(AggregateException ex) [Test]
{ public void GetWithExpiryWrongTypeAsync()
throw ex.InnerExceptions[0]; {
} using (var conn = Create())
Assert.Fail(); {
} var db = conn.GetDatabase();
} RedisKey key = Me();
db.KeyDelete(key);
[Test] db.SetAdd(key, "abc");
[ExpectedException(typeof(RedisServerException), ExpectedMessage = "WRONGTYPE Operation against a key holding the wrong kind of value")] Assert.Throws<RedisServerException>(() =>
public void GetWithExpiryWrongTypeSync() {
{ try
using (var conn = Create()) {
{ var async = db.Wait(db.StringGetWithExpiryAsync(key));
var db = conn.GetDatabase(); }
RedisKey key = Me(); catch (AggregateException ex)
db.KeyDelete(key); {
db.SetAdd(key, "abc"); throw ex.InnerExceptions[0];
db.StringGetWithExpiry(key); }
Assert.Fail(); Assert.Fail();
} },
} "A null key is not valid in this context");
}
[Test] }
[TestCase(true, true, ResultCompletionMode.ConcurrentIfContinuation)]
[TestCase(true, false, ResultCompletionMode.ConcurrentIfContinuation)] [Test]
[TestCase(false, true, ResultCompletionMode.ConcurrentIfContinuation)] public void GetWithExpiryWrongTypeSync()
[TestCase(false, false, ResultCompletionMode.ConcurrentIfContinuation)] {
[TestCase(true, true, ResultCompletionMode.Concurrent)] Assert.Throws<RedisServerException>(() =>
[TestCase(true, false, ResultCompletionMode.Concurrent)] {
[TestCase(false, true, ResultCompletionMode.Concurrent)] using (var conn = Create())
[TestCase(false, false, ResultCompletionMode.Concurrent)] {
[TestCase(true, true, ResultCompletionMode.PreserveOrder)] var db = conn.GetDatabase();
[TestCase(true, false, ResultCompletionMode.PreserveOrder)] RedisKey key = Me();
[TestCase(false, true, ResultCompletionMode.PreserveOrder)] db.KeyDelete(key);
[TestCase(false, false, ResultCompletionMode.PreserveOrder)] db.SetAdd(key, "abc");
public void MassiveBulkOpsAsyncOldStyle(bool withContinuation, bool suspendFlush, ResultCompletionMode completionMode) db.StringGetWithExpiry(key);
{ Assert.Fail();
using (var conn = GetOldStyleConnection()) }
{ },
const int db = 0; "WRONGTYPE Operation against a key holding the wrong kind of value");
string key = "MBOQ"; }
conn.CompletionMode = completionMode;
conn.Wait(conn.Server.Ping()); #if FEATURE_BOOKSLEEVE
Action<Task> nonTrivial = delegate [Test]
{ [TestCase(true, true, ResultCompletionMode.ConcurrentIfContinuation)]
Thread.SpinWait(5); [TestCase(true, false, ResultCompletionMode.ConcurrentIfContinuation)]
}; [TestCase(false, true, ResultCompletionMode.ConcurrentIfContinuation)]
var watch = Stopwatch.StartNew(); [TestCase(false, false, ResultCompletionMode.ConcurrentIfContinuation)]
[TestCase(true, true, ResultCompletionMode.Concurrent)]
if (suspendFlush) conn.SuspendFlush(); [TestCase(true, false, ResultCompletionMode.Concurrent)]
try [TestCase(false, true, ResultCompletionMode.Concurrent)]
{ [TestCase(false, false, ResultCompletionMode.Concurrent)]
[TestCase(true, true, ResultCompletionMode.PreserveOrder)]
for (int i = 0; i <= AsyncOpsQty; i++) [TestCase(true, false, ResultCompletionMode.PreserveOrder)]
{ [TestCase(false, true, ResultCompletionMode.PreserveOrder)]
var t = conn.Strings.Set(db, key, i); [TestCase(false, false, ResultCompletionMode.PreserveOrder)]
if (withContinuation) t.ContinueWith(nonTrivial); public void MassiveBulkOpsAsyncOldStyle(bool withContinuation, bool suspendFlush, ResultCompletionMode completionMode)
} {
} finally using (var conn = GetOldStyleConnection())
{ {
if (suspendFlush) conn.ResumeFlush(); const int db = 0;
} string key = "MBOQ";
int val = (int)conn.Wait(conn.Strings.GetInt64(db, key)); conn.CompletionMode = completionMode;
Assert.AreEqual(AsyncOpsQty, val); conn.Wait(conn.Server.Ping());
watch.Stop(); Action<Task> nonTrivial = delegate
Console.WriteLine("{2}: Time for {0} ops: {1}ms ({3}, {4}, {5}); ops/s: {6}", AsyncOpsQty, watch.ElapsedMilliseconds, Me(), {
withContinuation ? "with continuation" : "no continuation", Thread.SpinWait(5);
suspendFlush ? "suspend flush" : "flush at whim", };
completionMode, AsyncOpsQty / watch.Elapsed.TotalSeconds); var watch = Stopwatch.StartNew();
}
} if (suspendFlush) conn.SuspendFlush();
try
{
[Test]
[TestCase(true, 1)] for (int i = 0; i <= AsyncOpsQty; i++)
[TestCase(false, 1)] {
[TestCase(true, 5)] var t = conn.Strings.Set(db, key, i);
[TestCase(false, 5)] if (withContinuation) t.ContinueWith(nonTrivial);
[TestCase(true, 10)] }
[TestCase(false, 10)] } finally
[TestCase(true, 50)] {
[TestCase(false, 50)] if (suspendFlush) conn.ResumeFlush();
public void MassiveBulkOpsSync(bool preserveOrder, int threads) }
{ int val = (int)conn.Wait(conn.Strings.GetInt64(db, key));
int workPerThread = SyncOpsQty / threads; Assert.AreEqual(AsyncOpsQty, val);
using (var muxer = Create()) watch.Stop();
{ Console.WriteLine("{2}: Time for {0} ops: {1}ms ({3}, {4}, {5}); ops/s: {6}", AsyncOpsQty, watch.ElapsedMilliseconds, Me(),
muxer.PreserveAsyncOrder = preserveOrder; withContinuation ? "with continuation" : "no continuation",
RedisKey key = "MBOS"; suspendFlush ? "suspend flush" : "flush at whim",
var conn = muxer.GetDatabase(); completionMode, AsyncOpsQty / watch.Elapsed.TotalSeconds);
conn.KeyDelete(key); }
#if DEBUG }
long oldAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount(); #endif
long oldWorkerCount = ConnectionMultiplexer.GetAsyncCompletionWorkerCount();
#endif [Test]
var timeTaken = RunConcurrent(delegate [TestCase(true, 1)]
{ [TestCase(false, 1)]
for (int i = 0; i < workPerThread; i++) [TestCase(true, 5)]
{ [TestCase(false, 5)]
conn.StringIncrement(key); [TestCase(true, 10)]
} [TestCase(false, 10)]
}, threads); [TestCase(true, 50)]
[TestCase(false, 50)]
int val = (int)conn.StringGet(key); public void MassiveBulkOpsSync(bool preserveOrder, int threads)
Assert.AreEqual(workPerThread * threads, val); {
Console.WriteLine("{2}: Time for {0} ops on {4} threads: {1}ms ({3}); ops/s: {5}", int workPerThread = SyncOpsQty / threads;
threads * workPerThread, timeTaken.TotalMilliseconds, Me() using (var muxer = Create())
, preserveOrder ? "preserve order" : "any order", threads, (workPerThread * threads) / timeTaken.TotalSeconds); {
#if DEBUG muxer.PreserveAsyncOrder = preserveOrder;
long newAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount(); RedisKey key = "MBOS";
long newWorkerCount = ConnectionMultiplexer.GetAsyncCompletionWorkerCount(); var conn = muxer.GetDatabase();
Console.WriteLine("ResultBox allocations: {0}; workers {1}", newAlloc - oldAlloc, newWorkerCount - oldWorkerCount); conn.KeyDelete(key);
Assert.IsTrue(newAlloc - oldAlloc <= 2 * threads, "number of box allocations"); #if DEBUG
#endif long oldAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount();
} long oldWorkerCount = ConnectionMultiplexer.GetAsyncCompletionWorkerCount();
} #endif
[Test] var timeTaken = RunConcurrent(delegate
[TestCase(ResultCompletionMode.Concurrent, 1)] {
[TestCase(ResultCompletionMode.ConcurrentIfContinuation, 1)] for (int i = 0; i < workPerThread; i++)
[TestCase(ResultCompletionMode.PreserveOrder, 1)] {
[TestCase(ResultCompletionMode.Concurrent, 5)] conn.StringIncrement(key);
[TestCase(ResultCompletionMode.ConcurrentIfContinuation, 5)] }
[TestCase(ResultCompletionMode.PreserveOrder, 5)] }, threads);
[TestCase(ResultCompletionMode.Concurrent, 10)]
[TestCase(ResultCompletionMode.ConcurrentIfContinuation, 10)] int val = (int)conn.StringGet(key);
[TestCase(ResultCompletionMode.PreserveOrder, 10)] Assert.AreEqual(workPerThread * threads, val);
[TestCase(ResultCompletionMode.Concurrent, 50)] Console.WriteLine("{2}: Time for {0} ops on {4} threads: {1}ms ({3}); ops/s: {5}",
[TestCase(ResultCompletionMode.ConcurrentIfContinuation, 50)] threads * workPerThread, timeTaken.TotalMilliseconds, Me()
[TestCase(ResultCompletionMode.PreserveOrder, 50)] , preserveOrder ? "preserve order" : "any order", threads, (workPerThread * threads) / timeTaken.TotalSeconds);
public void MassiveBulkOpsSyncOldStyle(ResultCompletionMode completionMode, int threads) #if DEBUG
{ long newAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount();
int workPerThread = SyncOpsQty / threads; long newWorkerCount = ConnectionMultiplexer.GetAsyncCompletionWorkerCount();
Console.WriteLine("ResultBox allocations: {0}; workers {1}", newAlloc - oldAlloc, newWorkerCount - oldWorkerCount);
using (var conn = GetOldStyleConnection()) Assert.IsTrue(newAlloc - oldAlloc <= 2 * threads, "number of box allocations");
{ #endif
const int db = 0; }
string key = "MBOQ"; }
conn.CompletionMode = completionMode;
conn.Wait(conn.Keys.Remove(db, key)); #if FEATURE_BOOKSLEEVE
[Test]
var timeTaken = RunConcurrent(delegate [TestCase(ResultCompletionMode.Concurrent, 1)]
{ [TestCase(ResultCompletionMode.ConcurrentIfContinuation, 1)]
for (int i = 0; i < workPerThread; i++) [TestCase(ResultCompletionMode.PreserveOrder, 1)]
{ [TestCase(ResultCompletionMode.Concurrent, 5)]
conn.Wait(conn.Strings.Increment(db, key)); [TestCase(ResultCompletionMode.ConcurrentIfContinuation, 5)]
} [TestCase(ResultCompletionMode.PreserveOrder, 5)]
}, threads); [TestCase(ResultCompletionMode.Concurrent, 10)]
[TestCase(ResultCompletionMode.ConcurrentIfContinuation, 10)]
int val = (int)conn.Wait(conn.Strings.GetInt64(db, key)); [TestCase(ResultCompletionMode.PreserveOrder, 10)]
Assert.AreEqual(workPerThread * threads, val); [TestCase(ResultCompletionMode.Concurrent, 50)]
[TestCase(ResultCompletionMode.ConcurrentIfContinuation, 50)]
Console.WriteLine("{2}: Time for {0} ops on {4} threads: {1}ms ({3}); ops/s: {5}", workPerThread * threads, timeTaken.TotalMilliseconds, Me(), [TestCase(ResultCompletionMode.PreserveOrder, 50)]
completionMode, threads, (workPerThread * threads) / timeTaken.TotalSeconds); public void MassiveBulkOpsSyncOldStyle(ResultCompletionMode completionMode, int threads)
} {
} int workPerThread = SyncOpsQty / threads;
[Test] using (var conn = GetOldStyleConnection())
[TestCase(true, 1)] {
[TestCase(false, 1)] const int db = 0;
[TestCase(true, 5)] string key = "MBOQ";
[TestCase(false, 5)] conn.CompletionMode = completionMode;
public void MassiveBulkOpsFireAndForget(bool preserveOrder, int threads) conn.Wait(conn.Keys.Remove(db, key));
{
using (var muxer = Create()) var timeTaken = RunConcurrent(delegate
{ {
muxer.PreserveAsyncOrder = preserveOrder; for (int i = 0; i < workPerThread; i++)
#if DEBUG {
long oldAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount(); conn.Wait(conn.Strings.Increment(db, key));
#endif }
RedisKey key = "MBOF"; }, threads);
var conn = muxer.GetDatabase();
conn.Ping(); int val = (int)conn.Wait(conn.Strings.GetInt64(db, key));
Assert.AreEqual(workPerThread * threads, val);
conn.KeyDelete(key, CommandFlags.FireAndForget);
int perThread = AsyncOpsQty / threads; Console.WriteLine("{2}: Time for {0} ops on {4} threads: {1}ms ({3}); ops/s: {5}", workPerThread * threads, timeTaken.TotalMilliseconds, Me(),
var elapsed = RunConcurrent(delegate completionMode, threads, (workPerThread * threads) / timeTaken.TotalSeconds);
{ }
for (int i = 0; i < perThread; i++) }
{ #endif
conn.StringIncrement(key, flags: CommandFlags.FireAndForget);
} [Test]
conn.Ping(); [TestCase(true, 1)]
}, threads); [TestCase(false, 1)]
var val = (long)conn.StringGet(key); [TestCase(true, 5)]
Assert.AreEqual(perThread * threads, val); [TestCase(false, 5)]
public void MassiveBulkOpsFireAndForget(bool preserveOrder, int threads)
Console.WriteLine("{2}: Time for {0} ops over {5} threads: {1:###,###}ms ({3}); ops/s: {4:###,###,##0}", {
val, elapsed.TotalMilliseconds, Me(), using (var muxer = Create())
preserveOrder ? "preserve order" : "any order", {
val / elapsed.TotalSeconds, threads); muxer.PreserveAsyncOrder = preserveOrder;
#if DEBUG #if DEBUG
long newAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount(); long oldAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount();
Console.WriteLine("ResultBox allocations: {0}", #endif
newAlloc - oldAlloc); RedisKey key = "MBOF";
Assert.IsTrue(newAlloc - oldAlloc <= 4); var conn = muxer.GetDatabase();
#endif conn.Ping();
}
} conn.KeyDelete(key, CommandFlags.FireAndForget);
int perThread = AsyncOpsQty / threads;
var elapsed = RunConcurrent(delegate
#if DEBUG {
[Test] for (int i = 0; i < perThread; i++)
[TestCase(true)] {
[TestCase(false)] conn.StringIncrement(key, flags: CommandFlags.FireAndForget);
public void TestQuit(bool preserveOrder) }
{ conn.Ping();
SetExpectedAmbientFailureCount(1); }, threads);
using (var muxer = Create(allowAdmin: true)) var val = (long)conn.StringGet(key);
{ Assert.AreEqual(perThread * threads, val);
muxer.PreserveAsyncOrder = preserveOrder;
var db = muxer.GetDatabase(); Console.WriteLine("{2}: Time for {0} ops over {5} threads: {1:###,###}ms ({3}); ops/s: {4:###,###,##0}",
string key = Guid.NewGuid().ToString(); val, elapsed.TotalMilliseconds, Me(),
db.KeyDelete(key, CommandFlags.FireAndForget); preserveOrder ? "preserve order" : "any order",
db.StringSet(key, key, flags: CommandFlags.FireAndForget); val / elapsed.TotalSeconds, threads);
GetServer(muxer).Quit(CommandFlags.FireAndForget); #if DEBUG
var watch = Stopwatch.StartNew(); long newAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount();
try Console.WriteLine("ResultBox allocations: {0}",
{ newAlloc - oldAlloc);
db.Ping(); Assert.IsTrue(newAlloc - oldAlloc <= 4);
Assert.Fail(); #endif
} }
catch (RedisConnectionException) { } }
watch.Stop();
Console.WriteLine("Time to notice quit: {0}ms ({1})", watch.ElapsedMilliseconds,
preserveOrder ? "preserve order" : "any order"); #if DEBUG
Thread.Sleep(20); [Test]
Debug.WriteLine("Pinging..."); [TestCase(true)]
Assert.AreEqual(key, (string)db.StringGet(key)); [TestCase(false)]
} public void TestQuit(bool preserveOrder)
} {
[Test] SetExpectedAmbientFailureCount(1);
[TestCase(true)] using (var muxer = Create(allowAdmin: true))
[TestCase(false)] {
public void TestSevered(bool preserveOrder) muxer.PreserveAsyncOrder = preserveOrder;
{ var db = muxer.GetDatabase();
SetExpectedAmbientFailureCount(2); string key = Guid.NewGuid().ToString();
using (var muxer = Create(allowAdmin: true)) db.KeyDelete(key, CommandFlags.FireAndForget);
{ db.StringSet(key, key, flags: CommandFlags.FireAndForget);
muxer.PreserveAsyncOrder = preserveOrder; GetServer(muxer).Quit(CommandFlags.FireAndForget);
var db = muxer.GetDatabase(); var watch = Stopwatch.StartNew();
string key = Guid.NewGuid().ToString(); try
db.KeyDelete(key, CommandFlags.FireAndForget); {
db.StringSet(key, key, flags: CommandFlags.FireAndForget); db.Ping();
GetServer(muxer).SimulateConnectionFailure(); Assert.Fail();
var watch = Stopwatch.StartNew(); }
db.Ping(); catch (RedisConnectionException) { }
watch.Stop(); watch.Stop();
Console.WriteLine("Time to re-establish: {0}ms ({1})", watch.ElapsedMilliseconds, Console.WriteLine("Time to notice quit: {0}ms ({1})", watch.ElapsedMilliseconds,
preserveOrder ? "preserve order" : "any order"); preserveOrder ? "preserve order" : "any order");
Thread.Sleep(20); Thread.Sleep(20);
Debug.WriteLine("Pinging..."); Debug.WriteLine("Pinging...");
Assert.AreEqual(key, (string)db.StringGet(key)); Assert.AreEqual(key, (string)db.StringGet(key));
} }
} }
#endif [Test]
[TestCase(true)]
[TestCase(false)]
public void TestSevered(bool preserveOrder)
[Test] {
[TestCase(true)] SetExpectedAmbientFailureCount(2);
[TestCase(false)] using (var muxer = Create(allowAdmin: true))
public void IncrAsync(bool preserveOrder) {
{ muxer.PreserveAsyncOrder = preserveOrder;
using (var muxer = Create()) var db = muxer.GetDatabase();
{ string key = Guid.NewGuid().ToString();
muxer.PreserveAsyncOrder = preserveOrder; db.KeyDelete(key, CommandFlags.FireAndForget);
var conn = muxer.GetDatabase(); db.StringSet(key, key, flags: CommandFlags.FireAndForget);
RedisKey key = Me(); GetServer(muxer).SimulateConnectionFailure();
conn.KeyDelete(key, CommandFlags.FireAndForget); var watch = Stopwatch.StartNew();
var nix = conn.KeyExistsAsync(key); db.Ping();
var a = conn.StringGetAsync(key); watch.Stop();
var b = conn.StringIncrementAsync(key); Console.WriteLine("Time to re-establish: {0}ms ({1})", watch.ElapsedMilliseconds,
var c = conn.StringGetAsync(key); preserveOrder ? "preserve order" : "any order");
var d = conn.StringIncrementAsync(key, 10); Thread.Sleep(20);
var e = conn.StringGetAsync(key); Debug.WriteLine("Pinging...");
var f = conn.StringDecrementAsync(key, 11); Assert.AreEqual(key, (string)db.StringGet(key));
var g = conn.StringGetAsync(key); }
var h = conn.KeyExistsAsync(key); }
Assert.IsFalse(muxer.Wait(nix)); #endif
Assert.IsTrue(muxer.Wait(a).IsNull);
Assert.AreEqual(0, (long)muxer.Wait(a));
Assert.AreEqual(1, muxer.Wait(b));
Assert.AreEqual(1, (long)muxer.Wait(c)); [Test]
Assert.AreEqual(11, muxer.Wait(d)); [TestCase(true)]
Assert.AreEqual(11, (long)muxer.Wait(e)); [TestCase(false)]
Assert.AreEqual(0, muxer.Wait(f)); public void IncrAsync(bool preserveOrder)
Assert.AreEqual(0, (long)muxer.Wait(g)); {
Assert.IsTrue(muxer.Wait(h)); using (var muxer = Create())
} {
} muxer.PreserveAsyncOrder = preserveOrder;
[Test] var conn = muxer.GetDatabase();
[TestCase(true)] RedisKey key = Me();
[TestCase(false)] conn.KeyDelete(key, CommandFlags.FireAndForget);
public void IncrSync(bool preserveOrder) var nix = conn.KeyExistsAsync(key);
{ var a = conn.StringGetAsync(key);
using (var muxer = Create()) var b = conn.StringIncrementAsync(key);
{ var c = conn.StringGetAsync(key);
muxer.PreserveAsyncOrder = preserveOrder; var d = conn.StringIncrementAsync(key, 10);
var conn = muxer.GetDatabase(); var e = conn.StringGetAsync(key);
RedisKey key = Me(); var f = conn.StringDecrementAsync(key, 11);
conn.KeyDelete(key, CommandFlags.FireAndForget); var g = conn.StringGetAsync(key);
var nix = conn.KeyExists(key); var h = conn.KeyExistsAsync(key);
var a = conn.StringGet(key); Assert.IsFalse(muxer.Wait(nix));
var b = conn.StringIncrement(key); Assert.IsTrue(muxer.Wait(a).IsNull);
var c = conn.StringGet(key); Assert.AreEqual(0, (long)muxer.Wait(a));
var d = conn.StringIncrement(key, 10); Assert.AreEqual(1, muxer.Wait(b));
var e = conn.StringGet(key); Assert.AreEqual(1, (long)muxer.Wait(c));
var f = conn.StringDecrement(key, 11); Assert.AreEqual(11, muxer.Wait(d));
var g = conn.StringGet(key); Assert.AreEqual(11, (long)muxer.Wait(e));
var h = conn.KeyExists(key); Assert.AreEqual(0, muxer.Wait(f));
Assert.IsFalse(nix); Assert.AreEqual(0, (long)muxer.Wait(g));
Assert.IsTrue(a.IsNull); Assert.IsTrue(muxer.Wait(h));
Assert.AreEqual(0, (long)a); }
Assert.AreEqual(1, b); }
Assert.AreEqual(1, (long)c); [Test]
Assert.AreEqual(11, d); [TestCase(true)]
Assert.AreEqual(11, (long)e); [TestCase(false)]
Assert.AreEqual(0, f); public void IncrSync(bool preserveOrder)
Assert.AreEqual(0, (long)g); {
Assert.IsTrue(h); using (var muxer = Create())
} {
} muxer.PreserveAsyncOrder = preserveOrder;
var conn = muxer.GetDatabase();
[Test] RedisKey key = Me();
public void IncrDifferentSizes() conn.KeyDelete(key, CommandFlags.FireAndForget);
{ var nix = conn.KeyExists(key);
using (var muxer = Create()) var a = conn.StringGet(key);
{ var b = conn.StringIncrement(key);
var db = muxer.GetDatabase(); var c = conn.StringGet(key);
RedisKey key = Me(); var d = conn.StringIncrement(key, 10);
db.KeyDelete(key, CommandFlags.FireAndForget); var e = conn.StringGet(key);
int expected = 0; var f = conn.StringDecrement(key, 11);
Incr(db, key, -129019, ref expected); var g = conn.StringGet(key);
Incr(db, key, -10023, ref expected); var h = conn.KeyExists(key);
Incr(db, key, -9933, ref expected); Assert.IsFalse(nix);
Incr(db, key, -23, ref expected); Assert.IsTrue(a.IsNull);
Incr(db, key, -7, ref expected); Assert.AreEqual(0, (long)a);
Incr(db, key, -1, ref expected); Assert.AreEqual(1, b);
Incr(db, key, 0, ref expected); Assert.AreEqual(1, (long)c);
Incr(db, key, 1, ref expected); Assert.AreEqual(11, d);
Incr(db, key, 9, ref expected); Assert.AreEqual(11, (long)e);
Incr(db, key, 11, ref expected); Assert.AreEqual(0, f);
Incr(db, key, 345, ref expected); Assert.AreEqual(0, (long)g);
Incr(db, key, 4982, ref expected); Assert.IsTrue(h);
Incr(db, key, 13091, ref expected); }
Incr(db, key, 324092, ref expected); }
Assert.AreNotEqual(0, expected);
var sum = (long)db.StringGet(key); [Test]
Assert.AreEqual(expected, sum); public void IncrDifferentSizes()
} {
} using (var muxer = Create())
{
private void Incr(IDatabase database, RedisKey key, int delta, ref int total) var db = muxer.GetDatabase();
{ RedisKey key = Me();
database.StringIncrement(key, delta, CommandFlags.FireAndForget); db.KeyDelete(key, CommandFlags.FireAndForget);
total += delta; int expected = 0;
} Incr(db, key, -129019, ref expected);
Incr(db, key, -10023, ref expected);
[Test] Incr(db, key, -9933, ref expected);
public void WrappedDatabasePrefixIntegration() Incr(db, key, -23, ref expected);
{ Incr(db, key, -7, ref expected);
using (var conn = Create()) Incr(db, key, -1, ref expected);
{ Incr(db, key, 0, ref expected);
var db = conn.GetDatabase().WithKeyPrefix("abc"); Incr(db, key, 1, ref expected);
db.KeyDelete("count"); Incr(db, key, 9, ref expected);
db.StringIncrement("count"); Incr(db, key, 11, ref expected);
db.StringIncrement("count"); Incr(db, key, 345, ref expected);
db.StringIncrement("count"); Incr(db, key, 4982, ref expected);
Incr(db, key, 13091, ref expected);
int count = (int)conn.GetDatabase().StringGet("abccount"); Incr(db, key, 324092, ref expected);
Assert.AreEqual(3, count); Assert.AreNotEqual(0, expected);
} var sum = (long)db.StringGet(key);
} Assert.AreEqual(expected, sum);
} }
}
}
private void Incr(IDatabase database, RedisKey key, int delta, ref int total)
{
database.StringIncrement(key, delta, CommandFlags.FireAndForget);
total += delta;
}
[Test]
public void WrappedDatabasePrefixIntegration()
{
using (var conn = Create())
{
var db = conn.GetDatabase().WithKeyPrefix("abc");
db.KeyDelete("count");
db.StringIncrement("count");
db.StringIncrement("count");
db.StringIncrement("count");
int count = (int)conn.GetDatabase().StringGet("abccount");
Assert.AreEqual(3, count);
}
}
}
}
using System; #if FEATURE_MOQ
using System;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using StackExchange.Redis.KeyspaceIsolation; using StackExchange.Redis.KeyspaceIsolation;
...@@ -12,7 +13,8 @@ public sealed class BatchWrapperTests ...@@ -12,7 +13,8 @@ public sealed class BatchWrapperTests
private Mock<IBatch> mock; private Mock<IBatch> mock;
private BatchWrapper wrapper; private BatchWrapper wrapper;
[TestFixtureSetUp] //[TestFixtureSetUp]
[OneTimeSetUpAttribute]
public void Initialize() public void Initialize()
{ {
mock = new Mock<IBatch>(); mock = new Mock<IBatch>();
...@@ -27,3 +29,4 @@ public void Execute() ...@@ -27,3 +29,4 @@ public void Execute()
} }
} }
} }
#endif
\ No newline at end of file
...@@ -20,7 +20,11 @@ public class Cluster : TestBase ...@@ -20,7 +20,11 @@ public class Cluster : TestBase
protected override string GetConfiguration() protected override string GetConfiguration()
{ {
var server = ClusterIp; var server = ClusterIp;
#if !DNXCORE50
if (string.Equals(Environment.MachineName, "MARC-LAPTOP", StringComparison.InvariantCultureIgnoreCase)) if (string.Equals(Environment.MachineName, "MARC-LAPTOP", StringComparison.InvariantCultureIgnoreCase))
#else
if (string.Equals(Environment.GetEnvironmentVariable("COMPUTERNAME"), "MARC-LAPTOP", StringComparison.OrdinalIgnoreCase))
#endif
{ {
server = "192.168.56.101"; server = "192.168.56.101";
} }
...@@ -130,7 +134,7 @@ public void IntentionalWrongServer() ...@@ -130,7 +134,7 @@ public void IntentionalWrongServer()
using (var conn = Create()) using (var conn = Create())
{ {
var endpoints = conn.GetEndPoints(); var endpoints = conn.GetEndPoints();
var servers = Array.ConvertAll(endpoints, e => conn.GetServer(e)); var servers = endpoints.Select(e => conn.GetServer(e));
var key = Me(); var key = Me();
const string value = "abc"; const string value = "abc";
...@@ -198,110 +202,116 @@ public void IntentionalWrongServer() ...@@ -198,110 +202,116 @@ public void IntentionalWrongServer()
} }
[Test] [Test]
[ExpectedException(typeof(RedisCommandException), ExpectedMessage = "Multi-key operations must involve a single slot; keys can use 'hash tags' to help this, i.e. '{/users/12345}/account' and '{/users/12345}/contacts' will always be in the same slot")]
public void TransactionWithMultiServerKeys() public void TransactionWithMultiServerKeys()
{ {
using(var muxer = Create()) Assert.Throws<RedisCommandException>(() =>
{ {
// connect using (var muxer = Create())
var cluster = muxer.GetDatabase(); {
var anyServer = muxer.GetServer(muxer.GetEndPoints()[0]); // connect
anyServer.Ping(); var cluster = muxer.GetDatabase();
Assert.AreEqual(ServerType.Cluster, anyServer.ServerType); var anyServer = muxer.GetServer(muxer.GetEndPoints()[0]);
var config = anyServer.ClusterConfiguration; anyServer.Ping();
Assert.IsNotNull(config); Assert.AreEqual(ServerType.Cluster, anyServer.ServerType);
var config = anyServer.ClusterConfiguration;
// invent 2 keys that we believe are served by different nodes Assert.IsNotNull(config);
string x = Guid.NewGuid().ToString(), y;
var xNode = config.GetBySlot(x); // invent 2 keys that we believe are served by different nodes
int abort = 1000; string x = Guid.NewGuid().ToString(), y;
do var xNode = config.GetBySlot(x);
{ int abort = 1000;
y = Guid.NewGuid().ToString(); do
} while (--abort > 0 && config.GetBySlot(y) == xNode); {
if (abort == 0) Assert.Inconclusive("failed to find a different node to use"); y = Guid.NewGuid().ToString();
var yNode = config.GetBySlot(y); } while (--abort > 0 && config.GetBySlot(y) == xNode);
Console.WriteLine("x={0}, served by {1}", x, xNode.NodeId); if (abort == 0) Assert.Inconclusive("failed to find a different node to use");
Console.WriteLine("y={0}, served by {1}", y, yNode.NodeId); var yNode = config.GetBySlot(y);
Assert.AreNotEqual(xNode.NodeId, yNode.NodeId, "same node"); Console.WriteLine("x={0}, served by {1}", x, xNode.NodeId);
Console.WriteLine("y={0}, served by {1}", y, yNode.NodeId);
// wipe those keys Assert.AreNotEqual(xNode.NodeId, yNode.NodeId, "same node");
cluster.KeyDelete(x, CommandFlags.FireAndForget);
cluster.KeyDelete(y, CommandFlags.FireAndForget); // wipe those keys
cluster.KeyDelete(x, CommandFlags.FireAndForget);
// create a transaction that attempts to assign both keys cluster.KeyDelete(y, CommandFlags.FireAndForget);
var tran = cluster.CreateTransaction();
tran.AddCondition(Condition.KeyNotExists(x)); // create a transaction that attempts to assign both keys
tran.AddCondition(Condition.KeyNotExists(y)); var tran = cluster.CreateTransaction();
var setX = tran.StringSetAsync(x, "x-val"); tran.AddCondition(Condition.KeyNotExists(x));
var setY = tran.StringSetAsync(y, "y-val"); tran.AddCondition(Condition.KeyNotExists(y));
bool success = tran.Execute(); var setX = tran.StringSetAsync(x, "x-val");
var setY = tran.StringSetAsync(y, "y-val");
Assert.Fail("Expected single-slot rules to apply"); bool success = tran.Execute();
// the rest no longer applies while we are following single-slot rules
Assert.Fail("Expected single-slot rules to apply");
//// check that everything was aborted // the rest no longer applies while we are following single-slot rules
//Assert.IsFalse(success, "tran aborted");
//Assert.IsTrue(setX.IsCanceled, "set x cancelled"); //// check that everything was aborted
//Assert.IsTrue(setY.IsCanceled, "set y cancelled"); //Assert.IsFalse(success, "tran aborted");
//var existsX = cluster.KeyExistsAsync(x); //Assert.IsTrue(setX.IsCanceled, "set x cancelled");
//var existsY = cluster.KeyExistsAsync(y); //Assert.IsTrue(setY.IsCanceled, "set y cancelled");
//Assert.IsFalse(cluster.Wait(existsX), "x exists"); //var existsX = cluster.KeyExistsAsync(x);
//Assert.IsFalse(cluster.Wait(existsY), "y exists"); //var existsY = cluster.KeyExistsAsync(y);
} //Assert.IsFalse(cluster.Wait(existsX), "x exists");
//Assert.IsFalse(cluster.Wait(existsY), "y exists");
}
},
"Multi-key operations must involve a single slot; keys can use 'hash tags' to help this, i.e. '{/users/12345}/account' and '{/users/12345}/contacts' will always be in the same slot");
} }
[Test] [Test]
[ExpectedException(typeof(RedisCommandException), ExpectedMessage = "Multi-key operations must involve a single slot; keys can use 'hash tags' to help this, i.e. '{/users/12345}/account' and '{/users/12345}/contacts' will always be in the same slot")]
public void TransactionWithSameServerKeys() public void TransactionWithSameServerKeys()
{ {
using (var muxer = Create()) Assert.Throws<RedisCommandException>(() =>
{ {
// connect using (var muxer = Create())
var cluster = muxer.GetDatabase();
var anyServer = muxer.GetServer(muxer.GetEndPoints()[0]);
anyServer.Ping();
var config = anyServer.ClusterConfiguration;
Assert.IsNotNull(config);
// invent 2 keys that we believe are served by different nodes
string x = Guid.NewGuid().ToString(), y;
var xNode = config.GetBySlot(x);
int abort = 1000;
do
{ {
y = Guid.NewGuid().ToString(); // connect
} while (--abort > 0 && config.GetBySlot(y) != xNode); var cluster = muxer.GetDatabase();
if (abort == 0) Assert.Inconclusive("failed to find a key with the same node to use"); var anyServer = muxer.GetServer(muxer.GetEndPoints()[0]);
var yNode = config.GetBySlot(y); anyServer.Ping();
Console.WriteLine("x={0}, served by {1}", x, xNode.NodeId); var config = anyServer.ClusterConfiguration;
Console.WriteLine("y={0}, served by {1}", y, yNode.NodeId); Assert.IsNotNull(config);
Assert.AreEqual(xNode.NodeId, yNode.NodeId, "same node");
// invent 2 keys that we believe are served by different nodes
// wipe those keys string x = Guid.NewGuid().ToString(), y;
cluster.KeyDelete(x, CommandFlags.FireAndForget); var xNode = config.GetBySlot(x);
cluster.KeyDelete(y, CommandFlags.FireAndForget); int abort = 1000;
do
// create a transaction that attempts to assign both keys {
var tran = cluster.CreateTransaction(); y = Guid.NewGuid().ToString();
tran.AddCondition(Condition.KeyNotExists(x)); } while (--abort > 0 && config.GetBySlot(y) != xNode);
tran.AddCondition(Condition.KeyNotExists(y)); if (abort == 0) Assert.Inconclusive("failed to find a key with the same node to use");
var setX = tran.StringSetAsync(x, "x-val"); var yNode = config.GetBySlot(y);
var setY = tran.StringSetAsync(y, "y-val"); Console.WriteLine("x={0}, served by {1}", x, xNode.NodeId);
bool success = tran.Execute(); Console.WriteLine("y={0}, served by {1}", y, yNode.NodeId);
Assert.AreEqual(xNode.NodeId, yNode.NodeId, "same node");
Assert.Fail("Expected single-slot rules to apply");
// the rest no longer applies while we are following single-slot rules // wipe those keys
cluster.KeyDelete(x, CommandFlags.FireAndForget);
//// check that everything was aborted cluster.KeyDelete(y, CommandFlags.FireAndForget);
//Assert.IsTrue(success, "tran aborted");
//Assert.IsFalse(setX.IsCanceled, "set x cancelled"); // create a transaction that attempts to assign both keys
//Assert.IsFalse(setY.IsCanceled, "set y cancelled"); var tran = cluster.CreateTransaction();
//var existsX = cluster.KeyExistsAsync(x); tran.AddCondition(Condition.KeyNotExists(x));
//var existsY = cluster.KeyExistsAsync(y); tran.AddCondition(Condition.KeyNotExists(y));
//Assert.IsTrue(cluster.Wait(existsX), "x exists"); var setX = tran.StringSetAsync(x, "x-val");
//Assert.IsTrue(cluster.Wait(existsY), "y exists"); var setY = tran.StringSetAsync(y, "y-val");
} bool success = tran.Execute();
Assert.Fail("Expected single-slot rules to apply");
// the rest no longer applies while we are following single-slot rules
//// check that everything was aborted
//Assert.IsTrue(success, "tran aborted");
//Assert.IsFalse(setX.IsCanceled, "set x cancelled");
//Assert.IsFalse(setY.IsCanceled, "set y cancelled");
//var existsX = cluster.KeyExistsAsync(x);
//var existsY = cluster.KeyExistsAsync(y);
//Assert.IsTrue(cluster.Wait(existsX), "x exists");
//Assert.IsTrue(cluster.Wait(existsY), "y exists");
}
},
"Multi-key operations must involve a single slot; keys can use 'hash tags' to help this, i.e. '{/users/12345}/account' and '{/users/12345}/contacts' will always be in the same slot");
} }
[Test] [Test]
...@@ -361,7 +371,7 @@ public void Keys(string pattern, int pageSize) ...@@ -361,7 +371,7 @@ public void Keys(string pattern, int pageSize)
using (var conn = Create(allowAdmin: true)) using (var conn = Create(allowAdmin: true))
{ {
var cluster = conn.GetDatabase(); var cluster = conn.GetDatabase();
var server = Array.ConvertAll(conn.GetEndPoints(), x => conn.GetServer(x)).First(x => !x.IsSlave); var server = conn.GetEndPoints().Select(x => conn.GetServer(x)).First(x => !x.IsSlave);
server.FlushAllDatabases(); server.FlushAllDatabases();
try try
{ {
...@@ -467,7 +477,7 @@ public void AccessRandomKeys() ...@@ -467,7 +477,7 @@ public void AccessRandomKeys()
Task[] send = new Task[COUNT]; Task[] send = new Task[COUNT];
int index = 0; int index = 0;
var servers = Array.ConvertAll(conn.GetEndPoints(), x => conn.GetServer(x)); var servers = conn.GetEndPoints().Select(x => conn.GetServer(x));
foreach (var server in servers) foreach (var server in servers)
{ {
if (!server.IsSlave) if (!server.IsSlave)
...@@ -593,7 +603,7 @@ public void MovedProfiling() ...@@ -593,7 +603,7 @@ public void MovedProfiling()
conn.RegisterProfiler(profiler); conn.RegisterProfiler(profiler);
var endpoints = conn.GetEndPoints(); var endpoints = conn.GetEndPoints();
var servers = Array.ConvertAll(endpoints, e => conn.GetServer(e)); var servers = endpoints.Select(e => conn.GetServer(e));
conn.BeginProfiling(profiler.MyContext); conn.BeginProfiling(profiler.MyContext);
var db = conn.GetDatabase(); var db = conn.GetDatabase();
......
...@@ -125,13 +125,16 @@ public void ClientName() ...@@ -125,13 +125,16 @@ public void ClientName()
} }
[Test] [Test]
[ExpectedException(typeof(RedisCommandException), ExpectedMessage = "This operation has been disabled in the command-map and cannot be used: CONFIG")]
public void ReadConfigWithConfigDisabled() public void ReadConfigWithConfigDisabled()
{ {
using (var muxer = Create(allowAdmin: true, disabledCommands: new[] { "config", "info" })) using (var muxer = Create(allowAdmin: true, disabledCommands: new[] { "config", "info" }))
{ {
var conn = GetServer(muxer); var conn = GetServer(muxer);
var all = conn.ConfigGet(); Assert.Throws<RedisCommandException>(() =>
{
var all = conn.ConfigGet();
},
"This operation has been disabled in the command-map and cannot be used: CONFIG");
} }
} }
[Test] [Test]
...@@ -144,7 +147,11 @@ public void ReadConfig() ...@@ -144,7 +147,11 @@ public void ReadConfig()
var all = conn.ConfigGet(); var all = conn.ConfigGet();
Assert.IsTrue(all.Length > 0, "any"); Assert.IsTrue(all.Length > 0, "any");
#if !DNXCORE50
var pairs = all.ToDictionary(x => (string)x.Key, x => (string)x.Value, StringComparer.InvariantCultureIgnoreCase); var pairs = all.ToDictionary(x => (string)x.Key, x => (string)x.Value, StringComparer.InvariantCultureIgnoreCase);
#else
var pairs = all.ToDictionary(x => (string)x.Key, x => (string)x.Value, StringComparer.OrdinalIgnoreCase);
#endif
Assert.AreEqual(all.Length, pairs.Count); Assert.AreEqual(all.Length, pairs.Count);
Assert.IsTrue(pairs.ContainsKey("timeout"), "timeout"); Assert.IsTrue(pairs.ContainsKey("timeout"), "timeout");
...@@ -157,7 +164,7 @@ public void ReadConfig() ...@@ -157,7 +164,7 @@ public void ReadConfig()
} }
[Test] [Test]
public async void TestConfigureAsync() public async System.Threading.Tasks.Task TestConfigureAsync()
{ {
using(var muxer = Create()) using(var muxer = Create())
{ {
......
using System; #if FEATURE_MOQ
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Net; using System.Net;
...@@ -15,7 +16,8 @@ public sealed class DatabaseWrapperTests ...@@ -15,7 +16,8 @@ public sealed class DatabaseWrapperTests
private Mock<IDatabase> mock; private Mock<IDatabase> mock;
private DatabaseWrapper wrapper; private DatabaseWrapper wrapper;
[TestFixtureSetUp] //[TestFixtureSetUp]
[OneTimeSetUp]
public void Initialize() public void Initialize()
{ {
mock = new Mock<IDatabase>(); mock = new Mock<IDatabase>();
...@@ -289,10 +291,9 @@ public void KeyPersist() ...@@ -289,10 +291,9 @@ public void KeyPersist()
} }
[Test] [Test]
[ExpectedException(typeof(NotSupportedException))]
public void KeyRandom() public void KeyRandom()
{ {
wrapper.KeyRandom(); Assert.Throws<NotSupportedException>(() => wrapper.KeyRandom());
} }
[Test] [Test]
...@@ -933,3 +934,4 @@ public void StringSetRange() ...@@ -933,3 +934,4 @@ public void StringSetRange()
} }
} }
} }
#endif
\ No newline at end of file
...@@ -23,15 +23,21 @@ public void UnkonwnKeywordHandling_Ignore() ...@@ -23,15 +23,21 @@ public void UnkonwnKeywordHandling_Ignore()
{ {
var options = ConfigurationOptions.Parse("ssl2=true", true); var options = ConfigurationOptions.Parse("ssl2=true", true);
} }
[Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "Keyword 'ssl2' is not supported")] [Test]
public void UnkonwnKeywordHandling_ExplicitFail() public void UnkonwnKeywordHandling_ExplicitFail()
{ {
var options = ConfigurationOptions.Parse("ssl2=true", false); Assert.Throws<ArgumentException>(() => {
var options = ConfigurationOptions.Parse("ssl2=true", false);
},
"Keyword 'ssl2' is not supported");
} }
[Test, ExpectedException(typeof(ArgumentException), ExpectedMessage = "Keyword 'ssl2' is not supported")] [Test]
public void UnkonwnKeywordHandling_ImplicitFail() public void UnkonwnKeywordHandling_ImplicitFail()
{ {
var options = ConfigurationOptions.Parse("ssl2=true"); Assert.Throws<ArgumentException>(() => {
var options = ConfigurationOptions.Parse("ssl2=true");
},
"Keyword 'ssl2' is not supported");
} }
} }
} }
...@@ -9,7 +9,7 @@ public class SO25567566 : TestBase ...@@ -9,7 +9,7 @@ public class SO25567566 : TestBase
{ {
protected override string GetConfiguration() protected override string GetConfiguration()
{ {
return "127.0.0.1"; return "127.0.0.1:6379";
} }
[Test] [Test]
public async void Execute() public async void Execute()
......
...@@ -127,7 +127,7 @@ public enum TestMode ...@@ -127,7 +127,7 @@ public enum TestMode
NoMultiExec, NoMultiExec,
Twemproxy Twemproxy
} }
public IEnumerable<TestMode> TestModes() public static IEnumerable<TestMode> TestModes()
{ {
return (TestMode[])Enum.GetValues(typeof(TestMode)); return (TestMode[])Enum.GetValues(typeof(TestMode));
} }
......
...@@ -14,16 +14,19 @@ protected override string GetConfiguration() ...@@ -14,16 +14,19 @@ protected override string GetConfiguration()
return PrimaryServer + ":" + SecurePort + "," + PrimaryServer + ":" + PrimaryPort + ",password=" + SecurePassword; return PrimaryServer + ":" + SecurePort + "," + PrimaryServer + ":" + PrimaryPort + ",password=" + SecurePassword;
} }
[Test, ExpectedException(typeof(RedisCommandException), ExpectedMessage = "Command cannot be issued to a slave: FLUSHDB")] [Test]
public void CannotFlushSlave() public void CannotFlushSlave()
{ {
ConfigurationOptions config = GetMasterSlaveConfig(); Assert.Throws<RedisCommandException>(() => {
using (var conn = ConnectionMultiplexer.Connect(config)) ConfigurationOptions config = GetMasterSlaveConfig();
{ using (var conn = ConnectionMultiplexer.Connect(config))
var servers = Array.ConvertAll(conn.GetEndPoints(), e => conn.GetServer(e)); {
var slave = servers.First(x => x.IsSlave); var servers = conn.GetEndPoints().Select(e => conn.GetServer(e));
slave.FlushDatabase(); var slave = servers.First(x => x.IsSlave);
} slave.FlushDatabase();
}
},
"Command cannot be issued to a slave: FLUSHDB");
} }
[Test] [Test]
......
...@@ -17,7 +17,7 @@ public class Naming ...@@ -17,7 +17,7 @@ public class Naming
public void CheckSignatures(Type type, bool isAsync) public void CheckSignatures(Type type, bool isAsync)
{ {
// check that all methods and interfaces look appropriate for their sync/async nature // check that all methods and interfaces look appropriate for their sync/async nature
CheckName(type, isAsync); CheckName(type.GetTypeInfo(), isAsync);
var members = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); var members = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach(var member in members) foreach(var member in members)
{ {
...@@ -29,9 +29,9 @@ public void CheckSignatures(Type type, bool isAsync) ...@@ -29,9 +29,9 @@ public void CheckSignatures(Type type, bool isAsync)
[Test] [Test]
public void ShowReadOnlyOperations() public void ShowReadOnlyOperations()
{ {
var msg = typeof(ConnectionMultiplexer).Assembly.GetType("StackExchange.Redis.Message"); var msg = typeof(ConnectionMultiplexer).GetTypeInfo().Assembly.GetType("StackExchange.Redis.Message");
Assert.IsNotNull(msg, "Message"); Assert.IsNotNull(msg, "Message");
var cmd = typeof(ConnectionMultiplexer).Assembly.GetType("StackExchange.Redis.RedisCommand"); var cmd = typeof(ConnectionMultiplexer).GetTypeInfo().Assembly.GetType("StackExchange.Redis.RedisCommand");
Assert.IsNotNull(cmd, "RedisCommand"); Assert.IsNotNull(cmd, "RedisCommand");
var method = msg.GetMethod("IsMasterOnly", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); var method = msg.GetMethod("IsMasterOnly", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
Assert.IsNotNull(method, "IsMasterOnly"); Assert.IsNotNull(method, "IsMasterOnly");
...@@ -89,7 +89,7 @@ static bool UsesKey(Type type) ...@@ -89,7 +89,7 @@ static bool UsesKey(Type type)
{ {
if (UsesKey(type.GetElementType())) return true; if (UsesKey(type.GetElementType())) return true;
} }
if(type.IsGenericType) // KVP, etc if(type.GetTypeInfo().IsGenericType) // KVP, etc
{ {
var args = type.GetGenericArguments(); var args = type.GetGenericArguments();
if (args.Any(UsesKey)) return true; if (args.Any(UsesKey)) return true;
...@@ -139,7 +139,7 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to) ...@@ -139,7 +139,7 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to)
{ {
huntType = null; huntType = null;
} }
else if (method.ReturnType.IsSubclassOf(typeof(Task))) else if (method.ReturnType.GetTypeInfo().IsSubclassOf(typeof(Task)))
{ {
huntType = method.ReturnType.GetGenericArguments()[0]; huntType = method.ReturnType.GetGenericArguments()[0];
} }
...@@ -148,9 +148,14 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to) ...@@ -148,9 +148,14 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to)
huntType = typeof(Task<>).MakeGenericType(method.ReturnType); huntType = typeof(Task<>).MakeGenericType(method.ReturnType);
} }
var pFrom = method.GetParameters(); var pFrom = method.GetParameters();
Type[] args = Array.ConvertAll(pFrom, x => x.ParameterType); Type[] args = pFrom.Select(x => x.ParameterType).ToArray();
Assert.AreEqual(typeof(CommandFlags), args.Last()); Assert.AreEqual(typeof(CommandFlags), args.Last());
#if !DNXCORE50
var found = to.GetMethod(huntName, flags, null, method.CallingConvention, args, null); var found = to.GetMethod(huntName, flags, null, method.CallingConvention, args, null);
#else
var found = to.GetMethods(flags)
.SingleOrDefault(m => m.Name == huntName && m.HasMatchingParameterTypes(args));
#endif
Assert.IsNotNull(found, "Found " + name + ", no " + huntName); Assert.IsNotNull(found, "Found " + name + ", no " + huntName);
var pTo = found.GetParameters(); var pTo = found.GetParameters();
...@@ -166,16 +171,24 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to) ...@@ -166,16 +171,24 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to)
Console.WriteLine("Validated: {0} ({1} methods)", from.Name, count); Console.WriteLine("Validated: {0} ({1} methods)", from.Name, count);
} }
static readonly Type ignoreType = typeof(ConnectionMultiplexer).Assembly.GetType("StackExchange.Redis.IgnoreNamePrefixAttribute"); static readonly Type ignoreType = typeof(ConnectionMultiplexer).GetTypeInfo().Assembly.GetType("StackExchange.Redis.IgnoreNamePrefixAttribute");
void CheckMethod(MethodInfo method, bool isAsync) void CheckMethod(MethodInfo method, bool isAsync)
{ {
#if DEBUG #if DEBUG
#if !DNXCORE50
bool ignorePrefix = ignoreType != null && Attribute.IsDefined(method, ignoreType); bool ignorePrefix = ignoreType != null && Attribute.IsDefined(method, ignoreType);
if(ignorePrefix) #else
bool ignorePrefix = ignoreType != null && method.IsDefined(ignoreType);
#endif
if (ignorePrefix)
{ {
#if !DNXCORE50
Attribute attrib = Attribute.GetCustomAttribute(method, ignoreType); Attribute attrib = Attribute.GetCustomAttribute(method, ignoreType);
if((bool)attrib.GetType().GetProperty("IgnoreEntireMethod").GetValue(attrib)) #else
Attribute attrib = method.GetCustomAttribute(ignoreType);
#endif
if ((bool)attrib.GetType().GetProperty("IgnoreEntireMethod").GetValue(attrib))
{ {
return; return;
} }
...@@ -212,4 +225,34 @@ void CheckName(MemberInfo member, bool isAsync) ...@@ -212,4 +225,34 @@ void CheckName(MemberInfo member, bool isAsync)
else Assert.IsFalse(member.Name.EndsWith("Async"), member.Name + ":Name - don't end *Async"); else Assert.IsFalse(member.Name.EndsWith("Async"), member.Name + ":Name - don't end *Async");
} }
} }
public static class ReflectionExtensions
{
#if !DNXCORE50
public static Type GetTypeInfo(this Type type)
{
return type;
}
#else
public static bool HasMatchingParameterTypes(this MethodInfo method, Type[] paramTypes)
{
var types = method.GetParameters().Select(pi => pi.ParameterType).ToArray();
if (types.Length != paramTypes.Length)
{
return false;
}
for (int i = 0; i < types.Length; i++)
{
if (types[i] != paramTypes[i])
{
return false;
}
}
return true;
}
#endif
}
} }
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
#if DNXCORE50
using System.Reflection;
#endif
using System.Threading.Tasks; using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using System.Threading; using System.Threading;
...@@ -456,11 +459,11 @@ public void LowAllocationEnumerable() ...@@ -456,11 +459,11 @@ public void LowAllocationEnumerable()
conn.WaitAll(allTasks.ToArray()); conn.WaitAll(allTasks.ToArray());
var res = conn.FinishProfiling(profiler.MyContext); var res = conn.FinishProfiling(profiler.MyContext);
Assert.IsTrue(res.GetType().IsValueType); Assert.IsTrue(res.GetType().GetTypeInfo().IsValueType);
using(var e = res.GetEnumerator()) using(var e = res.GetEnumerator())
{ {
Assert.IsTrue(e.GetType().IsValueType); Assert.IsTrue(e.GetType().GetTypeInfo().IsValueType);
Assert.IsTrue(e.MoveNext()); Assert.IsTrue(e.MoveNext());
var i = e.Current; var i = e.Current;
......
...@@ -38,17 +38,17 @@ public void ExplicitPublishMode() ...@@ -38,17 +38,17 @@ public void ExplicitPublishMode()
Thread.Sleep(1000); Thread.Sleep(1000);
pub.Publish("abcd", "efg"); pub.Publish("abcd", "efg");
Thread.Sleep(500); Thread.Sleep(500);
Assert.AreEqual(0, Thread.VolatileRead(ref a), "a1"); Assert.AreEqual(0, VolatileWrapper.Read(ref a), "a1");
Assert.AreEqual(1, Thread.VolatileRead(ref b), "b1"); Assert.AreEqual(1, VolatileWrapper.Read(ref b), "b1");
Assert.AreEqual(1, Thread.VolatileRead(ref c), "c1"); Assert.AreEqual(1, VolatileWrapper.Read(ref c), "c1");
Assert.AreEqual(1, Thread.VolatileRead(ref d), "d1"); Assert.AreEqual(1, VolatileWrapper.Read(ref d), "d1");
pub.Publish("*bcd", "efg"); pub.Publish("*bcd", "efg");
Thread.Sleep(500); Thread.Sleep(500);
Assert.AreEqual(1, Thread.VolatileRead(ref a), "a2"); Assert.AreEqual(1, VolatileWrapper.Read(ref a), "a2");
//Assert.AreEqual(1, Thread.VolatileRead(ref b), "b2"); //Assert.AreEqual(1, VolatileWrapper.Read(ref b), "b2");
//Assert.AreEqual(1, Thread.VolatileRead(ref c), "c2"); //Assert.AreEqual(1, VolatileWrapper.Read(ref c), "c2");
//Assert.AreEqual(1, Thread.VolatileRead(ref d), "d2"); //Assert.AreEqual(1, VolatileWrapper.Read(ref d), "d2");
} }
} }
...@@ -101,7 +101,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC ...@@ -101,7 +101,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC
{ {
Assert.AreEqual(0, received.Count); Assert.AreEqual(0, received.Count);
} }
Assert.AreEqual(0, Thread.VolatileRead(ref secondHandler)); Assert.AreEqual(0, VolatileWrapper.Read(ref secondHandler));
var count = sub.Publish(pubChannel, "def"); var count = sub.Publish(pubChannel, "def");
Ping(muxer, pub, sub, 3); Ping(muxer, pub, sub, 3);
...@@ -110,7 +110,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC ...@@ -110,7 +110,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC
{ {
Assert.AreEqual(1, received.Count); Assert.AreEqual(1, received.Count);
} }
Assert.AreEqual(1, Thread.VolatileRead(ref secondHandler)); Assert.AreEqual(1, VolatileWrapper.Read(ref secondHandler));
// unsubscribe from first; should still see second // unsubscribe from first; should still see second
sub.Unsubscribe(subChannel, handler1); sub.Unsubscribe(subChannel, handler1);
...@@ -120,7 +120,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC ...@@ -120,7 +120,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC
{ {
Assert.AreEqual(1, received.Count); Assert.AreEqual(1, received.Count);
} }
Assert.AreEqual(2, Thread.VolatileRead(ref secondHandler)); Assert.AreEqual(2, VolatileWrapper.Read(ref secondHandler));
Assert.AreEqual(1, count); Assert.AreEqual(1, count);
// unsubscribe from second; should see nothing this time // unsubscribe from second; should see nothing this time
...@@ -131,7 +131,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC ...@@ -131,7 +131,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC
{ {
Assert.AreEqual(1, received.Count); Assert.AreEqual(1, received.Count);
} }
Assert.AreEqual(2, Thread.VolatileRead(ref secondHandler)); Assert.AreEqual(2, VolatileWrapper.Read(ref secondHandler));
Assert.AreEqual(0, count); Assert.AreEqual(0, count);
} }
} }
...@@ -172,7 +172,7 @@ public void TestBasicPubSubFireAndForget(bool preserveOrder) ...@@ -172,7 +172,7 @@ public void TestBasicPubSubFireAndForget(bool preserveOrder)
{ {
Assert.AreEqual(0, received.Count); Assert.AreEqual(0, received.Count);
} }
Assert.AreEqual(0, Thread.VolatileRead(ref secondHandler)); Assert.AreEqual(0, VolatileWrapper.Read(ref secondHandler));
Ping(muxer, pub, sub); Ping(muxer, pub, sub);
var count = sub.Publish(key, "def", CommandFlags.FireAndForget); var count = sub.Publish(key, "def", CommandFlags.FireAndForget);
Ping(muxer, pub, sub); Ping(muxer, pub, sub);
...@@ -181,7 +181,7 @@ public void TestBasicPubSubFireAndForget(bool preserveOrder) ...@@ -181,7 +181,7 @@ public void TestBasicPubSubFireAndForget(bool preserveOrder)
{ {
Assert.AreEqual(1, received.Count); Assert.AreEqual(1, received.Count);
} }
Assert.AreEqual(1, Thread.VolatileRead(ref secondHandler)); Assert.AreEqual(1, VolatileWrapper.Read(ref secondHandler));
sub.Unsubscribe(key); sub.Unsubscribe(key);
count = sub.Publish(key, "ghi", CommandFlags.FireAndForget); count = sub.Publish(key, "ghi", CommandFlags.FireAndForget);
...@@ -247,7 +247,7 @@ public void TestPatternPubSub(bool preserveOrder) ...@@ -247,7 +247,7 @@ public void TestPatternPubSub(bool preserveOrder)
{ {
Assert.AreEqual(0, received.Count); Assert.AreEqual(0, received.Count);
} }
Assert.AreEqual(0, Thread.VolatileRead(ref secondHandler)); Assert.AreEqual(0, VolatileWrapper.Read(ref secondHandler));
var count = sub.Publish("abc", "def"); var count = sub.Publish("abc", "def");
Ping(muxer, pub, sub); Ping(muxer, pub, sub);
...@@ -256,7 +256,7 @@ public void TestPatternPubSub(bool preserveOrder) ...@@ -256,7 +256,7 @@ public void TestPatternPubSub(bool preserveOrder)
{ {
Assert.AreEqual(1, received.Count); Assert.AreEqual(1, received.Count);
} }
Assert.AreEqual(1, Thread.VolatileRead(ref secondHandler)); Assert.AreEqual(1, VolatileWrapper.Read(ref secondHandler));
sub.Unsubscribe("a*c"); sub.Unsubscribe("a*c");
count = sub.Publish("abc", "ghi"); count = sub.Publish("abc", "ghi");
...@@ -392,7 +392,7 @@ public void SubscriptionsSurviveConnectionFailure() ...@@ -392,7 +392,7 @@ public void SubscriptionsSurviveConnectionFailure()
}); });
sub.Publish(channel, "abc"); sub.Publish(channel, "abc");
sub.Ping(); sub.Ping();
Assert.AreEqual(1, Thread.VolatileRead(ref counter), "counter"); Assert.AreEqual(1, VolatileWrapper.Read(ref counter), "counter");
var server = GetServer(muxer); var server = GetServer(muxer);
Assert.AreEqual(1, server.GetCounters().Subscription.SocketCount, "sockets"); Assert.AreEqual(1, server.GetCounters().Subscription.SocketCount, "sockets");
...@@ -408,8 +408,20 @@ public void SubscriptionsSurviveConnectionFailure() ...@@ -408,8 +408,20 @@ public void SubscriptionsSurviveConnectionFailure()
#endif #endif
sub.Publish(channel, "abc"); sub.Publish(channel, "abc");
sub.Ping(); sub.Ping();
Assert.AreEqual(2, Thread.VolatileRead(ref counter), "counter"); Assert.AreEqual(2, VolatileWrapper.Read(ref counter), "counter");
} }
} }
} }
internal static class VolatileWrapper
{
public static int Read(ref int location)
{
#if !DNXCORE50
return Thread.VolatileRead(ref location);
#else
return Volatile.Read(ref location);
#endif
}
}
} }
...@@ -158,7 +158,7 @@ public void TestCallByHash() ...@@ -158,7 +158,7 @@ public void TestCallByHash()
var db = conn.GetDatabase(); var db = conn.GetDatabase();
RedisKey[] keys = { Me() }; RedisKey[] keys = { Me() };
string hexHash = string.Concat(Array.ConvertAll(hash, x => x.ToString("X2"))); string hexHash = string.Concat(hash.Select(x => x.ToString("X2")));
Assert.AreEqual("2BAB3B661081DB58BD2341920E0BA7CF5DC77B25", hexHash); Assert.AreEqual("2BAB3B661081DB58BD2341920E0BA7CF5DC77B25", hexHash);
db.ScriptEvaluate(hexHash, keys); db.ScriptEvaluate(hexHash, keys);
......
...@@ -68,14 +68,16 @@ public void Connect() ...@@ -68,14 +68,16 @@ public void Connect()
[Test] [Test]
[TestCase("wrong")] [TestCase("wrong")]
[TestCase("")] [TestCase("")]
[ExpectedException(typeof(RedisConnectionException), ExpectedMessage = "No connection is available to service this operation: PING")]
public void ConnectWithWrongPassword(string password) public void ConnectWithWrongPassword(string password)
{ {
SetExpectedAmbientFailureCount(-1); Assert.Throws<RedisConnectionException>(() => {
using (var server = Create(password: password, checkConnect: false)) SetExpectedAmbientFailureCount(-1);
{ using (var server = Create(password: password, checkConnect: false))
server.GetDatabase().Ping(); {
} server.GetDatabase().Ping();
}
},
"No connection is available to service this operation: PING");
} }
} }
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[TestFixture, Ignore] [TestFixture, Ignore("reason?")]
public class Sentinel public class Sentinel
{ {
// TODO fill in these constants before running tests // TODO fill in these constants before running tests
...@@ -100,7 +100,7 @@ public void SentinelSlavesTest() ...@@ -100,7 +100,7 @@ public void SentinelSlavesTest()
} }
} }
[Test, Ignore] [Test, Ignore("reason?")]
public void SentinelFailoverTest() public void SentinelFailoverTest()
{ {
Server.SentinelFailover(ServiceName); Server.SentinelFailover(ServiceName);
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;PLAT_SAFE_CONTINUATIONS</DefineConstants> <DefineConstants>TRACE;DEBUG;PLAT_SAFE_CONTINUATIONS;FEATURE_BOOKSLEEVE;FEATURE_MOQ</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
...@@ -28,14 +28,14 @@ ...@@ -28,14 +28,14 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;PLAT_SAFE_CONTINUATIONS</DefineConstants> <DefineConstants>TRACE;PLAT_SAFE_CONTINUATIONS;FEATURE_BOOKSLEEVE;FEATURE_MOQ</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Verbose|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Verbose|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Verbose\</OutputPath> <OutputPath>bin\Verbose\</OutputPath>
<DefineConstants>TRACE;DEBUG;VERBOSE</DefineConstants> <DefineConstants>TRACE;DEBUG;VERBOSE;FEATURE_BOOKSLEEVE;FEATURE_MOQ</DefineConstants>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Log Output|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Log Output|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Log Output\</OutputPath> <OutputPath>bin\Log Output\</OutputPath>
<DefineConstants>TRACE;DEBUG;LOGOUTPUT</DefineConstants> <DefineConstants>TRACE;DEBUG;LOGOUTPUT;FEATURE_BOOKSLEEVE;FEATURE_MOQ</DefineConstants>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'">
<OutputPath>bin\Mono\</OutputPath> <OutputPath>bin\Mono\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE;FEATURE_BOOKSLEEVE;FEATURE_MOQ</DefineConstants>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
...@@ -68,9 +68,9 @@ ...@@ -68,9 +68,9 @@
<HintPath>..\packages\Moq.4.2.1502.0911\lib\net40\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.2.1502.0911\lib\net40\Moq.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=3.0.5797.27534, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <HintPath>..\packages\NUnit.3.0.0\lib\net45\nunit.framework.dll</HintPath>
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
#if FEATURE_BOOKSLEEVE
using BookSleeve; using BookSleeve;
#endif
using NUnit.Framework; using NUnit.Framework;
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
...@@ -221,6 +223,7 @@ protected static string Me([CallerMemberName] string caller = null) ...@@ -221,6 +223,7 @@ protected static string Me([CallerMemberName] string caller = null)
return caller; return caller;
} }
#if FEATURE_BOOKSLEEVE
protected static RedisConnection GetOldStyleConnection(bool open = true, bool allowAdmin = false, bool waitForOpen = false, int syncTimeout = 5000, int ioTimeout = 5000) protected static RedisConnection GetOldStyleConnection(bool open = true, bool allowAdmin = false, bool waitForOpen = false, int syncTimeout = 5000, int ioTimeout = 5000)
{ {
return GetOldStyleConnection(PrimaryServer, PrimaryPort, open, allowAdmin, waitForOpen, syncTimeout, ioTimeout); return GetOldStyleConnection(PrimaryServer, PrimaryPort, open, allowAdmin, waitForOpen, syncTimeout, ioTimeout);
...@@ -239,7 +242,7 @@ private static RedisConnection GetOldStyleConnection(string host, int port, bool ...@@ -239,7 +242,7 @@ private static RedisConnection GetOldStyleConnection(string host, int port, bool
} }
return conn; return conn;
} }
#endif
protected static TimeSpan RunConcurrent(Action work, int threads, int timeout = 10000, [CallerMemberName] string caller = null) protected static TimeSpan RunConcurrent(Action work, int threads, int timeout = 10000, [CallerMemberName] string caller = null)
{ {
if (work == null) throw new ArgumentNullException("work"); if (work == null) throw new ArgumentNullException("work");
...@@ -282,11 +285,13 @@ protected static TimeSpan RunConcurrent(Action work, int threads, int timeout = ...@@ -282,11 +285,13 @@ protected static TimeSpan RunConcurrent(Action work, int threads, int timeout =
} }
if (!allDone.WaitOne(timeout)) if (!allDone.WaitOne(timeout))
{ {
for(int i = 0; i < threads; i++) #if !DNXCORE50
for (int i = 0; i < threads; i++)
{ {
var thd = threadArr[i]; var thd = threadArr[i];
if (thd.IsAlive) thd.Abort(); if (thd.IsAlive) thd.Abort();
} }
#endif
throw new TimeoutException(); throw new TimeoutException();
} }
......
using System.Text; #if FEATURE_MOQ
using System.Text;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using StackExchange.Redis.KeyspaceIsolation; using StackExchange.Redis.KeyspaceIsolation;
...@@ -11,7 +12,7 @@ public sealed class TransactionWrapperTests ...@@ -11,7 +12,7 @@ public sealed class TransactionWrapperTests
private Mock<ITransaction> mock; private Mock<ITransaction> mock;
private TransactionWrapper wrapper; private TransactionWrapper wrapper;
[TestFixtureSetUp] [OneTimeSetUp]
public void Initialize() public void Initialize()
{ {
mock = new Mock<ITransaction>(); mock = new Mock<ITransaction>();
...@@ -89,3 +90,4 @@ public void Execute() ...@@ -89,3 +90,4 @@ public void Execute()
} }
} }
} }
#endif
\ No newline at end of file
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using StackExchange.Redis.KeyspaceIsolation; using StackExchange.Redis.KeyspaceIsolation;
namespace StackExchange.Redis.Tests namespace StackExchange.Redis.Tests
{ {
[TestFixture] [TestFixture]
public class WithKeyPrefixTests : TestBase public class WithKeyPrefixTests : TestBase
{ {
[Test] [Test]
public void BlankPrefixYieldsSame_Bytes() public void BlankPrefixYieldsSame_Bytes()
{ {
using (var conn = Create()) using (var conn = Create())
{ {
var raw = conn.GetDatabase(1); var raw = conn.GetDatabase(1);
var prefixed = raw.WithKeyPrefix(new byte[0]); var prefixed = raw.WithKeyPrefix(new byte[0]);
Assert.AreSame(raw, prefixed); Assert.AreSame(raw, prefixed);
} }
} }
[Test] [Test]
public void BlankPrefixYieldsSame_String() public void BlankPrefixYieldsSame_String()
{ {
using (var conn = Create()) using (var conn = Create())
{ {
var raw = conn.GetDatabase(1); var raw = conn.GetDatabase(1);
var prefixed = raw.WithKeyPrefix(""); var prefixed = raw.WithKeyPrefix("");
Assert.AreSame(raw, prefixed); Assert.AreSame(raw, prefixed);
} }
} }
[Test, ExpectedException(typeof(ArgumentNullException))] [Test]
public void NullPrefixIsError_Bytes() public void NullPrefixIsError_Bytes()
{ {
using (var conn = Create()) Assert.Throws<ArgumentNullException>(() => {
{ using (var conn = Create())
var raw = conn.GetDatabase(1); {
var prefixed = raw.WithKeyPrefix((byte[])null); var raw = conn.GetDatabase(1);
} var prefixed = raw.WithKeyPrefix((byte[])null);
} }
[Test, ExpectedException(typeof(ArgumentNullException))] });
public void NullPrefixIsError_String() }
{
using (var conn = Create()) [Test]
{ public void NullPrefixIsError_String()
var raw = conn.GetDatabase(1); {
var prefixed = raw.WithKeyPrefix((string)null); Assert.Throws<ArgumentNullException>(() => {
} using (var conn = Create())
} {
var raw = conn.GetDatabase(1);
[Test, ExpectedException(typeof(ArgumentNullException))] var prefixed = raw.WithKeyPrefix((string)null);
[TestCase("abc")] }
[TestCase("")] });
[TestCase(null)] }
public void NullDatabaseIsError(string prefix)
{ [Test]
IDatabase raw = null; [TestCase("abc")]
var prefixed = raw.WithKeyPrefix(prefix); [TestCase("")]
} [TestCase(null)]
[Test] public void NullDatabaseIsError(string prefix)
public void BasicSmokeTest() {
{ Assert.Throws<ArgumentNullException>(() => {
using(var conn = Create()) IDatabase raw = null;
{ var prefixed = raw.WithKeyPrefix(prefix);
var raw = conn.GetDatabase(1); });
}
var foo = raw.WithKeyPrefix("foo");
var foobar = foo.WithKeyPrefix("bar"); [Test]
public void BasicSmokeTest()
string key = Me(); {
using(var conn = Create())
string s = Guid.NewGuid().ToString(), t = Guid.NewGuid().ToString(); {
var raw = conn.GetDatabase(1);
foo.StringSet(key, s);
var val = (string)foo.StringGet(key); var foo = raw.WithKeyPrefix("foo");
Assert.AreEqual(s, val); // fooBasicSmokeTest var foobar = foo.WithKeyPrefix("bar");
foobar.StringSet(key, t); string key = Me();
val = (string)foobar.StringGet(key);
Assert.AreEqual(t, val); // foobarBasicSmokeTest string s = Guid.NewGuid().ToString(), t = Guid.NewGuid().ToString();
val = (string)foo.StringGet("bar" + key); foo.StringSet(key, s);
Assert.AreEqual(t, val); // foobarBasicSmokeTest var val = (string)foo.StringGet(key);
Assert.AreEqual(s, val); // fooBasicSmokeTest
val = (string)raw.StringGet("foo" + key);
Assert.AreEqual(s, val); // fooBasicSmokeTest foobar.StringSet(key, t);
val = (string)foobar.StringGet(key);
val = (string)raw.StringGet("foobar" + key); Assert.AreEqual(t, val); // foobarBasicSmokeTest
Assert.AreEqual(t, val); // foobarBasicSmokeTest
} val = (string)foo.StringGet("bar" + key);
} Assert.AreEqual(t, val); // foobarBasicSmokeTest
[Test]
public void ConditionTest() val = (string)raw.StringGet("foo" + key);
{ Assert.AreEqual(s, val); // fooBasicSmokeTest
using(var conn = Create())
{ val = (string)raw.StringGet("foobar" + key);
var raw = conn.GetDatabase(2); Assert.AreEqual(t, val); // foobarBasicSmokeTest
}
var foo = raw.WithKeyPrefix("tran:"); }
[Test]
raw.KeyDelete("tran:abc"); public void ConditionTest()
raw.KeyDelete("tran:i"); {
using(var conn = Create())
// execute while key exists {
raw.StringSet("tran:abc", "def"); var raw = conn.GetDatabase(2);
var tran = foo.CreateTransaction();
tran.AddCondition(Condition.KeyExists("abc")); var foo = raw.WithKeyPrefix("tran:");
tran.StringIncrementAsync("i");
tran.Execute(); raw.KeyDelete("tran:abc");
raw.KeyDelete("tran:i");
int i = (int)raw.StringGet("tran:i");
Assert.AreEqual(1, i); // execute while key exists
raw.StringSet("tran:abc", "def");
// repeat without key var tran = foo.CreateTransaction();
raw.KeyDelete("tran:abc"); tran.AddCondition(Condition.KeyExists("abc"));
tran = foo.CreateTransaction(); tran.StringIncrementAsync("i");
tran.AddCondition(Condition.KeyExists("abc")); tran.Execute();
tran.StringIncrementAsync("i");
tran.Execute(); int i = (int)raw.StringGet("tran:i");
Assert.AreEqual(1, i);
i = (int)raw.StringGet("tran:i");
Assert.AreEqual(1, i); // repeat without key
} raw.KeyDelete("tran:abc");
} tran = foo.CreateTransaction();
} tran.AddCondition(Condition.KeyExists("abc"));
} tran.StringIncrementAsync("i");
tran.Execute();
i = (int)raw.StringGet("tran:i");
Assert.AreEqual(1, i);
}
}
}
}
using System; #if FEATURE_MOQ
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Net; using System.Net;
...@@ -15,7 +16,8 @@ public sealed class WrapperBaseTests ...@@ -15,7 +16,8 @@ public sealed class WrapperBaseTests
private Mock<IDatabaseAsync> mock; private Mock<IDatabaseAsync> mock;
private WrapperBase<IDatabaseAsync> wrapper; private WrapperBase<IDatabaseAsync> wrapper;
[TestFixtureSetUp] //[TestFixtureSetUp]
[OneTimeSetUp]
public void Initialize() public void Initialize()
{ {
mock = new Mock<IDatabaseAsync>(); mock = new Mock<IDatabaseAsync>();
...@@ -258,10 +260,11 @@ public void KeyPersistAsync() ...@@ -258,10 +260,11 @@ public void KeyPersistAsync()
} }
[Test] [Test]
[ExpectedException(typeof(NotSupportedException))]
public void KeyRandomAsync() public void KeyRandomAsync()
{ {
wrapper.KeyRandomAsync(); Assert.Throws<NotSupportedException>(() => {
wrapper.KeyRandomAsync();
});
} }
[Test] [Test]
...@@ -888,3 +891,4 @@ public void StringSetRangeAsync() ...@@ -888,3 +891,4 @@ public void StringSetRangeAsync()
} }
} }
} }
#endif
\ No newline at end of file
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
<packages> <packages>
<package id="BookSleeve" version="1.3.41" targetFramework="net45" /> <package id="BookSleeve" version="1.3.41" targetFramework="net45" />
<package id="Moq" version="4.2.1502.0911" targetFramework="net45" /> <package id="Moq" version="4.2.1502.0911" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" /> <package id="NUnit" version="3.0.0" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
using System;
using System.Reflection;
using NUnitLite;
namespace StackExchange.Redis.Tests
{
public class Program
{
public int Main(string[] args)
{
return new AutoRun().Execute(typeof(TestBase).GetTypeInfo().Assembly, Console.Out, Console.In, args);
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a51dca17-e8f1-44e1-9a37-328fab14d2ce</ProjectGuid>
<RootNamespace>StackExchange.Redis.Tests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\StackExchange.Redis_dnxcore50\StackExchange.Redis\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\StackExchange.Redis_dnxcore50\StackExchange.Redis\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
\ No newline at end of file
{
"version": "1.0.0-*",
"description": "StackExchange.Redis.Tests",
"authors": [ "jeremymeng" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"compile": [
"../../StackExchange.Redis.Tests/**/*.cs"
],
"dependencies": {
"StackExchange.Redis": "1.1.0-*"
},
"commands": {
"run": "StackExchange.Redis.Tests"
},
"configurations": {
"Debug": {
"compilationOptions": {
"define": [ "DEBUG", "TRACE", "PLAT_SAFE_CONTINUATIONS" ]
}
},
"Release": {
"compilationOptions": {
"define": [ "TRACE", "PLAT_SAFE_CONTINUATIONS" ]
}
}
},
"frameworks": {
"dnxcore50": {
"dependencies": {
"System.Console": "4.0.0-beta-*",
"System.Linq.Expressions": "4.0.11-beta-23409",
"System.Reflection.Extensions": "4.0.1-beta-23409",
"System.Threading.Tasks.Parallel": "4.0.1-beta-23409",
"Microsoft.CSharp": "4.0.1-beta-23409",
"nunitlite": "3.0.0"
}
}
}
}

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24709.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "StackExchange.Redis", "StackExchange.Redis_dnxcore50\StackExchange.Redis\StackExchange.Redis.xproj", "{86526B5C-1163-4481-A5E2-A303A0BB1535}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "StackExchange.Redis.Tests", "StackExchange.Redis.Tests_dnxcore50\StackExchange.Redis.Tests\StackExchange.Redis.Tests.xproj", "{A51DCA17-E8F1-44E1-9A37-328FAB14D2CE}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "BasicTest_dnxcore50", "BasicTest_dnxcore50\BasicTest_dnxcore50.xproj", "{9D83BABA-A92E-495F-BF63-DEB4F6B09355}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{86526B5C-1163-4481-A5E2-A303A0BB1535}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86526B5C-1163-4481-A5E2-A303A0BB1535}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86526B5C-1163-4481-A5E2-A303A0BB1535}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86526B5C-1163-4481-A5E2-A303A0BB1535}.Release|Any CPU.Build.0 = Release|Any CPU
{A51DCA17-E8F1-44E1-9A37-328FAB14D2CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A51DCA17-E8F1-44E1-9A37-328FAB14D2CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A51DCA17-E8F1-44E1-9A37-328FAB14D2CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A51DCA17-E8F1-44E1-9A37-328FAB14D2CE}.Release|Any CPU.Build.0 = Release|Any CPU
{9D83BABA-A92E-495F-BF63-DEB4F6B09355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D83BABA-A92E-495F-BF63-DEB4F6B09355}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D83BABA-A92E-495F-BF63-DEB4F6B09355}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D83BABA-A92E-495F-BF63-DEB4F6B09355}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{EBF46088-E318-4D32-9EFB-01EF130A4554}</ProjectGuid> <ProjectGuid>{EBF46088-E318-4D32-9EFB-01EF130A4554}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>StackExchange.Redis</RootNamespace> <RootNamespace>StackExchange.Redis</RootNamespace>
<AssemblyName>StackExchange.Redis.StrongName</AssemblyName> <AssemblyName>StackExchange.Redis.StrongName</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin.snk\Debug\</OutputPath> <OutputPath>bin.snk\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;STRONG_NAME</DefineConstants> <DefineConstants>TRACE;DEBUG;STRONG_NAME FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin.snk\Debug\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\Debug\StackExchange.Redis.StrongName.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin.snk\Release\</OutputPath> <OutputPath>bin.snk\Release\</OutputPath>
<DefineConstants>TRACE;STRONG_NAME</DefineConstants> <DefineConstants>TRACE;STRONG_NAME FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Verbose|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Verbose|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin.snk\Verbose\</OutputPath> <OutputPath>bin.snk\Verbose\</OutputPath>
<DefineConstants>TRACE;DEBUG;VERBOSE STRONG_NAME</DefineConstants> <DefineConstants>TRACE;DEBUG;VERBOSE STRONG_NAME FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin.snk\Verbose\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\Verbose\StackExchange.Redis.StrongName.xml</DocumentationFile>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Log Output|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Log Output|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin.snk\LogOutput\</OutputPath> <OutputPath>bin.snk\LogOutput\</OutputPath>
<DefineConstants>TRACE;DEBUG;LOGOUTPUT STRONG_NAME</DefineConstants> <DefineConstants>TRACE;DEBUG;LOGOUTPUT STRONG_NAME FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin.snk\LogOutput\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\LogOutput\StackExchange.Redis.StrongName.xml</DocumentationFile>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<AssemblyOriginatorKeyFile>StackExchange.Redis.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>StackExchange.Redis.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'">
<OutputPath>bin\Mono\</OutputPath> <OutputPath>bin\Mono\</OutputPath>
<DefineConstants>TRACE;STRONG_NAME</DefineConstants> <DefineConstants>TRACE;STRONG_NAME FEATURE_SERIALIZATION</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.IO.Compression" /> <Reference Include="System.IO.Compression" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="StackExchange\Redis\Aggregate.cs" /> <Compile Include="StackExchange\Redis\Aggregate.cs" />
<Compile Include="StackExchange\Redis\ClientType.cs" /> <Compile Include="StackExchange\Redis\ClientType.cs" />
<Compile Include="StackExchange\Redis\ConcurrentProfileStorageCollection.cs" /> <Compile Include="StackExchange\Redis\Compat\VolatileWrapper.cs" />
<Compile Include="StackExchange\Redis\ConnectionMultiplexer.Profiling.cs"> <Compile Include="StackExchange\Redis\ConcurrentProfileStorageCollection.cs" />
<DependentUpon>ConnectionMultiplexer.cs</DependentUpon> <Compile Include="StackExchange\Redis\ConnectionMultiplexer.Profiling.cs">
</Compile> <DependentUpon>ConnectionMultiplexer.cs</DependentUpon>
<Compile Include="StackExchange\Redis\ExtensionMethods.cs" /> </Compile>
<Compile Include="StackExchange\Redis\HashEntry.cs" /> <Compile Include="StackExchange\Redis\ExtensionMethods.cs" />
<Compile Include="StackExchange\Redis\IConnectionMultiplexer.cs" /> <Compile Include="StackExchange\Redis\HashEntry.cs" />
<Compile Include="StackExchange\Redis\InternalErrorEventArgs.cs" /> <Compile Include="StackExchange\Redis\IConnectionMultiplexer.cs" />
<Compile Include="StackExchange\Redis\IProfiler.cs" /> <Compile Include="StackExchange\Redis\InternalErrorEventArgs.cs" />
<Compile Include="StackExchange\Redis\MigrateOptions.cs" /> <Compile Include="StackExchange\Redis\InternalRegexCompiledOption.cs" />
<Compile Include="StackExchange\Redis\ProfileContextTracker.cs" /> <Compile Include="StackExchange\Redis\IProfiler.cs" />
<Compile Include="StackExchange\Redis\ProfileStorage.cs" /> <Compile Include="StackExchange\Redis\MigrateOptions.cs" />
<Compile Include="StackExchange\Redis\LuaScript.cs" /> <Compile Include="StackExchange\Redis\ProfileContextTracker.cs" />
<Compile Include="StackExchange\Redis\RedisChannel.cs" /> <Compile Include="StackExchange\Redis\ProfileStorage.cs" />
<Compile Include="StackExchange\Redis\Bitwise.cs" /> <Compile Include="StackExchange\Redis\LuaScript.cs" />
<Compile Include="StackExchange\Redis\ClientFlags.cs" /> <Compile Include="StackExchange\Redis\RedisChannel.cs" />
<Compile Include="StackExchange\Redis\ClientInfo.cs" /> <Compile Include="StackExchange\Redis\Bitwise.cs" />
<Compile Include="StackExchange\Redis\ClusterConfiguration.cs" /> <Compile Include="StackExchange\Redis\ClientFlags.cs" />
<Compile Include="StackExchange\Redis\CommandTrace.cs" /> <Compile Include="StackExchange\Redis\ClientInfo.cs" />
<Compile Include="StackExchange\Redis\Condition.cs" /> <Compile Include="StackExchange\Redis\ClusterConfiguration.cs" />
<Compile Include="StackExchange\Redis\ConfigurationOptions.cs" /> <Compile Include="StackExchange\Redis\CommandTrace.cs" />
<Compile Include="StackExchange\Redis\ConnectionCounters.cs" /> <Compile Include="StackExchange\Redis\Condition.cs" />
<Compile Include="StackExchange\Redis\ConnectionFailedEventArgs.cs" /> <Compile Include="StackExchange\Redis\ConfigurationOptions.cs" />
<Compile Include="StackExchange\Redis\ConnectionFailureType.cs" /> <Compile Include="StackExchange\Redis\ConnectionCounters.cs" />
<Compile Include="StackExchange\Redis\ConnectionMultiplexer.ReaderWriter.cs"> <Compile Include="StackExchange\Redis\ConnectionFailedEventArgs.cs" />
<DependentUpon>ConnectionMultiplexer.cs</DependentUpon> <Compile Include="StackExchange\Redis\ConnectionFailureType.cs" />
</Compile> <Compile Include="StackExchange\Redis\ConnectionMultiplexer.ReaderWriter.cs">
<Compile Include="StackExchange\Redis\ConnectionType.cs" /> <DependentUpon>ConnectionMultiplexer.cs</DependentUpon>
<Compile Include="StackExchange\Redis\DebuggingAids.cs" /> </Compile>
<Compile Include="StackExchange\Redis\EndPointCollection.cs" /> <Compile Include="StackExchange\Redis\ConnectionType.cs" />
<Compile Include="StackExchange\Redis\EndPointEventArgs.cs" /> <Compile Include="StackExchange\Redis\DebuggingAids.cs" />
<Compile Include="StackExchange\Redis\ExceptionFactory.cs" /> <Compile Include="StackExchange\Redis\EndPointCollection.cs" />
<Compile Include="StackExchange\Redis\Exclude.cs" /> <Compile Include="StackExchange\Redis\EndPointEventArgs.cs" />
<Compile Include="StackExchange\Redis\ExportOptions.cs" /> <Compile Include="StackExchange\Redis\ExceptionFactory.cs" />
<Compile Include="StackExchange\Redis\Format.cs" /> <Compile Include="StackExchange\Redis\Exclude.cs" />
<Compile Include="StackExchange\Redis\IBatch.cs" /> <Compile Include="StackExchange\Redis\ExportOptions.cs" />
<Compile Include="StackExchange\Redis\IMultiMessage.cs" /> <Compile Include="StackExchange\Redis\Format.cs" />
<Compile Include="StackExchange\Redis\Order.cs" /> <Compile Include="StackExchange\Redis\IBatch.cs" />
<Compile Include="StackExchange\Redis\RedisBatch.cs" /> <Compile Include="StackExchange\Redis\IMultiMessage.cs" />
<Compile Include="StackExchange\Redis\RedisCommand.cs" /> <Compile Include="StackExchange\Redis\Order.cs" />
<Compile Include="StackExchange\Redis\RedisErrorEventArgs.cs" /> <Compile Include="StackExchange\Redis\RedisBatch.cs" />
<Compile Include="StackExchange\Redis\HashSlotMovedEventArgs.cs" /> <Compile Include="StackExchange\Redis\RedisCommand.cs" />
<Compile Include="StackExchange\Redis\ITransaction.cs" /> <Compile Include="StackExchange\Redis\RedisErrorEventArgs.cs" />
<Compile Include="StackExchange\Redis\IRedis.cs" /> <Compile Include="StackExchange\Redis\HashSlotMovedEventArgs.cs" />
<Compile Include="StackExchange\Redis\IRedisAsync.cs" /> <Compile Include="StackExchange\Redis\ITransaction.cs" />
<Compile Include="StackExchange\Redis\IDatabase.cs" /> <Compile Include="StackExchange\Redis\IRedis.cs" />
<Compile Include="StackExchange\Redis\IDatabaseAsync.cs" /> <Compile Include="StackExchange\Redis\IRedisAsync.cs" />
<Compile Include="StackExchange\Redis\IServer.cs" /> <Compile Include="StackExchange\Redis\IDatabase.cs" />
<Compile Include="StackExchange\Redis\ISubscriber.cs" /> <Compile Include="StackExchange\Redis\IDatabaseAsync.cs" />
<Compile Include="StackExchange\Redis\MessageCompletable.cs" /> <Compile Include="StackExchange\Redis\IServer.cs" />
<Compile Include="StackExchange\Redis\RedisBase.cs" /> <Compile Include="StackExchange\Redis\ISubscriber.cs" />
<Compile Include="StackExchange\Redis\CommandFlags.cs" /> <Compile Include="StackExchange\Redis\MessageCompletable.cs" />
<Compile Include="StackExchange\Redis\CommandMap.cs" /> <Compile Include="StackExchange\Redis\RedisBase.cs" />
<Compile Include="StackExchange\Redis\CompletionManager.cs" /> <Compile Include="StackExchange\Redis\CommandFlags.cs" />
<Compile Include="StackExchange\Redis\ConnectionMultiplexer.cs" /> <Compile Include="StackExchange\Redis\CommandMap.cs" />
<Compile Include="StackExchange\Redis\CompletedDefaultTask.cs" /> <Compile Include="StackExchange\Redis\CompletionManager.cs" />
<Compile Include="StackExchange\Redis\ICompletable.cs" /> <Compile Include="StackExchange\Redis\ConnectionMultiplexer.cs" />
<Compile Include="StackExchange\Redis\LoggingTextStream.cs" /> <Compile Include="StackExchange\Redis\CompletedDefaultTask.cs" />
<Compile Include="StackExchange\Redis\PhysicalBridge.cs" /> <Compile Include="StackExchange\Redis\ICompletable.cs" />
<Compile Include="StackExchange\Redis\Message.cs" /> <Compile Include="StackExchange\Redis\LoggingTextStream.cs" />
<Compile Include="StackExchange\Redis\MessageQueue.cs" /> <Compile Include="StackExchange\Redis\PhysicalBridge.cs" />
<Compile Include="StackExchange\Redis\PhysicalConnection.cs" /> <Compile Include="StackExchange\Redis\Message.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="StackExchange\Redis\MessageQueue.cs" />
<Compile Include="StackExchange\Redis\RedisResult.cs" /> <Compile Include="StackExchange\Redis\PhysicalConnection.cs" />
<Compile Include="StackExchange\Redis\RedisTransaction.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StackExchange\Redis\RedisDatabase.cs" /> <Compile Include="StackExchange\Redis\RedisResult.cs" />
<Compile Include="StackExchange\Redis\RedisFeatures.cs" /> <Compile Include="StackExchange\Redis\RedisTransaction.cs" />
<Compile Include="StackExchange\Redis\RedisKey.cs" /> <Compile Include="StackExchange\Redis\RedisDatabase.cs" />
<Compile Include="StackExchange\Redis\RedisLiterals.cs" /> <Compile Include="StackExchange\Redis\RedisFeatures.cs" />
<Compile Include="StackExchange\Redis\RedisServer.cs" /> <Compile Include="StackExchange\Redis\RedisKey.cs" />
<Compile Include="StackExchange\Redis\RedisType.cs" /> <Compile Include="StackExchange\Redis\RedisLiterals.cs" />
<Compile Include="StackExchange\Redis\RedisValue.cs" /> <Compile Include="StackExchange\Redis\RedisServer.cs" />
<Compile Include="StackExchange\Redis\ReplicationChangeOptions.cs" /> <Compile Include="StackExchange\Redis\RedisType.cs" />
<Compile Include="StackExchange\Redis\RawResult.cs" /> <Compile Include="StackExchange\Redis\RedisValue.cs" />
<Compile Include="StackExchange\Redis\ResultBox.cs" /> <Compile Include="StackExchange\Redis\ReplicationChangeOptions.cs" />
<Compile Include="StackExchange\Redis\ResultProcessor.cs" /> <Compile Include="StackExchange\Redis\RawResult.cs" />
<Compile Include="StackExchange\Redis\RedisSubscriber.cs" /> <Compile Include="StackExchange\Redis\ResultBox.cs" />
<Compile Include="StackExchange\Redis\ResultType.cs" /> <Compile Include="StackExchange\Redis\ResultProcessor.cs" />
<Compile Include="StackExchange\Redis\ScriptParameterMapper.cs" /> <Compile Include="StackExchange\Redis\RedisSubscriber.cs" />
<Compile Include="StackExchange\Redis\ServerCounters.cs" /> <Compile Include="StackExchange\Redis\ResultType.cs" />
<Compile Include="StackExchange\Redis\ServerEndPoint.cs" /> <Compile Include="StackExchange\Redis\ScriptParameterMapper.cs" />
<Compile Include="StackExchange\Redis\ServerSelectionStrategy.cs" /> <Compile Include="StackExchange\Redis\ServerCounters.cs" />
<Compile Include="StackExchange\Redis\ServerType.cs" /> <Compile Include="StackExchange\Redis\ServerEndPoint.cs" />
<Compile Include="StackExchange\Redis\SetOperation.cs" /> <Compile Include="StackExchange\Redis\ServerSelectionStrategy.cs" />
<Compile Include="StackExchange\Redis\SocketManager.cs" /> <Compile Include="StackExchange\Redis\ServerType.cs" />
<Compile Include="StackExchange\Redis\SocketManager.NoPoll.cs"> <Compile Include="StackExchange\Redis\SetOperation.cs" />
<DependentUpon>SocketManager.cs</DependentUpon> <Compile Include="StackExchange\Redis\SocketManager.cs" />
</Compile> <Compile Include="StackExchange\Redis\SocketManager.NoPoll.cs">
<Compile Include="StackExchange\Redis\SortedSetEntry.cs" /> <DependentUpon>SocketManager.cs</DependentUpon>
<Compile Include="StackExchange\Redis\SortType.cs" /> </Compile>
<Compile Include="StackExchange\Redis\StringSplits.cs" /> <Compile Include="StackExchange\Redis\SortedSetEntry.cs" />
<Compile Include="StackExchange\Redis\TaskSource.cs" /> <Compile Include="StackExchange\Redis\SortType.cs" />
<Compile Include="StackExchange\Redis\When.cs" /> <Compile Include="StackExchange\Redis\StringSplits.cs" />
<Compile Include="StackExchange\Redis\ShutdownMode.cs" /> <Compile Include="StackExchange\Redis\TaskSource.cs" />
<Compile Include="StackExchange\Redis\SaveType.cs" /> <Compile Include="StackExchange\Redis\When.cs" />
<Compile Include="StackExchange\Redis\KeyspaceIsolation\BatchWrapper.cs" /> <Compile Include="StackExchange\Redis\ShutdownMode.cs" />
<Compile Include="StackExchange\Redis\KeyspaceIsolation\DatabaseExtension.cs" /> <Compile Include="StackExchange\Redis\SaveType.cs" />
<Compile Include="StackExchange\Redis\KeyspaceIsolation\DatabaseWrapper.cs" /> <Compile Include="StackExchange\Redis\KeyspaceIsolation\BatchWrapper.cs" />
<Compile Include="StackExchange\Redis\KeyspaceIsolation\TransactionWrapper.cs" /> <Compile Include="StackExchange\Redis\KeyspaceIsolation\DatabaseExtension.cs" />
<Compile Include="StackExchange\Redis\KeyspaceIsolation\WrapperBase.cs" /> <Compile Include="StackExchange\Redis\KeyspaceIsolation\DatabaseWrapper.cs" />
</ItemGroup> <Compile Include="StackExchange\Redis\KeyspaceIsolation\TransactionWrapper.cs" />
<ItemGroup> <Compile Include="StackExchange\Redis\KeyspaceIsolation\WrapperBase.cs" />
<Compile Include="StackExchange\Redis\SocketManager.Poll.cs"> </ItemGroup>
<DependentUpon>SocketManager.cs</DependentUpon> <ItemGroup>
</Compile> <Compile Include="StackExchange\Redis\SocketManager.Poll.cs">
</ItemGroup> <DependentUpon>SocketManager.cs</DependentUpon>
<ItemGroup> </Compile>
<None Include="StackExchange.Redis.snk" /> </ItemGroup>
</ItemGroup> <ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <None Include="StackExchange.Redis.snk" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. </ItemGroup>
Other similar extension points exist, see Microsoft.Common.targets. <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="BeforeBuild"> <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
</Target> Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="AfterBuild"> <Target Name="BeforeBuild">
</Target> </Target>
--> <Target Name="AfterBuild">
</Target>
-->
</Project> </Project>
\ No newline at end of file
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants> <DefineConstants>TRACE;DEBUG;FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE;FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Verbose|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Verbose|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Verbose\</OutputPath> <OutputPath>bin\Verbose\</OutputPath>
<DefineConstants>TRACE;DEBUG;VERBOSE</DefineConstants> <DefineConstants>TRACE;DEBUG;VERBOSE;FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Debug\StackExchange.Redis.xml</DocumentationFile> <DocumentationFile>bin\Debug\StackExchange.Redis.xml</DocumentationFile>
<DebugType>full</DebugType> <DebugType>full</DebugType>
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Log Output|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Log Output|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Log Output\</OutputPath> <OutputPath>bin\Log Output\</OutputPath>
<DefineConstants>TRACE;DEBUG;LOGOUTPUT</DefineConstants> <DefineConstants>TRACE;DEBUG;LOGOUTPUT FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Debug\StackExchange.Redis.xml</DocumentationFile> <DocumentationFile>bin\Debug\StackExchange.Redis.xml</DocumentationFile>
<DebugType>full</DebugType> <DebugType>full</DebugType>
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'">
<OutputPath>bin\Mono\</OutputPath> <OutputPath>bin\Mono\</OutputPath>
<DefineConstants>TRACE;__MonoCS__</DefineConstants> <DefineConstants>TRACE;__MonoCS__ FEATURE_SERIALIZATION</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Release\StackExchange.Redis.xml</DocumentationFile> <DocumentationFile>bin\Release\StackExchange.Redis.xml</DocumentationFile>
<Optimize>true</Optimize> <Optimize>true</Optimize>
...@@ -75,6 +75,7 @@ ...@@ -75,6 +75,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="StackExchange\Redis\Aggregate.cs" /> <Compile Include="StackExchange\Redis\Aggregate.cs" />
<Compile Include="StackExchange\Redis\ClientType.cs" /> <Compile Include="StackExchange\Redis\ClientType.cs" />
<Compile Include="StackExchange\Redis\Compat\VolatileWrapper.cs" />
<Compile Include="StackExchange\Redis\ConcurrentProfileStorageCollection.cs" /> <Compile Include="StackExchange\Redis\ConcurrentProfileStorageCollection.cs" />
<Compile Include="StackExchange\Redis\ConnectionMultiplexer.Profiling.cs"> <Compile Include="StackExchange\Redis\ConnectionMultiplexer.Profiling.cs">
<DependentUpon>ConnectionMultiplexer.cs</DependentUpon> <DependentUpon>ConnectionMultiplexer.cs</DependentUpon>
...@@ -83,6 +84,7 @@ ...@@ -83,6 +84,7 @@
<Compile Include="StackExchange\Redis\HashEntry.cs" /> <Compile Include="StackExchange\Redis\HashEntry.cs" />
<Compile Include="StackExchange\Redis\IConnectionMultiplexer.cs" /> <Compile Include="StackExchange\Redis\IConnectionMultiplexer.cs" />
<Compile Include="StackExchange\Redis\InternalErrorEventArgs.cs" /> <Compile Include="StackExchange\Redis\InternalErrorEventArgs.cs" />
<Compile Include="StackExchange\Redis\InternalRegexCompiledOption.cs" />
<Compile Include="StackExchange\Redis\IProfiler.cs" /> <Compile Include="StackExchange\Redis\IProfiler.cs" />
<Compile Include="StackExchange\Redis\MigrateOptions.cs" /> <Compile Include="StackExchange\Redis\MigrateOptions.cs" />
<Compile Include="StackExchange\Redis\ProfileContextTracker.cs" /> <Compile Include="StackExchange\Redis\ProfileContextTracker.cs" />
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
/// <summary> /// <summary>
/// Represents the commands mapped on a particular configuration /// Represents the commands mapped on a particular configuration
/// </summary> /// </summary>
public sealed class CommandMap public sealed class CommandMap
{ {
private static readonly CommandMap private static readonly CommandMap
@default = CreateImpl(null, null), @default = CreateImpl(null, null),
twemproxy = CreateImpl(null, exclusions: new HashSet<RedisCommand> twemproxy = CreateImpl(null, exclusions: new HashSet<RedisCommand>
{ {
// see https://github.com/twitter/twemproxy/blob/master/notes/redis.md // see https://github.com/twitter/twemproxy/blob/master/notes/redis.md
RedisCommand.KEYS, RedisCommand.MIGRATE, RedisCommand.MOVE, RedisCommand.OBJECT, RedisCommand.RANDOMKEY, RedisCommand.KEYS, RedisCommand.MIGRATE, RedisCommand.MOVE, RedisCommand.OBJECT, RedisCommand.RANDOMKEY,
RedisCommand.RENAME, RedisCommand.RENAMENX, RedisCommand.SORT, RedisCommand.SCAN, RedisCommand.RENAME, RedisCommand.RENAMENX, RedisCommand.SORT, RedisCommand.SCAN,
RedisCommand.BITOP, RedisCommand.MSET, RedisCommand.MSETNX, RedisCommand.BITOP, RedisCommand.MSET, RedisCommand.MSETNX,
RedisCommand.HSCAN, RedisCommand.HSCAN,
RedisCommand.BLPOP, RedisCommand.BRPOP, RedisCommand.BRPOPLPUSH, // yeah, me neither! RedisCommand.BLPOP, RedisCommand.BRPOP, RedisCommand.BRPOPLPUSH, // yeah, me neither!
RedisCommand.SSCAN, RedisCommand.SSCAN,
RedisCommand.ZSCAN, RedisCommand.ZSCAN,
RedisCommand.PSUBSCRIBE, RedisCommand.PUBLISH, RedisCommand.PUNSUBSCRIBE, RedisCommand.SUBSCRIBE, RedisCommand.UNSUBSCRIBE, RedisCommand.PSUBSCRIBE, RedisCommand.PUBLISH, RedisCommand.PUNSUBSCRIBE, RedisCommand.SUBSCRIBE, RedisCommand.UNSUBSCRIBE,
RedisCommand.DISCARD, RedisCommand.EXEC, RedisCommand.MULTI, RedisCommand.UNWATCH, RedisCommand.WATCH, RedisCommand.DISCARD, RedisCommand.EXEC, RedisCommand.MULTI, RedisCommand.UNWATCH, RedisCommand.WATCH,
RedisCommand.SCRIPT, RedisCommand.SCRIPT,
RedisCommand.AUTH, RedisCommand.ECHO, RedisCommand.PING, RedisCommand.QUIT, RedisCommand.SELECT, RedisCommand.AUTH, RedisCommand.ECHO, RedisCommand.PING, RedisCommand.QUIT, RedisCommand.SELECT,
RedisCommand.BGREWRITEAOF, RedisCommand.BGSAVE, RedisCommand.CLIENT, RedisCommand.CLUSTER, RedisCommand.CONFIG, RedisCommand.DBSIZE, RedisCommand.BGREWRITEAOF, RedisCommand.BGSAVE, RedisCommand.CLIENT, RedisCommand.CLUSTER, RedisCommand.CONFIG, RedisCommand.DBSIZE,
RedisCommand.DEBUG, RedisCommand.FLUSHALL, RedisCommand.FLUSHDB, RedisCommand.INFO, RedisCommand.LASTSAVE, RedisCommand.MONITOR, RedisCommand.SAVE, RedisCommand.DEBUG, RedisCommand.FLUSHALL, RedisCommand.FLUSHDB, RedisCommand.INFO, RedisCommand.LASTSAVE, RedisCommand.MONITOR, RedisCommand.SAVE,
RedisCommand.SHUTDOWN, RedisCommand.SLAVEOF, RedisCommand.SLOWLOG, RedisCommand.SYNC, RedisCommand.TIME RedisCommand.SHUTDOWN, RedisCommand.SLAVEOF, RedisCommand.SLOWLOG, RedisCommand.SYNC, RedisCommand.TIME
}), ssdb = Create(new HashSet<string> { }), ssdb = Create(new HashSet<string> {
// see http://www.ideawu.com/ssdb/docs/redis-to-ssdb.html // see http://www.ideawu.com/ssdb/docs/redis-to-ssdb.html
"ping", "ping",
"get", "set", "del", "incr", "incrby", "mget", "mset", "keys", "getset", "setnx", "get", "set", "del", "incr", "incrby", "mget", "mset", "keys", "getset", "setnx",
"hget", "hset", "hdel", "hincrby", "hkeys", "hvals", "hmget", "hmset", "hlen", "hget", "hset", "hdel", "hincrby", "hkeys", "hvals", "hmget", "hmset", "hlen",
"zscore", "zadd", "zrem", "zrange", "zrangebyscore", "zincrby", "zdecrby", "zcard", "zscore", "zadd", "zrem", "zrange", "zrangebyscore", "zincrby", "zdecrby", "zcard",
"llen", "lpush", "rpush", "lpop", "rpop", "lrange", "lindex" "llen", "lpush", "rpush", "lpop", "rpop", "lrange", "lindex"
}, true), }, true),
sentinel = Create(new HashSet<string> { sentinel = Create(new HashSet<string> {
// see http://redis.io/topics/sentinel // see http://redis.io/topics/sentinel
"ping", "info", "sentinel", "subscribe", "psubscribe", "unsubscribe", "punsubscribe" }, true); "ping", "info", "sentinel", "subscribe", "psubscribe", "unsubscribe", "punsubscribe" }, true);
private readonly byte[][] map; private readonly byte[][] map;
internal CommandMap(byte[][] map) internal CommandMap(byte[][] map)
{ {
this.map = map; this.map = map;
} }
/// <summary> /// <summary>
/// The default commands specified by redis /// The default commands specified by redis
/// </summary> /// </summary>
public static CommandMap Default { get { return @default; } } public static CommandMap Default { get { return @default; } }
/// <summary> /// <summary>
/// The commands available to <a href="twemproxy">https://github.com/twitter/twemproxy</a> /// The commands available to <a href="twemproxy">https://github.com/twitter/twemproxy</a>
/// </summary> /// </summary>
/// <remarks>https://github.com/twitter/twemproxy/blob/master/notes/redis.md</remarks> /// <remarks>https://github.com/twitter/twemproxy/blob/master/notes/redis.md</remarks>
public static CommandMap Twemproxy { get { return twemproxy; } } public static CommandMap Twemproxy { get { return twemproxy; } }
/// <summary> /// <summary>
/// The commands available to <a href="ssdb">http://www.ideawu.com/ssdb/</a> /// The commands available to <a href="ssdb">http://www.ideawu.com/ssdb/</a>
/// </summary> /// </summary>
/// <remarks>http://www.ideawu.com/ssdb/docs/redis-to-ssdb.html</remarks> /// <remarks>http://www.ideawu.com/ssdb/docs/redis-to-ssdb.html</remarks>
public static CommandMap SSDB { get { return ssdb; } } public static CommandMap SSDB { get { return ssdb; } }
/// <summary> /// <summary>
/// The commands available to <a href="Sentinel">http://redis.io/topics/sentinel</a> /// The commands available to <a href="Sentinel">http://redis.io/topics/sentinel</a>
/// </summary> /// </summary>
/// <remarks>http://redis.io/topics/sentinel</remarks> /// <remarks>http://redis.io/topics/sentinel</remarks>
public static CommandMap Sentinel { get { return sentinel; } } public static CommandMap Sentinel { get { return sentinel; } }
/// <summary> /// <summary>
/// Create a new CommandMap, customizing some commands /// Create a new CommandMap, customizing some commands
/// </summary> /// </summary>
public static CommandMap Create(Dictionary<string, string> overrides) public static CommandMap Create(Dictionary<string, string> overrides)
{ {
if (overrides == null || overrides.Count == 0) return Default; if (overrides == null || overrides.Count == 0) return Default;
if (ReferenceEquals(overrides.Comparer, StringComparer.OrdinalIgnoreCase) || if (ReferenceEquals(overrides.Comparer, StringComparer.OrdinalIgnoreCase))
ReferenceEquals(overrides.Comparer, StringComparer.InvariantCultureIgnoreCase)) {
{ // that's ok; we're happy with ordinal/invariant case-insensitive
// that's ok; we're happy with ordinal/invariant case-insensitive // (but not culture-specific insensitive; completely untested)
// (but not culture-specific insensitive; completely untested) }
} else
else {
{ // need case insensitive
// need case insensitive overrides = new Dictionary<string, string>(overrides, StringComparer.OrdinalIgnoreCase);
overrides = new Dictionary<string, string>(overrides, StringComparer.OrdinalIgnoreCase); }
} return CreateImpl(overrides, null);
return CreateImpl(overrides, null); }
}
/// <summary>
/// <summary> /// Creates a CommandMap by specifying which commands are available or unavailable
/// Creates a CommandMap by specifying which commands are available or unavailable /// </summary>
/// </summary> public static CommandMap Create(HashSet<string> commands, bool available = true)
public static CommandMap Create(HashSet<string> commands, bool available = true) {
{
if (available)
if (available) {
{ var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // nix everything
// nix everything foreach (RedisCommand command in Enum.GetValues(typeof(RedisCommand)))
foreach (RedisCommand command in Enum.GetValues(typeof(RedisCommand))) {
{ dictionary[command.ToString()] = null;
dictionary[command.ToString()] = null; }
} if (commands != null)
if (commands != null) {
{ // then include (by removal) the things that are available
// then include (by removal) the things that are available foreach (string command in commands)
foreach (string command in commands) {
{ dictionary.Remove(command);
dictionary.Remove(command); }
} }
} return CreateImpl(dictionary, null);
return CreateImpl(dictionary, null); }
} else
else {
{ HashSet<RedisCommand> exclusions = null;
HashSet<RedisCommand> exclusions = null; if (commands != null)
if (commands != null) {
{ // nix the things that are specified
// nix the things that are specified foreach (var command in commands)
foreach (var command in commands) {
{ RedisCommand parsed;
RedisCommand parsed; if (Enum.TryParse(command, true, out parsed))
if (Enum.TryParse(command, true, out parsed)) {
{ (exclusions ?? (exclusions = new HashSet<RedisCommand>())).Add(parsed);
(exclusions ?? (exclusions = new HashSet<RedisCommand>())).Add(parsed); }
} }
} }
} if (exclusions == null || exclusions.Count == 0) return Default;
if (exclusions == null || exclusions.Count == 0) return Default; return CreateImpl(null, exclusions);
return CreateImpl(null, exclusions); }
}
}
}
/// <summary>
/// <summary> /// See Object.ToString()
/// See Object.ToString() /// </summary>
/// </summary> public override string ToString()
public override string ToString() {
{ var sb = new StringBuilder();
var sb = new StringBuilder(); AppendDeltas(sb);
AppendDeltas(sb); return sb.ToString();
return sb.ToString(); }
}
internal void AppendDeltas(StringBuilder sb)
internal void AppendDeltas(StringBuilder sb) {
{ for (int i = 0; i < map.Length; i++)
for (int i = 0; i < map.Length; i++) {
{ var key = ((RedisCommand)i).ToString();
var key = ((RedisCommand)i).ToString(); var value = map[i] == null ? "" : Encoding.UTF8.GetString(map[i]);
var value = map[i] == null ? "" : Encoding.UTF8.GetString(map[i]); if (key != value)
if (key != value) {
{ if (sb.Length != 0) sb.Append(',');
if (sb.Length != 0) sb.Append(','); sb.Append('$').Append(key).Append('=').Append(value);
sb.Append('$').Append(key).Append('=').Append(value); }
} }
} }
}
internal void AssertAvailable(RedisCommand command)
internal void AssertAvailable(RedisCommand command) {
{ if (map[(int)command] == null) throw ExceptionFactory.CommandDisabled(false, command, null, null);
if (map[(int)command] == null) throw ExceptionFactory.CommandDisabled(false, command, null, null); }
}
internal byte[] GetBytes(RedisCommand command)
internal byte[] GetBytes(RedisCommand command) {
{ return map[(int)command];
return map[(int)command]; }
}
internal bool IsAvailable(RedisCommand command)
internal bool IsAvailable(RedisCommand command) {
{ return map[(int)command] != null;
return map[(int)command] != null; }
}
private static CommandMap CreateImpl(Dictionary<string, string> caseInsensitiveOverrides, HashSet<RedisCommand> exclusions)
private static CommandMap CreateImpl(Dictionary<string, string> caseInsensitiveOverrides, HashSet<RedisCommand> exclusions) {
{ var commands = (RedisCommand[])Enum.GetValues(typeof(RedisCommand));
var commands = (RedisCommand[])Enum.GetValues(typeof(RedisCommand));
byte[][] map = new byte[commands.Length][];
byte[][] map = new byte[commands.Length][]; bool haveDelta = false;
bool haveDelta = false; for (int i = 0; i < commands.Length; i++)
for (int i = 0; i < commands.Length; i++) {
{ int idx = (int)commands[i];
int idx = (int)commands[i]; string name = commands[i].ToString(), value = name;
string name = commands[i].ToString(), value = name;
if (exclusions != null && exclusions.Contains(commands[i]))
if (exclusions != null && exclusions.Contains(commands[i])) {
{ map[idx] = null;
map[idx] = null; }
} else
else {
{ if (caseInsensitiveOverrides != null)
if (caseInsensitiveOverrides != null) {
{ string tmp;
string tmp; if (caseInsensitiveOverrides.TryGetValue(name, out tmp))
if (caseInsensitiveOverrides.TryGetValue(name, out tmp)) {
{ value = tmp;
value = tmp; }
} }
} if (value != name) haveDelta = true;
if (value != name) haveDelta = true;
haveDelta = true;
haveDelta = true; byte[] val = string.IsNullOrWhiteSpace(value) ? null : Encoding.UTF8.GetBytes(value);
byte[] val = string.IsNullOrWhiteSpace(value) ? null : Encoding.UTF8.GetBytes(value); map[idx] = val;
map[idx] = val; }
} }
} if (!haveDelta && @default != null) return @default;
if (!haveDelta && @default != null) return @default;
return new CommandMap(map);
return new CommandMap(map); }
} }
} }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackExchange.Redis
{
internal static class VolatileWrapper
{
public static int Read(ref int location)
{
#if !DNXCORE50
return System.Threading.Thread.VolatileRead(ref location);
#else
return System.Threading.Volatile.Read(ref location);
#endif
}
public static void Write(ref int address, int value)
{
#if !DNXCORE50
System.Threading.Thread.VolatileWrite(ref address, value);
#else
System.Threading.Volatile.Write(ref address, value);
#endif
}
}
}
...@@ -145,7 +145,7 @@ private void ProcessAsyncCompletionQueueImpl() ...@@ -145,7 +145,7 @@ private void ProcessAsyncCompletionQueueImpl()
// give it a moment and try again, noting that we might lose the battle // give it a moment and try again, noting that we might lose the battle
// when we pause // when we pause
Interlocked.CompareExchange(ref activeAsyncWorkerThread, 0, currentThread); Interlocked.CompareExchange(ref activeAsyncWorkerThread, 0, currentThread);
if (Thread.Yield() && Interlocked.CompareExchange(ref activeAsyncWorkerThread, currentThread, 0) == 0) if (SpinWait() && Interlocked.CompareExchange(ref activeAsyncWorkerThread, currentThread, 0) == 0)
{ {
// we paused, and we got the lock back; anything else? // we paused, and we got the lock back; anything else?
lock (asyncCompletionQueue) lock (asyncCompletionQueue)
...@@ -176,5 +176,20 @@ private void ProcessAsyncCompletionQueueImpl() ...@@ -176,5 +176,20 @@ private void ProcessAsyncCompletionQueueImpl()
Interlocked.CompareExchange(ref activeAsyncWorkerThread, 0, currentThread); Interlocked.CompareExchange(ref activeAsyncWorkerThread, 0, currentThread);
} }
} }
private bool SpinWait()
{
var sw = new SpinWait();
byte maxSpins = 128;
do
{
if (sw.NextSpinWillYield)
return true;
maxSpins--;
}
while (maxSpins > 0);
return false;
}
} }
} }
...@@ -29,7 +29,10 @@ public enum Proxy ...@@ -29,7 +29,10 @@ public enum Proxy
/// <summary> /// <summary>
/// The options relevant to a set of redis connections /// The options relevant to a set of redis connections
/// </summary> /// </summary>
public sealed class ConfigurationOptions : ICloneable public sealed class ConfigurationOptions
#if !DNXCORE50
: ICloneable
#endif
{ {
internal const string DefaultTieBreaker = "__Booksleeve_TieBreak", DefaultConfigurationChannel = "__Booksleeve_MasterChanged"; internal const string DefaultTieBreaker = "__Booksleeve_TieBreak", DefaultConfigurationChannel = "__Booksleeve_MasterChanged";
...@@ -84,7 +87,7 @@ internal static void Unknown(string key) ...@@ -84,7 +87,7 @@ internal static void Unknown(string key)
ConfigChannel, AbortOnConnectFail, ResolveDns, ConfigChannel, AbortOnConnectFail, ResolveDns,
ChannelPrefix, Proxy, ConnectRetry, ChannelPrefix, Proxy, ConnectRetry,
ConfigCheckSeconds, DefaultDatabase, ConfigCheckSeconds, DefaultDatabase,
}.ToDictionary(x => x, StringComparer.InvariantCultureIgnoreCase); }.ToDictionary(x => x, StringComparer.OrdinalIgnoreCase);
public static string TryNormalize(string value) public static string TryNormalize(string value)
{ {
...@@ -139,7 +142,11 @@ public static string TryNormalize(string value) ...@@ -139,7 +142,11 @@ public static string TryNormalize(string value)
/// <summary> /// <summary>
/// Indicates whether the connection should be encrypted /// Indicates whether the connection should be encrypted
/// </summary> /// </summary>
[Obsolete("Please use .Ssl instead of .UseSsl"), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Please use .Ssl instead of .UseSsl"),
#if !DNXCORE50
Browsable(false),
#endif
EditorBrowsable(EditorBrowsableState.Never)]
public bool UseSsl { get { return Ssl; } set { Ssl = value; } } public bool UseSsl { get { return Ssl; } set { Ssl = value; } }
/// <summary> /// <summary>
...@@ -248,7 +255,7 @@ public CommandMap CommandMap ...@@ -248,7 +255,7 @@ public CommandMap CommandMap
/// <summary> /// <summary>
/// Specifies the time in milliseconds that the system should allow for synchronous operations (defaults to 1 second) /// Specifies the time in milliseconds that the system should allow for synchronous operations (defaults to 1 second)
/// </summary> /// </summary>
public int SyncTimeout { get { return syncTimeout.GetValueOrDefault(1000); } set { syncTimeout = value; } } public int SyncTimeout { get { return syncTimeout.GetValueOrDefault(20000); } set { syncTimeout = value; } }
/// <summary> /// <summary>
/// Specifies the time in milliseconds that the system should allow for responses before concluding that the socket is unhealthy /// Specifies the time in milliseconds that the system should allow for responses before concluding that the socket is unhealthy
...@@ -394,7 +401,7 @@ internal bool HasDnsEndPoints() ...@@ -394,7 +401,7 @@ internal bool HasDnsEndPoints()
#pragma warning disable 1998 // NET40 is sync, not async, currently #pragma warning disable 1998 // NET40 is sync, not async, currently
internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, TextWriter log) internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, TextWriter log)
{ {
Dictionary<string, IPAddress> cache = new Dictionary<string, IPAddress>(StringComparer.InvariantCultureIgnoreCase); Dictionary<string, IPAddress> cache = new Dictionary<string, IPAddress>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < endpoints.Count; i++) for (int i = 0; i < endpoints.Count; i++)
{ {
var dns = endpoints[i] as DnsEndPoint; var dns = endpoints[i] as DnsEndPoint;
...@@ -463,10 +470,13 @@ static void Append(StringBuilder sb, string prefix, object value) ...@@ -463,10 +470,13 @@ static void Append(StringBuilder sb, string prefix, object value)
} }
} }
#if !DNXCORE50
static bool IsOption(string option, string prefix) static bool IsOption(string option, string prefix)
{ {
return option.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase); return option.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase);
} }
#endif
void Clear() void Clear()
{ {
clientName = serviceName = password = tieBreaker = sslHost = configChannel = null; clientName = serviceName = password = tieBreaker = sslHost = configChannel = null;
...@@ -482,7 +492,9 @@ void Clear() ...@@ -482,7 +492,9 @@ void Clear()
SocketManager = null; SocketManager = null;
} }
#if !DNXCORE50
object ICloneable.Clone() { return Clone(); } object ICloneable.Clone() { return Clone(); }
#endif
private void DoParse(string configuration, bool ignoreUnknown) private void DoParse(string configuration, bool ignoreUnknown)
{ {
...@@ -586,7 +598,7 @@ private void DoParse(string configuration, bool ignoreUnknown) ...@@ -586,7 +598,7 @@ private void DoParse(string configuration, bool ignoreUnknown)
var cmdName = option.Substring(1, idx - 1); var cmdName = option.Substring(1, idx - 1);
if (Enum.TryParse(cmdName, true, out cmd)) if (Enum.TryParse(cmdName, true, out cmd))
{ {
if (map == null) map = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); if (map == null) map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
map[cmdName] = value; map[cmdName] = value;
} }
} }
......
...@@ -95,7 +95,7 @@ public ServerCounters GetCounters() ...@@ -95,7 +95,7 @@ public ServerCounters GetCounters()
/// <summary> /// <summary>
/// Gets the client-name that will be used on all new connections /// Gets the client-name that will be used on all new connections
/// </summary> /// </summary>
public string ClientName { get { return configuration.ClientName ?? Environment.MachineName; } } public string ClientName { get { return configuration.ClientName ?? Environment.GetEnvironmentVariable("ComputerName"); } }
/// <summary> /// <summary>
/// Gets the configuration of the connection /// Gets the configuration of the connection
...@@ -482,7 +482,7 @@ public EndPoint[] GetEndPoints(bool configuredOnly = false) ...@@ -482,7 +482,7 @@ public EndPoint[] GetEndPoints(bool configuredOnly = false)
{ {
if (configuredOnly) return configuration.EndPoints.ToArray(); if (configuredOnly) return configuration.EndPoints.ToArray();
return Array.ConvertAll(serverSnapshot, x => x.EndPoint); return serverSnapshot.Select(x => x.EndPoint).ToArray();
} }
private readonly int timeoutMilliseconds; private readonly int timeoutMilliseconds;
...@@ -560,6 +560,8 @@ private static bool WaitAllIgnoreErrors(Task[] tasks, int timeout) ...@@ -560,6 +560,8 @@ private static bool WaitAllIgnoreErrors(Task[] tasks, int timeout)
} }
return false; return false;
} }
#if !DNXCORE50
private void LogLockedWithThreadPoolStats(TextWriter log, string message, out int busyWorkerCount) private void LogLockedWithThreadPoolStats(TextWriter log, string message, out int busyWorkerCount)
{ {
busyWorkerCount = 0; busyWorkerCount = 0;
...@@ -573,6 +575,8 @@ private void LogLockedWithThreadPoolStats(TextWriter log, string message, out in ...@@ -573,6 +575,8 @@ private void LogLockedWithThreadPoolStats(TextWriter log, string message, out in
LogLocked(log, sb.ToString()); LogLocked(log, sb.ToString());
} }
} }
#endif
static bool AllComplete(Task[] tasks) static bool AllComplete(Task[] tasks)
{ {
for(int i = 0 ; i < tasks.Length ; i++) for(int i = 0 ; i < tasks.Length ; i++)
...@@ -599,16 +603,19 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli ...@@ -599,16 +603,19 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli
} }
var watch = Stopwatch.StartNew(); var watch = Stopwatch.StartNew();
#if !DNXCORE50
int busyWorkerCount; int busyWorkerCount;
LogLockedWithThreadPoolStats(log, "Awaiting task completion", out busyWorkerCount); LogLockedWithThreadPoolStats(log, "Awaiting task completion", out busyWorkerCount);
#endif
try try
{ {
// if none error, great // if none error, great
var remaining = timeoutMilliseconds - checked((int)watch.ElapsedMilliseconds); var remaining = timeoutMilliseconds - checked((int)watch.ElapsedMilliseconds);
if (remaining <= 0) if (remaining <= 0)
{ {
#if !DNXCORE50
LogLockedWithThreadPoolStats(log, "Timeout before awaiting for tasks", out busyWorkerCount); LogLockedWithThreadPoolStats(log, "Timeout before awaiting for tasks", out busyWorkerCount);
#endif
return false; return false;
} }
...@@ -620,7 +627,9 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli ...@@ -620,7 +627,9 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli
var any = Task.WhenAny(allTasks, Task.Delay(remaining)).ObserveErrors(); var any = Task.WhenAny(allTasks, Task.Delay(remaining)).ObserveErrors();
#endif #endif
bool all = await any.ForAwait() == allTasks; bool all = await any.ForAwait() == allTasks;
#if !DNXCORE50
LogLockedWithThreadPoolStats(log, all ? "All tasks completed cleanly" : "Not all tasks completed cleanly", out busyWorkerCount); LogLockedWithThreadPoolStats(log, all ? "All tasks completed cleanly" : "Not all tasks completed cleanly", out busyWorkerCount);
#endif
return all; return all;
} }
catch catch
...@@ -636,7 +645,9 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli ...@@ -636,7 +645,9 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli
var remaining = timeoutMilliseconds - checked((int)watch.ElapsedMilliseconds); var remaining = timeoutMilliseconds - checked((int)watch.ElapsedMilliseconds);
if (remaining <= 0) if (remaining <= 0)
{ {
#if !DNXCORE50
LogLockedWithThreadPoolStats(log, "Timeout awaiting tasks", out busyWorkerCount); LogLockedWithThreadPoolStats(log, "Timeout awaiting tasks", out busyWorkerCount);
#endif
return false; return false;
} }
try try
...@@ -652,7 +663,9 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli ...@@ -652,7 +663,9 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli
{ } { }
} }
} }
#if !DNXCORE50
LogLockedWithThreadPoolStats(log, "Finished awaiting tasks", out busyWorkerCount); LogLockedWithThreadPoolStats(log, "Finished awaiting tasks", out busyWorkerCount);
#endif
return false; return false;
} }
...@@ -927,12 +940,12 @@ private void OnHeartbeat() ...@@ -927,12 +940,12 @@ private void OnHeartbeat()
private static int lastGlobalHeartbeatTicks = Environment.TickCount; private static int lastGlobalHeartbeatTicks = Environment.TickCount;
internal long LastHeartbeatSecondsAgo { internal long LastHeartbeatSecondsAgo {
get { get {
if (pulse == null) return -1; if (pulse == null) return -1;
return unchecked(Environment.TickCount - Thread.VolatileRead(ref lastHeartbeatTicks)) / 1000; return unchecked(Environment.TickCount - VolatileWrapper.Read(ref lastHeartbeatTicks)) / 1000;
} }
} }
internal static long LastGlobalHeartbeatSecondsAgo internal static long LastGlobalHeartbeatSecondsAgo
{ get { return unchecked(Environment.TickCount - Thread.VolatileRead(ref lastGlobalHeartbeatTicks)) / 1000; } } { get { return unchecked(Environment.TickCount - VolatileWrapper.Read(ref lastGlobalHeartbeatTicks)) / 1000; } }
internal CompletionManager UnprocessableCompletionManager { get { return unprocessableCompletionManager; } } internal CompletionManager UnprocessableCompletionManager { get { return unprocessableCompletionManager; } }
...@@ -1434,7 +1447,7 @@ private async Task<ServerEndPoint> NominatePreferredMaster(TextWriter log, Serve ...@@ -1434,7 +1447,7 @@ private async Task<ServerEndPoint> NominatePreferredMaster(TextWriter log, Serve
Dictionary<string, int> uniques = null; Dictionary<string, int> uniques = null;
if (useTieBreakers) if (useTieBreakers)
{ // count the votes { // count the votes
uniques = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase); uniques = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
await WaitAllIgnoreErrorsAsync(tieBreakers, 50, log).ForAwait(); await WaitAllIgnoreErrorsAsync(tieBreakers, 50, log).ForAwait();
for (int i = 0; i < tieBreakers.Length; i++) for (int i = 0; i < tieBreakers.Length; i++)
{ {
...@@ -1874,8 +1887,7 @@ internal T ExecuteSyncImpl<T>(Message message, ResultProcessor<T> processor, Ser ...@@ -1874,8 +1887,7 @@ internal T ExecuteSyncImpl<T>(Message message, ResultProcessor<T> processor, Ser
else else
{ {
int inst, qu, qs, qc, wr, wq, @in, ar; int inst, qu, qs, qc, wr, wq, @in, ar;
string iocp, worker; #if FEATURE_SOCKET_MODE_POLL
#if !__MonoCS__
var mgrState = socketManager.State; var mgrState = socketManager.State;
var lastError = socketManager.LastErrorTimeRelative(); var lastError = socketManager.LastErrorTimeRelative();
...@@ -1889,9 +1901,8 @@ internal T ExecuteSyncImpl<T>(Message message, ResultProcessor<T> processor, Ser ...@@ -1889,9 +1901,8 @@ internal T ExecuteSyncImpl<T>(Message message, ResultProcessor<T> processor, Ser
}; };
int queue = server.GetOutstandingCount(message.Command, out inst, out qu, out qs, out qc, out wr, out wq, out @in, out ar); int queue = server.GetOutstandingCount(message.Command, out inst, out qu, out qs, out qc, out wr, out wq, out @in, out ar);
int busyWorkerCount = GetThreadPoolStats(out iocp, out worker);
add("Instantaneous", "inst", inst.ToString()); add("Instantaneous", "inst", inst.ToString());
#if !__MonoCS__ #if FEATURE_SOCKET_MODE_POLL
add("Manager-State", "mgr", mgrState.ToString()); add("Manager-State", "mgr", mgrState.ToString());
add("Last-Error", "err", lastError); add("Last-Error", "err", lastError);
#endif #endif
...@@ -1904,10 +1915,14 @@ internal T ExecuteSyncImpl<T>(Message message, ResultProcessor<T> processor, Ser ...@@ -1904,10 +1915,14 @@ internal T ExecuteSyncImpl<T>(Message message, ResultProcessor<T> processor, Ser
add("Inbound-Bytes", "in", @in.ToString()); add("Inbound-Bytes", "in", @in.ToString());
add("Active-Readers", "ar", ar.ToString()); add("Active-Readers", "ar", ar.ToString());
add("Client-Name", "clientName", ClientName);
#if !DNXCORE50
string iocp, worker;
int busyWorkerCount = GetThreadPoolStats(out iocp, out worker);
add("ThreadPool-IO-Completion", "IOCP", iocp); add("ThreadPool-IO-Completion", "IOCP", iocp);
add("ThreadPool-Workers", "WORKER", worker); add("ThreadPool-Workers", "WORKER", worker);
add("Client-Name", "clientName", ClientName);
data.Add(Tuple.Create("Busy-Workers", busyWorkerCount.ToString())); data.Add(Tuple.Create("Busy-Workers", busyWorkerCount.ToString()));
#endif
errMessage = sb.ToString(); errMessage = sb.ToString();
if (stormLogThreshold >= 0 && queue >= stormLogThreshold && Interlocked.CompareExchange(ref haveStormLog, 1, 0) == 0) if (stormLogThreshold >= 0 && queue >= stormLogThreshold && Interlocked.CompareExchange(ref haveStormLog, 1, 0) == 0)
{ {
...@@ -1936,7 +1951,9 @@ internal T ExecuteSyncImpl<T>(Message message, ResultProcessor<T> processor, Ser ...@@ -1936,7 +1951,9 @@ internal T ExecuteSyncImpl<T>(Message message, ResultProcessor<T> processor, Ser
Trace(message + " received " + val); Trace(message + " received " + val);
return val; return val;
} }
} }
#if !DNXCORE50
private static int GetThreadPoolStats(out string iocp, out string worker) private static int GetThreadPoolStats(out string iocp, out string worker)
{ {
//BusyThreads = TP.GetMaxThreads() –TP.GetAVailable(); //BusyThreads = TP.GetMaxThreads() –TP.GetAVailable();
...@@ -1958,6 +1975,7 @@ private static int GetThreadPoolStats(out string iocp, out string worker) ...@@ -1958,6 +1975,7 @@ private static int GetThreadPoolStats(out string iocp, out string worker)
worker = string.Format("(Busy={0},Free={1},Min={2},Max={3})", busyWorkerThreads, freeWorkerThreads, minWorkerThreads, maxWorkerThreads); worker = string.Format("(Busy={0},Free={1},Min={2},Max={3})", busyWorkerThreads, freeWorkerThreads, minWorkerThreads, maxWorkerThreads);
return busyWorkerThreads; return busyWorkerThreads;
} }
#endif
/// <summary> /// <summary>
/// Should exceptions include identifiable details? (key names, additional .Data annotations) /// Should exceptions include identifiable details? (key names, additional .Data annotations)
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace StackExchange.Redis
{ namespace StackExchange.Redis
/// <summary> {
/// Utility methods /// <summary>
/// </summary> /// Utility methods
public static class ExtensionMethods /// </summary>
{ public static class ExtensionMethods
/// <summary> {
/// Create a dictionary from an array of HashEntry values /// <summary>
/// </summary> /// Create a dictionary from an array of HashEntry values
public static Dictionary<string,string> ToStringDictionary(this HashEntry[] hash) /// </summary>
{ public static Dictionary<string,string> ToStringDictionary(this HashEntry[] hash)
if (hash == null) return null; {
if (hash == null) return null;
var result = new Dictionary<string, string>(hash.Length, StringComparer.Ordinal);
for(int i = 0; i < hash.Length; i++) var result = new Dictionary<string, string>(hash.Length, StringComparer.Ordinal);
{ for(int i = 0; i < hash.Length; i++)
result.Add(hash[i].name, hash[i].value); {
} result.Add(hash[i].name, hash[i].value);
return result; }
} return result;
/// <summary> }
/// Create a dictionary from an array of HashEntry values /// <summary>
/// </summary> /// Create a dictionary from an array of HashEntry values
public static Dictionary<RedisValue, RedisValue> ToDictionary(this HashEntry[] hash) /// </summary>
{ public static Dictionary<RedisValue, RedisValue> ToDictionary(this HashEntry[] hash)
if (hash == null) return null; {
if (hash == null) return null;
var result = new Dictionary<RedisValue, RedisValue>(hash.Length);
for (int i = 0; i < hash.Length; i++) var result = new Dictionary<RedisValue, RedisValue>(hash.Length);
{ for (int i = 0; i < hash.Length; i++)
result.Add(hash[i].name, hash[i].value); {
} result.Add(hash[i].name, hash[i].value);
return result; }
} return result;
}
/// <summary>
/// Create a dictionary from an array of SortedSetEntry values /// <summary>
/// </summary> /// Create a dictionary from an array of SortedSetEntry values
public static Dictionary<string, double> ToStringDictionary(this SortedSetEntry[] sortedSet) /// </summary>
{ public static Dictionary<string, double> ToStringDictionary(this SortedSetEntry[] sortedSet)
if (sortedSet == null) return null; {
if (sortedSet == null) return null;
var result = new Dictionary<string, double>(sortedSet.Length, StringComparer.Ordinal);
for (int i = 0; i < sortedSet.Length; i++) var result = new Dictionary<string, double>(sortedSet.Length, StringComparer.Ordinal);
{ for (int i = 0; i < sortedSet.Length; i++)
result.Add(sortedSet[i].element, sortedSet[i].score); {
} result.Add(sortedSet[i].element, sortedSet[i].score);
return result; }
} return result;
}
/// <summary>
/// Create a dictionary from an array of SortedSetEntry values /// <summary>
/// </summary> /// Create a dictionary from an array of SortedSetEntry values
public static Dictionary<RedisValue, double> ToDictionary(this SortedSetEntry[] sortedSet) /// </summary>
{ public static Dictionary<RedisValue, double> ToDictionary(this SortedSetEntry[] sortedSet)
if (sortedSet == null) return null; {
if (sortedSet == null) return null;
var result = new Dictionary<RedisValue, double>(sortedSet.Length);
for (int i = 0; i < sortedSet.Length; i++) var result = new Dictionary<RedisValue, double>(sortedSet.Length);
{ for (int i = 0; i < sortedSet.Length; i++)
result.Add(sortedSet[i].element, sortedSet[i].score); {
} result.Add(sortedSet[i].element, sortedSet[i].score);
return result; }
} return result;
}
/// <summary>
/// Create a dictionary from an array of key/value pairs /// <summary>
/// </summary> /// Create a dictionary from an array of key/value pairs
public static Dictionary<string, string> ToStringDictionary(this KeyValuePair<RedisKey, RedisValue>[] pairs) /// </summary>
{ public static Dictionary<string, string> ToStringDictionary(this KeyValuePair<RedisKey, RedisValue>[] pairs)
if (pairs == null) return null; {
if (pairs == null) return null;
var result = new Dictionary<string, string>(pairs.Length, StringComparer.Ordinal);
for (int i = 0; i < pairs.Length; i++) var result = new Dictionary<string, string>(pairs.Length, StringComparer.Ordinal);
{ for (int i = 0; i < pairs.Length; i++)
result.Add(pairs[i].Key, pairs[i].Value); {
} result.Add(pairs[i].Key, pairs[i].Value);
return result; }
} return result;
}
/// <summary>
/// Create a dictionary from an array of key/value pairs /// <summary>
/// </summary> /// Create a dictionary from an array of key/value pairs
public static Dictionary<RedisKey, RedisValue> ToDictionary(this KeyValuePair<RedisKey, RedisValue>[] pairs) /// </summary>
{ public static Dictionary<RedisKey, RedisValue> ToDictionary(this KeyValuePair<RedisKey, RedisValue>[] pairs)
if (pairs == null) return null; {
if (pairs == null) return null;
var result = new Dictionary<RedisKey, RedisValue>(pairs.Length);
for (int i = 0; i < pairs.Length; i++) var result = new Dictionary<RedisKey, RedisValue>(pairs.Length);
{ for (int i = 0; i < pairs.Length; i++)
result.Add(pairs[i].Key, pairs[i].Value); {
} result.Add(pairs[i].Key, pairs[i].Value);
return result; }
} return result;
}
/// <summary>
/// Create a dictionary from an array of string pairs /// <summary>
/// </summary> /// Create a dictionary from an array of string pairs
public static Dictionary<string, string> ToDictionary(this KeyValuePair<string, string>[] pairs) /// </summary>
{ public static Dictionary<string, string> ToDictionary(this KeyValuePair<string, string>[] pairs)
if (pairs == null) return null; {
if (pairs == null) return null;
var result = new Dictionary<string, string>(pairs.Length, StringComparer.Ordinal);
for (int i = 0; i < pairs.Length; i++) var result = new Dictionary<string, string>(pairs.Length, StringComparer.Ordinal);
{ for (int i = 0; i < pairs.Length; i++)
result.Add(pairs[i].Key, pairs[i].Value); {
} result.Add(pairs[i].Key, pairs[i].Value);
return result; }
} return result;
}
static readonly string[] nix = new string[0];
/// <summary> static readonly string[] nix = new string[0];
/// Create an array of strings from an array of values /// <summary>
/// </summary> /// Create an array of strings from an array of values
public static string[] ToStringArray(this RedisValue[] values) /// </summary>
{ public static string[] ToStringArray(this RedisValue[] values)
if (values == null) return null; {
if (values.Length == 0) return nix; if (values == null) return null;
return Array.ConvertAll(values, x => (string)x); if (values.Length == 0) return nix;
} return values.Select(x => (string)x).ToArray();
} }
} }
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
/// <summary> /// <summary>
/// Describes a hash-field (a name/value pair) /// Describes a hash-field (a name/value pair)
/// </summary> /// </summary>
public struct HashEntry : IEquatable<HashEntry> public struct HashEntry : IEquatable<HashEntry>
{ {
internal readonly RedisValue name, value; internal readonly RedisValue name, value;
/// <summary> /// <summary>
/// Initializes a HashEntry value /// Initializes a HashEntry value
/// </summary> /// </summary>
public HashEntry(RedisValue name, RedisValue value) public HashEntry(RedisValue name, RedisValue value)
{ {
this.name = name; this.name = name;
this.value = value; this.value = value;
} }
/// <summary> /// <summary>
/// The name of the hash field /// The name of the hash field
/// </summary> /// </summary>
public RedisValue Name { get { return name; } } public RedisValue Name { get { return name; } }
/// <summary> /// <summary>
/// The value of the hash field /// The value of the hash field
/// </summary> /// </summary>
public RedisValue Value{ get { return value; } } public RedisValue Value{ get { return value; } }
/// <summary> /// <summary>
/// The name of the hash field /// The name of the hash field
/// </summary> /// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Obsolete("Please use Name", false)] #if !DNXCORE50
public RedisValue Key { get { return name; } } [Browsable(false)]
#endif
/// <summary> [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Please use Name", false)]
/// Converts to a key/value pair public RedisValue Key { get { return name; } }
/// </summary>
public static implicit operator KeyValuePair<RedisValue, RedisValue>(HashEntry value) /// <summary>
{ /// Converts to a key/value pair
return new KeyValuePair<RedisValue, RedisValue>(value.name, value.value); /// </summary>
} public static implicit operator KeyValuePair<RedisValue, RedisValue>(HashEntry value)
/// <summary> {
/// Converts from a key/value pair return new KeyValuePair<RedisValue, RedisValue>(value.name, value.value);
/// </summary> }
public static implicit operator HashEntry(KeyValuePair<RedisValue, RedisValue> value) /// <summary>
{ /// Converts from a key/value pair
return new HashEntry(value.Key, value.Value); /// </summary>
} public static implicit operator HashEntry(KeyValuePair<RedisValue, RedisValue> value)
{
/// <summary> return new HashEntry(value.Key, value.Value);
/// See Object.ToString() }
/// </summary>
public override string ToString() /// <summary>
{ /// See Object.ToString()
return name + ": " + value; /// </summary>
} public override string ToString()
/// <summary> {
/// See Object.GetHashCode() return name + ": " + value;
/// </summary> }
public override int GetHashCode() /// <summary>
{ /// See Object.GetHashCode()
return name.GetHashCode() ^ value.GetHashCode(); /// </summary>
} public override int GetHashCode()
/// <summary> {
/// Compares two values for equality return name.GetHashCode() ^ value.GetHashCode();
/// </summary> }
public override bool Equals(object obj) /// <summary>
{ /// Compares two values for equality
return obj is HashEntry && Equals((HashEntry)obj); /// </summary>
} public override bool Equals(object obj)
{
/// <summary> return obj is HashEntry && Equals((HashEntry)obj);
/// Compares two values for equality }
/// </summary>
public bool Equals(HashEntry value) /// <summary>
{ /// Compares two values for equality
return this.name == value.name && this.value == value.value; /// </summary>
} public bool Equals(HashEntry value)
/// <summary> {
/// Compares two values for equality return this.name == value.name && this.value == value.value;
/// </summary> }
public static bool operator ==(HashEntry x, HashEntry y) /// <summary>
{ /// Compares two values for equality
return x.name == y.name && x.value == y.value; /// </summary>
} public static bool operator ==(HashEntry x, HashEntry y)
/// <summary> {
/// Compares two values for non-equality return x.name == y.name && x.value == y.value;
/// </summary> }
public static bool operator !=(HashEntry x, HashEntry y) /// <summary>
{ /// Compares two values for non-equality
return x.name != y.name || x.value != y.value; /// </summary>
} public static bool operator !=(HashEntry x, HashEntry y)
} {
} return x.name != y.name || x.value != y.value;
}
}
}
#if DNXCORE50
using System;
#endif
using System.Text.RegularExpressions;
namespace StackExchange.Redis
{
internal static class InternalRegexCompiledOption
{
private static readonly RegexOptions RegexCompiledOption;
static InternalRegexCompiledOption()
{
#if DNXCORE50
if (!Enum.TryParse("Compiled", out RegexCompiledOption))
RegexCompiledOption = RegexOptions.None;
#else
RegexCompiledOption = RegexOptions.Compiled;
#endif
}
/// <summary>
/// Gets the default <see cref="RegexOptions"/> to use.
/// <see cref="System.Text.RegularExpressions.RegexOptions.Compiled"/> option isn't available yet for dnxcore50.
/// This returns <see cref="System.Text.RegularExpressions.RegexOptions.Compiled"/> if it is supported;
/// <see cref="System.Text.RegularExpressions.RegexOptions.None"/> otherwise.
/// </summary>
public static RegexOptions Default
{
get
{
return RegexCompiledOption;
}
}
}
}
...@@ -119,7 +119,7 @@ internal void ExtractParameters(object ps, RedisKey? keyPrefix, out RedisKey[] k ...@@ -119,7 +119,7 @@ internal void ExtractParameters(object ps, RedisKey? keyPrefix, out RedisKey[] k
{ {
string missingMember; string missingMember;
string badMemberType; string badMemberType;
if(!ScriptParameterMapper.IsValidParameterHash(psType, this, out missingMember, out badMemberType)) if (!ScriptParameterMapper.IsValidParameterHash(psType, this, out missingMember, out badMemberType))
{ {
if (missingMember != null) if (missingMember != null)
{ {
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
#if FEATURE_SERIALIZATION
using System.Runtime.Serialization; using System.Runtime.Serialization;
#endif
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -11,22 +13,29 @@ namespace StackExchange.Redis ...@@ -11,22 +13,29 @@ namespace StackExchange.Redis
/// <summary> /// <summary>
/// Indicates that a command was illegal and was not sent to the server /// Indicates that a command was illegal and was not sent to the server
/// </summary> /// </summary>
#if FEATURE_SERIALIZATION
[Serializable] [Serializable]
#endif
public sealed class RedisCommandException : Exception public sealed class RedisCommandException : Exception
{ {
#if FEATURE_SERIALIZATION
private RedisCommandException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { } private RedisCommandException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
#endif
internal RedisCommandException(string message) : base(message) { } internal RedisCommandException(string message) : base(message) { }
internal RedisCommandException(string message, Exception innerException) : base(message, innerException) { } internal RedisCommandException(string message, Exception innerException) : base(message, innerException) { }
} }
/// <summary> /// <summary>
/// Indicates a connection fault when communicating with redis /// Indicates a connection fault when communicating with redis
/// </summary> /// </summary>
#if FEATURE_SERIALIZATION
[Serializable] [Serializable]
#endif
public sealed class RedisConnectionException : RedisException public sealed class RedisConnectionException : RedisException
{ {
#if FEATURE_SERIALIZATION
private RedisConnectionException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) private RedisConnectionException(SerializationInfo info, StreamingContext ctx) : base(info, ctx)
{ {
this.FailureType = (ConnectionFailureType)info.GetInt32("failureType"); this.FailureType = (ConnectionFailureType)info.GetInt32("failureType");
...@@ -39,6 +48,7 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont ...@@ -39,6 +48,7 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont
base.GetObjectData(info, context); base.GetObjectData(info, context);
info.AddValue("failureType", (int)this.FailureType); info.AddValue("failureType", (int)this.FailureType);
} }
#endif
internal RedisConnectionException(ConnectionFailureType failureType, string message) : base(message) internal RedisConnectionException(ConnectionFailureType failureType, string message) : base(message)
{ {
...@@ -54,28 +64,36 @@ internal RedisConnectionException(ConnectionFailureType failureType, string mess ...@@ -54,28 +64,36 @@ internal RedisConnectionException(ConnectionFailureType failureType, string mess
/// </summary> /// </summary>
public ConnectionFailureType FailureType { get; private set; } public ConnectionFailureType FailureType { get; private set; }
} }
/// <summary> /// <summary>
/// Indicates an issue communicating with redis /// Indicates an issue communicating with redis
/// </summary> /// </summary>
#if FEATURE_SERIALIZATION
[Serializable] [Serializable]
#endif
public class RedisException : Exception public class RedisException : Exception
{ {
/// <summary> /// <summary>
/// Deserialization constructor; not intended for general usage /// Deserialization constructor; not intended for general usage
/// </summary> /// </summary>
#if FEATURE_SERIALIZATION
protected RedisException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { } protected RedisException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
#endif
internal RedisException(string message) : base(message) { } internal RedisException(string message) : base(message) { }
internal RedisException(string message, Exception innerException) : base(message, innerException) { } internal RedisException(string message, Exception innerException) : base(message, innerException) { }
} }
/// <summary> /// <summary>
/// Indicates an exception raised by a redis server /// Indicates an exception raised by a redis server
/// </summary> /// </summary>
#if FEATURE_SERIALIZATION
[Serializable] [Serializable]
#endif
public sealed class RedisServerException : RedisException public sealed class RedisServerException : RedisException
{ {
#if FEATURE_SERIALIZATION
private RedisServerException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { } private RedisServerException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
#endif
internal RedisServerException(string message) : base(message) { } internal RedisServerException(string message) : base(message) { }
} }
......
...@@ -394,7 +394,7 @@ internal void OnHeartbeat(bool ifConnectedOnly) ...@@ -394,7 +394,7 @@ internal void OnHeartbeat(bool ifConnectedOnly)
switch (state) switch (state)
{ {
case (int)State.Connecting: case (int)State.Connecting:
int connectTimeMilliseconds = unchecked(Environment.TickCount - Thread.VolatileRead(ref connectStartTicks)); int connectTimeMilliseconds = unchecked(Environment.TickCount - VolatileWrapper.Read(ref connectStartTicks));
if (connectTimeMilliseconds >= multiplexer.RawConfig.ConnectTimeout) if (connectTimeMilliseconds >= multiplexer.RawConfig.ConnectTimeout)
{ {
Trace("Aborting connect"); Trace("Aborting connect");
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
#if DNXCORE50
using System.Threading.Tasks;
#endif
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
...@@ -93,7 +96,7 @@ public PhysicalConnection(PhysicalBridge bridge) ...@@ -93,7 +96,7 @@ public PhysicalConnection(PhysicalBridge bridge)
public void BeginConnect(TextWriter log) public void BeginConnect(TextWriter log)
{ {
Thread.VolatileWrite(ref firstUnansweredWriteTickCount, 0); VolatileWrapper.Write(ref firstUnansweredWriteTickCount, 0);
var endpoint = this.bridge.ServerEndPoint.EndPoint; var endpoint = this.bridge.ServerEndPoint.EndPoint;
multiplexer.Trace("Connecting...", physicalName); multiplexer.Trace("Connecting...", physicalName);
...@@ -113,7 +116,7 @@ public long LastWriteSecondsAgo ...@@ -113,7 +116,7 @@ public long LastWriteSecondsAgo
{ {
get get
{ {
return unchecked(Environment.TickCount - Thread.VolatileRead(ref lastWriteTickCount)) / 1000; return unchecked(Environment.TickCount - VolatileWrapper.Read(ref lastWriteTickCount)) / 1000;
} }
} }
...@@ -128,13 +131,17 @@ public void Dispose() ...@@ -128,13 +131,17 @@ public void Dispose()
if (outStream != null) if (outStream != null)
{ {
multiplexer.Trace("Disconnecting...", physicalName); multiplexer.Trace("Disconnecting...", physicalName);
#if !DNXCORE50
try { outStream.Close(); } catch { } try { outStream.Close(); } catch { }
#endif
try { outStream.Dispose(); } catch { } try { outStream.Dispose(); } catch { }
outStream = null; outStream = null;
} }
if (netStream != null) if (netStream != null)
{ {
#if !DNXCORE50
try { netStream.Close(); } catch { } try { netStream.Close(); } catch { }
#endif
try { netStream.Dispose(); } catch { } try { netStream.Dispose(); } catch { }
netStream = null; netStream = null;
} }
...@@ -189,9 +196,9 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, ref Socket ...@@ -189,9 +196,9 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, ref Socket
if (isCurrent && Interlocked.CompareExchange(ref failureReported, 1, 0) == 0) if (isCurrent && Interlocked.CompareExchange(ref failureReported, 1, 0) == 0)
{ {
managerState = SocketManager.ManagerState.RecordConnectionFailed_ReportFailure; managerState = SocketManager.ManagerState.RecordConnectionFailed_ReportFailure;
int now = Environment.TickCount, lastRead = Thread.VolatileRead(ref lastReadTickCount), lastWrite = Thread.VolatileRead(ref lastWriteTickCount), int now = Environment.TickCount, lastRead = VolatileWrapper.Read(ref lastReadTickCount), lastWrite = VolatileWrapper.Read(ref lastWriteTickCount),
lastBeat = Thread.VolatileRead(ref lastBeatTickCount); lastBeat = VolatileWrapper.Read(ref lastBeatTickCount);
int unansweredRead = Thread.VolatileRead(ref firstUnansweredWriteTickCount); int unansweredRead = VolatileWrapper.Read(ref firstUnansweredWriteTickCount);
var exMessage = new StringBuilder(failureType + " on " + Format.ToString(bridge.ServerEndPoint.EndPoint) + "/" + connectionType); var exMessage = new StringBuilder(failureType + " on " + Format.ToString(bridge.ServerEndPoint.EndPoint) + "/" + connectionType);
var data = new List<Tuple<string, string>> var data = new List<Tuple<string, string>>
...@@ -224,12 +231,12 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, ref Socket ...@@ -224,12 +231,12 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, ref Socket
add("Last-Heartbeat", "last-heartbeat", (lastBeat == 0 ? "never" : (unchecked(now - lastBeat)/1000 + "s ago"))+ (bridge.IsBeating ? " (mid-beat)" : "") ); add("Last-Heartbeat", "last-heartbeat", (lastBeat == 0 ? "never" : (unchecked(now - lastBeat)/1000 + "s ago"))+ (bridge.IsBeating ? " (mid-beat)" : "") );
add("Last-Multiplexer-Heartbeat", "last-mbeat", multiplexer.LastHeartbeatSecondsAgo + "s ago"); add("Last-Multiplexer-Heartbeat", "last-mbeat", multiplexer.LastHeartbeatSecondsAgo + "s ago");
add("Last-Global-Heartbeat", "global", ConnectionMultiplexer.LastGlobalHeartbeatSecondsAgo + "s ago"); add("Last-Global-Heartbeat", "global", ConnectionMultiplexer.LastGlobalHeartbeatSecondsAgo + "s ago");
#if !__MonoCS__ #if FEATURE_SOCKET_MODE_POLL
var mgr = bridge.Multiplexer.SocketManager; var mgr = bridge.Multiplexer.SocketManager;
add("SocketManager-State", "mgr", mgr.State.ToString()); add("SocketManager-State", "mgr", mgr.State.ToString());
add("Last-Error", "err", mgr.LastErrorTimeRelative()); add("Last-Error", "err", mgr.LastErrorTimeRelative());
#endif #endif
var ex = innerException == null var ex = innerException == null
? new RedisConnectionException(failureType, exMessage.ToString()) ? new RedisConnectionException(failureType, exMessage.ToString())
: new RedisConnectionException(failureType, exMessage.ToString(), innerException); : new RedisConnectionException(failureType, exMessage.ToString(), innerException);
...@@ -596,20 +603,17 @@ unsafe void WriteRaw(Stream stream, string value, int encodedLength) ...@@ -596,20 +603,17 @@ unsafe void WriteRaw(Stream stream, string value, int encodedLength)
} }
else else
{ {
fixed (char* c = value) int charsRemaining = value.Length, charOffset = 0, bytesWritten;
fixed (byte* b = outScratch) var valueCharArray = value.ToCharArray();
while (charsRemaining > Scratch_CharsPerBlock)
{ {
int charsRemaining = value.Length, charOffset = 0, bytesWritten; bytesWritten = outEncoder.GetBytes(valueCharArray, charOffset, Scratch_CharsPerBlock, outScratch, 0, false);
while (charsRemaining > Scratch_CharsPerBlock) stream.Write(outScratch, 0, bytesWritten);
{ charOffset += Scratch_CharsPerBlock;
bytesWritten = outEncoder.GetBytes(c + charOffset, Scratch_CharsPerBlock, b, ScratchSize, false); charsRemaining -= Scratch_CharsPerBlock;
stream.Write(outScratch, 0, bytesWritten);
charOffset += Scratch_CharsPerBlock;
charsRemaining -= Scratch_CharsPerBlock;
}
bytesWritten = outEncoder.GetBytes(c + charOffset, charsRemaining, b, ScratchSize, true);
if (bytesWritten != 0) stream.Write(outScratch, 0, bytesWritten);
} }
bytesWritten = outEncoder.GetBytes(valueCharArray, charOffset, charsRemaining, outScratch, 0, true);
if (bytesWritten != 0) stream.Write(outScratch, 0, bytesWritten);
} }
} }
const int ScratchSize = 512; const int ScratchSize = 512;
...@@ -657,6 +661,14 @@ void BeginReading() ...@@ -657,6 +661,14 @@ void BeginReading()
int space = EnsureSpaceAndComputeBytesToRead(); int space = EnsureSpaceAndComputeBytesToRead();
multiplexer.Trace("Beginning async read...", physicalName); multiplexer.Trace("Beginning async read...", physicalName);
var result = netStream.BeginRead(ioBuffer, ioBufferBytes, space, endRead, this); var result = netStream.BeginRead(ioBuffer, ioBufferBytes, space, endRead, this);
#if DNXCORE50
Task<int> t = (Task<int>)result;
if (t.Status == TaskStatus.RanToCompletion && t.Result == -1)
{
multiplexer.Trace("Could not connect: ", physicalName);
return;
}
#endif
if (result.CompletedSynchronously) if (result.CompletedSynchronously)
{ {
multiplexer.Trace("Completed synchronously: processing immediately", physicalName); multiplexer.Trace("Completed synchronously: processing immediately", physicalName);
...@@ -664,7 +676,13 @@ void BeginReading() ...@@ -664,7 +676,13 @@ void BeginReading()
} }
} while (keepReading); } while (keepReading);
} }
catch(System.IO.IOException ex) #if DNXCORE50
catch (AggregateException ex)
{
throw ex.InnerException;
}
#endif
catch (System.IO.IOException ex)
{ {
multiplexer.Trace("Could not connect: " + ex.Message, physicalName); multiplexer.Trace("Could not connect: " + ex.Message, physicalName);
} }
...@@ -724,8 +742,11 @@ SocketMode ISocketCallback.Connected(Stream stream, TextWriter log) ...@@ -724,8 +742,11 @@ SocketMode ISocketCallback.Connected(Stream stream, TextWriter log)
, EncryptionPolicy.RequireEncryption , EncryptionPolicy.RequireEncryption
#endif #endif
); );
ssl.AuthenticateAsClient(host); try
if (!ssl.IsEncrypted) {
ssl.AuthenticateAsClient(host);
}
catch (AuthenticationException)
{ {
RecordConnectionFailed(ConnectionFailureType.AuthenticationFailure); RecordConnectionFailed(ConnectionFailureType.AuthenticationFailure);
multiplexer.Trace("Encryption failure"); multiplexer.Trace("Encryption failure");
...@@ -738,7 +759,11 @@ SocketMode ISocketCallback.Connected(Stream stream, TextWriter log) ...@@ -738,7 +759,11 @@ SocketMode ISocketCallback.Connected(Stream stream, TextWriter log)
int bufferSize = config.WriteBuffer; int bufferSize = config.WriteBuffer;
this.netStream = stream; this.netStream = stream;
#if !DNXCORE50
this.outStream = bufferSize <= 0 ? stream : new BufferedStream(stream, bufferSize); this.outStream = bufferSize <= 0 ? stream : new BufferedStream(stream, bufferSize);
#else
this.outStream = stream;
#endif
multiplexer.LogLocked(log, "Connected {0}", bridge); multiplexer.LogLocked(log, "Connected {0}", bridge);
bridge.OnConnected(this, log); bridge.OnConnected(this, log);
...@@ -895,7 +920,7 @@ private bool ProcessReadBytes(int bytesRead) ...@@ -895,7 +920,7 @@ private bool ProcessReadBytes(int bytesRead)
Interlocked.Exchange(ref lastReadTickCount, Environment.TickCount); Interlocked.Exchange(ref lastReadTickCount, Environment.TickCount);
// reset unanswered write timestamp // reset unanswered write timestamp
Thread.VolatileWrite(ref firstUnansweredWriteTickCount, 0); VolatileWrapper.Write(ref firstUnansweredWriteTickCount, 0);
ioBufferBytes += bytesRead; ioBufferBytes += bytesRead;
multiplexer.Trace("More bytes available: " + bytesRead + " (" + ioBufferBytes + ")", physicalName); multiplexer.Trace("More bytes available: " + bytesRead + " (" + ioBufferBytes + ")", physicalName);
...@@ -1058,7 +1083,7 @@ RawResult TryParseResult(byte[] buffer, ref int offset, ref int count) ...@@ -1058,7 +1083,7 @@ RawResult TryParseResult(byte[] buffer, ref int offset, ref int count)
public void CheckForStaleConnection(ref SocketManager.ManagerState managerState) public void CheckForStaleConnection(ref SocketManager.ManagerState managerState)
{ {
int firstUnansweredWrite; int firstUnansweredWrite;
firstUnansweredWrite = Thread.VolatileRead(ref firstUnansweredWriteTickCount); firstUnansweredWrite = VolatileWrapper.Read(ref firstUnansweredWriteTickCount);
DebugEmulateStaleConnection(ref firstUnansweredWrite); DebugEmulateStaleConnection(ref firstUnansweredWrite);
...@@ -1070,4 +1095,38 @@ public void CheckForStaleConnection(ref SocketManager.ManagerState managerState) ...@@ -1070,4 +1095,38 @@ public void CheckForStaleConnection(ref SocketManager.ManagerState managerState)
} }
} }
} }
#if DNXCORE50
internal static class StreamExtensions
{
internal static IAsyncResult BeginRead(this Stream stream, byte[] buffer, int offset, int count, AsyncCallback ac, object state)
{
Task<int> f = Task<int>.Factory.StartNew(_ => {
try
{
return stream.Read(buffer, offset, count);
}
catch (IOException ex)
{
System.Diagnostics.Trace.WriteLine("Could not connect: " + ex.InnerException.Message);
return -1;
}
}, state);
if (ac != null) f.ContinueWith(res => ac(f));
return f;
}
internal static int EndRead(this Stream stream, IAsyncResult ar)
{
try
{
return ((Task<int>)ar).Result;
}
catch (AggregateException ex)
{
throw ex.InnerException;
}
}
}
#endif
} }
using System; using System;
using System.Linq;
namespace StackExchange.Redis
{ namespace StackExchange.Redis
/// <summary> {
/// Represents a general-purpose result from redis, that may be cast into various anticipated types /// <summary>
/// </summary> /// Represents a general-purpose result from redis, that may be cast into various anticipated types
public abstract class RedisResult /// </summary>
{ public abstract class RedisResult
{
// internally, this is very similar to RawResult, except it is designed to be usable
// outside of the IO-processing pipeline: the buffers are standalone, etc // internally, this is very similar to RawResult, except it is designed to be usable
// outside of the IO-processing pipeline: the buffers are standalone, etc
internal static RedisResult TryCreate(PhysicalConnection connection, RawResult result)
{ internal static RedisResult TryCreate(PhysicalConnection connection, RawResult result)
try {
{ try
switch (result.Type) {
{ switch (result.Type)
case ResultType.Integer: {
case ResultType.SimpleString: case ResultType.Integer:
case ResultType.BulkString: case ResultType.SimpleString:
return new SingleRedisResult(result.AsRedisValue()); case ResultType.BulkString:
case ResultType.MultiBulk: return new SingleRedisResult(result.AsRedisValue());
var items = result.GetItems(); case ResultType.MultiBulk:
var arr = new RedisResult[items.Length]; var items = result.GetItems();
for (int i = 0; i < arr.Length; i++) var arr = new RedisResult[items.Length];
{ for (int i = 0; i < arr.Length; i++)
var next = TryCreate(connection, items[i]); {
if (next == null) return null; // means we didn't understand var next = TryCreate(connection, items[i]);
arr[i] = next; if (next == null) return null; // means we didn't understand
} arr[i] = next;
return new ArrayRedisResult(arr); }
case ResultType.Error: return new ArrayRedisResult(arr);
return new ErrorRedisResult(result.GetString()); case ResultType.Error:
default: return new ErrorRedisResult(result.GetString());
return null; default:
} return null;
} catch(Exception ex) }
{ } catch(Exception ex)
if(connection != null) connection.OnInternalError(ex); {
return null; // will be logged as a protocol fail by the processor if(connection != null) connection.OnInternalError(ex);
} return null; // will be logged as a protocol fail by the processor
} }
}
/// <summary>
/// Indicates whether this result was a null result /// <summary>
/// </summary> /// Indicates whether this result was a null result
public abstract bool IsNull { get; } /// </summary>
public abstract bool IsNull { get; }
/// <summary>
/// Interprets the result as a String /// <summary>
/// </summary> /// Interprets the result as a String
public static explicit operator string (RedisResult result) { return result.AsString(); } /// </summary>
/// <summary> public static explicit operator string (RedisResult result) { return result.AsString(); }
/// Interprets the result as a Byte[] /// <summary>
/// </summary> /// Interprets the result as a Byte[]
public static explicit operator byte[] (RedisResult result) { return result.AsByteArray(); } /// </summary>
/// <summary> public static explicit operator byte[] (RedisResult result) { return result.AsByteArray(); }
/// Interprets the result as a Double /// <summary>
/// </summary> /// Interprets the result as a Double
public static explicit operator double (RedisResult result) { return result.AsDouble(); } /// </summary>
/// <summary> public static explicit operator double (RedisResult result) { return result.AsDouble(); }
/// Interprets the result as an Int64 /// <summary>
/// </summary> /// Interprets the result as an Int64
public static explicit operator long (RedisResult result) { return result.AsInt64(); } /// </summary>
/// <summary> public static explicit operator long (RedisResult result) { return result.AsInt64(); }
/// Interprets the result as an Int32 /// <summary>
/// </summary> /// Interprets the result as an Int32
public static explicit operator int (RedisResult result) { return result.AsInt32(); } /// </summary>
/// <summary> public static explicit operator int (RedisResult result) { return result.AsInt32(); }
/// Interprets the result as a Boolean /// <summary>
/// </summary> /// Interprets the result as a Boolean
public static explicit operator bool (RedisResult result) { return result.AsBoolean(); } /// </summary>
/// <summary> public static explicit operator bool (RedisResult result) { return result.AsBoolean(); }
/// Interprets the result as a RedisValue /// <summary>
/// </summary> /// Interprets the result as a RedisValue
public static explicit operator RedisValue (RedisResult result) { return result.AsRedisValue(); } /// </summary>
/// <summary> public static explicit operator RedisValue (RedisResult result) { return result.AsRedisValue(); }
/// Interprets the result as a RedisKey /// <summary>
/// </summary> /// Interprets the result as a RedisKey
public static explicit operator RedisKey (RedisResult result) { return result.AsRedisKey(); } /// </summary>
/// <summary> public static explicit operator RedisKey (RedisResult result) { return result.AsRedisKey(); }
/// Interprets the result as a Nullable Double /// <summary>
/// </summary> /// Interprets the result as a Nullable Double
public static explicit operator double? (RedisResult result) { return result.AsNullableDouble(); } /// </summary>
/// <summary> public static explicit operator double? (RedisResult result) { return result.AsNullableDouble(); }
/// Interprets the result as a Nullable Int64 /// <summary>
/// </summary> /// Interprets the result as a Nullable Int64
public static explicit operator long? (RedisResult result) { return result.AsNullableInt64(); } /// </summary>
/// <summary> public static explicit operator long? (RedisResult result) { return result.AsNullableInt64(); }
/// Interprets the result as a Nullable Int32 /// <summary>
/// </summary> /// Interprets the result as a Nullable Int32
public static explicit operator int? (RedisResult result) { return result.AsNullableInt32(); } /// </summary>
/// <summary> public static explicit operator int? (RedisResult result) { return result.AsNullableInt32(); }
/// Interprets the result as a Nullable Boolean /// <summary>
/// </summary> /// Interprets the result as a Nullable Boolean
public static explicit operator bool? (RedisResult result) { return result.AsNullableBoolean(); } /// </summary>
/// <summary> public static explicit operator bool? (RedisResult result) { return result.AsNullableBoolean(); }
/// Interprets the result as an array of String /// <summary>
/// </summary> /// Interprets the result as an array of String
public static explicit operator string[] (RedisResult result) { return result.AsStringArray(); } /// </summary>
/// <summary> public static explicit operator string[] (RedisResult result) { return result.AsStringArray(); }
/// Interprets the result as an array of Byte[] /// <summary>
/// </summary> /// Interprets the result as an array of Byte[]
public static explicit operator byte[][] (RedisResult result) { return result.AsByteArrayArray(); } /// </summary>
/// <summary> public static explicit operator byte[][] (RedisResult result) { return result.AsByteArrayArray(); }
/// Interprets the result as an array of Double /// <summary>
/// </summary> /// Interprets the result as an array of Double
public static explicit operator double[] (RedisResult result) { return result.AsDoubleArray(); } /// </summary>
/// <summary> public static explicit operator double[] (RedisResult result) { return result.AsDoubleArray(); }
/// Interprets the result as an array of Int64 /// <summary>
/// </summary> /// Interprets the result as an array of Int64
public static explicit operator long[] (RedisResult result) { return result.AsInt64Array(); } /// </summary>
/// <summary> public static explicit operator long[] (RedisResult result) { return result.AsInt64Array(); }
/// Interprets the result as an array of Int32 /// <summary>
/// </summary> /// Interprets the result as an array of Int32
public static explicit operator int[] (RedisResult result) { return result.AsInt32Array(); } /// </summary>
/// <summary> public static explicit operator int[] (RedisResult result) { return result.AsInt32Array(); }
/// Interprets the result as an array of Boolean /// <summary>
/// </summary> /// Interprets the result as an array of Boolean
public static explicit operator bool[] (RedisResult result) { return result.AsBooleanArray(); } /// </summary>
/// <summary> public static explicit operator bool[] (RedisResult result) { return result.AsBooleanArray(); }
/// Interprets the result as an array of RedisValue /// <summary>
/// </summary> /// Interprets the result as an array of RedisValue
public static explicit operator RedisValue[] (RedisResult result) { return result.AsRedisValueArray(); } /// </summary>
/// <summary> public static explicit operator RedisValue[] (RedisResult result) { return result.AsRedisValueArray(); }
/// Interprets the result as an array of RedisKey /// <summary>
/// </summary> /// Interprets the result as an array of RedisKey
public static explicit operator RedisKey[] (RedisResult result) { return result.AsRedisKeyArray(); } /// </summary>
/// <summary> public static explicit operator RedisKey[] (RedisResult result) { return result.AsRedisKeyArray(); }
/// Interprets the result as an array of RedisResult /// <summary>
/// </summary> /// Interprets the result as an array of RedisResult
public static explicit operator RedisResult[] (RedisResult result) { return result.AsRedisResultArray(); } /// </summary>
public static explicit operator RedisResult[] (RedisResult result) { return result.AsRedisResultArray(); }
internal abstract bool AsBoolean();
internal abstract bool AsBoolean();
internal abstract bool[] AsBooleanArray();
internal abstract bool[] AsBooleanArray();
internal abstract byte[] AsByteArray();
internal abstract byte[] AsByteArray();
internal abstract byte[][] AsByteArrayArray();
internal abstract byte[][] AsByteArrayArray();
internal abstract double AsDouble();
internal abstract double AsDouble();
internal abstract double[] AsDoubleArray();
internal abstract double[] AsDoubleArray();
internal abstract int AsInt32();
internal abstract int AsInt32();
internal abstract int[] AsInt32Array();
internal abstract int[] AsInt32Array();
internal abstract long AsInt64();
internal abstract long AsInt64();
internal abstract long[] AsInt64Array();
internal abstract long[] AsInt64Array();
internal abstract bool? AsNullableBoolean();
internal abstract bool? AsNullableBoolean();
internal abstract double? AsNullableDouble();
internal abstract double? AsNullableDouble();
internal abstract int? AsNullableInt32();
internal abstract int? AsNullableInt32();
internal abstract long? AsNullableInt64();
internal abstract long? AsNullableInt64();
internal abstract RedisKey AsRedisKey();
internal abstract RedisKey AsRedisKey();
internal abstract RedisKey[] AsRedisKeyArray();
internal abstract RedisKey[] AsRedisKeyArray();
internal abstract RedisResult[] AsRedisResultArray();
internal abstract RedisResult[] AsRedisResultArray();
internal abstract RedisValue AsRedisValue();
internal abstract RedisValue AsRedisValue();
internal abstract RedisValue[] AsRedisValueArray();
internal abstract string AsString(); internal abstract RedisValue[] AsRedisValueArray();
internal abstract string[] AsStringArray(); internal abstract string AsString();
private sealed class ArrayRedisResult : RedisResult internal abstract string[] AsStringArray();
{ private sealed class ArrayRedisResult : RedisResult
public override bool IsNull {
{ public override bool IsNull
get { return value == null; } {
} get { return value == null; }
private readonly RedisResult[] value; }
public ArrayRedisResult(RedisResult[] value) private readonly RedisResult[] value;
{ public ArrayRedisResult(RedisResult[] value)
if (value == null) throw new ArgumentNullException("value"); {
this.value = value; if (value == null) throw new ArgumentNullException("value");
} this.value = value;
public override string ToString() }
{ public override string ToString()
return value.Length + " element(s)"; {
} return value.Length + " element(s)";
internal override bool AsBoolean() }
{ internal override bool AsBoolean()
if (value.Length == 1) return value[0].AsBoolean(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsBoolean();
} throw new InvalidCastException();
}
internal override bool[] AsBooleanArray() { return Array.ConvertAll(value, x => x.AsBoolean()); }
internal override bool[] AsBooleanArray() { return value.Select(x => x.AsBoolean()).ToArray(); }
internal override byte[] AsByteArray()
{ internal override byte[] AsByteArray()
if (value.Length == 1) return value[0].AsByteArray(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsByteArray();
} throw new InvalidCastException();
internal override byte[][] AsByteArrayArray() { return Array.ConvertAll(value, x => x.AsByteArray()); } }
internal override byte[][] AsByteArrayArray() { return value.Select(x => x.AsByteArray()).ToArray(); }
internal override double AsDouble()
{ internal override double AsDouble()
if (value.Length == 1) return value[0].AsDouble(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsDouble();
} throw new InvalidCastException();
}
internal override double[] AsDoubleArray() { return Array.ConvertAll(value, x => x.AsDouble()); }
internal override double[] AsDoubleArray() { return value.Select(x => x.AsDouble()).ToArray(); }
internal override int AsInt32()
{ internal override int AsInt32()
if (value.Length == 1) return value[0].AsInt32(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsInt32();
} throw new InvalidCastException();
}
internal override int[] AsInt32Array() { return Array.ConvertAll(value, x => x.AsInt32()); }
internal override int[] AsInt32Array() { return value.Select(x => x.AsInt32()).ToArray(); }
internal override long AsInt64()
{ internal override long AsInt64()
if (value.Length == 1) return value[0].AsInt64(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsInt64();
} throw new InvalidCastException();
}
internal override long[] AsInt64Array() { return Array.ConvertAll(value, x => x.AsInt64()); }
internal override long[] AsInt64Array() { return value.Select(x => x.AsInt64()).ToArray(); }
internal override bool? AsNullableBoolean()
{ internal override bool? AsNullableBoolean()
if (value.Length == 1) return value[0].AsNullableBoolean(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsNullableBoolean();
} throw new InvalidCastException();
}
internal override double? AsNullableDouble()
{ internal override double? AsNullableDouble()
if (value.Length == 1) return value[0].AsNullableDouble(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsNullableDouble();
} throw new InvalidCastException();
}
internal override int? AsNullableInt32()
{ internal override int? AsNullableInt32()
if (value.Length == 1) return value[0].AsNullableInt32(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsNullableInt32();
} throw new InvalidCastException();
}
internal override long? AsNullableInt64()
{ internal override long? AsNullableInt64()
if (value.Length == 1) return value[0].AsNullableInt64(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsNullableInt64();
} throw new InvalidCastException();
}
internal override RedisKey AsRedisKey()
{ internal override RedisKey AsRedisKey()
if (value.Length == 1) return value[0].AsRedisKey(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsRedisKey();
} throw new InvalidCastException();
}
internal override RedisKey[] AsRedisKeyArray() { return Array.ConvertAll(value, x => x.AsRedisKey()); }
internal override RedisKey[] AsRedisKeyArray() { return value.Select(x => x.AsRedisKey()).ToArray(); }
internal override RedisResult[] AsRedisResultArray() { return value; }
internal override RedisResult[] AsRedisResultArray() { return value; }
internal override RedisValue AsRedisValue()
{ internal override RedisValue AsRedisValue()
if (value.Length == 1) return value[0].AsRedisValue(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsRedisValue();
} throw new InvalidCastException();
}
internal override RedisValue[] AsRedisValueArray() { return Array.ConvertAll(value, x => x.AsRedisValue()); }
internal override RedisValue[] AsRedisValueArray() { return value.Select(x => x.AsRedisValue()).ToArray(); }
internal override string AsString()
{ internal override string AsString()
if (value.Length == 1) return value[0].AsString(); {
throw new InvalidCastException(); if (value.Length == 1) return value[0].AsString();
} throw new InvalidCastException();
internal override string[] AsStringArray() { return Array.ConvertAll(value, x => x.AsString()); } }
} internal override string[] AsStringArray() { return value.Select(x => x.AsString()).ToArray(); }
}
private sealed class ErrorRedisResult : RedisResult
{ private sealed class ErrorRedisResult : RedisResult
private readonly string value; {
public ErrorRedisResult(string value) private readonly string value;
{ public ErrorRedisResult(string value)
if (value == null) throw new ArgumentNullException("value"); {
this.value = value; if (value == null) throw new ArgumentNullException("value");
} this.value = value;
public override bool IsNull }
{ public override bool IsNull
get { return value == null; } {
} get { return value == null; }
public override string ToString() { return value; } }
internal override bool AsBoolean() { throw new RedisServerException(value); } public override string ToString() { return value; }
internal override bool AsBoolean() { throw new RedisServerException(value); }
internal override bool[] AsBooleanArray() { throw new RedisServerException(value); }
internal override bool[] AsBooleanArray() { throw new RedisServerException(value); }
internal override byte[] AsByteArray() { throw new RedisServerException(value); }
internal override byte[] AsByteArray() { throw new RedisServerException(value); }
internal override byte[][] AsByteArrayArray() { throw new RedisServerException(value); }
internal override byte[][] AsByteArrayArray() { throw new RedisServerException(value); }
internal override double AsDouble() { throw new RedisServerException(value); }
internal override double AsDouble() { throw new RedisServerException(value); }
internal override double[] AsDoubleArray() { throw new RedisServerException(value); }
internal override double[] AsDoubleArray() { throw new RedisServerException(value); }
internal override int AsInt32() { throw new RedisServerException(value); }
internal override int AsInt32() { throw new RedisServerException(value); }
internal override int[] AsInt32Array() { throw new RedisServerException(value); }
internal override int[] AsInt32Array() { throw new RedisServerException(value); }
internal override long AsInt64() { throw new RedisServerException(value); }
internal override long AsInt64() { throw new RedisServerException(value); }
internal override long[] AsInt64Array() { throw new RedisServerException(value); }
internal override long[] AsInt64Array() { throw new RedisServerException(value); }
internal override bool? AsNullableBoolean() { throw new RedisServerException(value); }
internal override bool? AsNullableBoolean() { throw new RedisServerException(value); }
internal override double? AsNullableDouble() { throw new RedisServerException(value); }
internal override double? AsNullableDouble() { throw new RedisServerException(value); }
internal override int? AsNullableInt32() { throw new RedisServerException(value); }
internal override int? AsNullableInt32() { throw new RedisServerException(value); }
internal override long? AsNullableInt64() { throw new RedisServerException(value); }
internal override long? AsNullableInt64() { throw new RedisServerException(value); }
internal override RedisKey AsRedisKey() { throw new RedisServerException(value); }
internal override RedisKey AsRedisKey() { throw new RedisServerException(value); }
internal override RedisKey[] AsRedisKeyArray() { throw new RedisServerException(value); }
internal override RedisKey[] AsRedisKeyArray() { throw new RedisServerException(value); }
internal override RedisResult[] AsRedisResultArray() { throw new RedisServerException(value); }
internal override RedisResult[] AsRedisResultArray() { throw new RedisServerException(value); }
internal override RedisValue AsRedisValue() { throw new RedisServerException(value); }
internal override RedisValue AsRedisValue() { throw new RedisServerException(value); }
internal override RedisValue[] AsRedisValueArray() { throw new RedisServerException(value); }
internal override RedisValue[] AsRedisValueArray() { throw new RedisServerException(value); }
internal override string AsString() { throw new RedisServerException(value); }
internal override string[] AsStringArray() { throw new RedisServerException(value); } internal override string AsString() { throw new RedisServerException(value); }
} internal override string[] AsStringArray() { throw new RedisServerException(value); }
}
private sealed class SingleRedisResult : RedisResult
{ private sealed class SingleRedisResult : RedisResult
private readonly RedisValue value; {
public SingleRedisResult(RedisValue value) private readonly RedisValue value;
{ public SingleRedisResult(RedisValue value)
this.value = value; {
} this.value = value;
}
public override bool IsNull
{ public override bool IsNull
get { return value.IsNull; } {
} get { return value.IsNull; }
}
public override string ToString() { return value.ToString(); }
internal override bool AsBoolean() { return (bool)value; } public override string ToString() { return value.ToString(); }
internal override bool AsBoolean() { return (bool)value; }
internal override bool[] AsBooleanArray() { return new[] { AsBoolean() }; }
internal override bool[] AsBooleanArray() { return new[] { AsBoolean() }; }
internal override byte[] AsByteArray() { return (byte[])value; }
internal override byte[][] AsByteArrayArray() { return new[] { AsByteArray() }; } internal override byte[] AsByteArray() { return (byte[])value; }
internal override byte[][] AsByteArrayArray() { return new[] { AsByteArray() }; }
internal override double AsDouble() { return (double)value; }
internal override double AsDouble() { return (double)value; }
internal override double[] AsDoubleArray() { return new[] { AsDouble() }; }
internal override double[] AsDoubleArray() { return new[] { AsDouble() }; }
internal override int AsInt32() { return (int)value; }
internal override int AsInt32() { return (int)value; }
internal override int[] AsInt32Array() { return new[] { AsInt32() }; }
internal override int[] AsInt32Array() { return new[] { AsInt32() }; }
internal override long AsInt64() { return (long)value; }
internal override long AsInt64() { return (long)value; }
internal override long[] AsInt64Array() { return new[] { AsInt64() }; }
internal override long[] AsInt64Array() { return new[] { AsInt64() }; }
internal override bool? AsNullableBoolean() { return (bool?)value; }
internal override bool? AsNullableBoolean() { return (bool?)value; }
internal override double? AsNullableDouble() { return (double?)value; }
internal override double? AsNullableDouble() { return (double?)value; }
internal override int? AsNullableInt32() { return (int?)value; }
internal override int? AsNullableInt32() { return (int?)value; }
internal override long? AsNullableInt64() { return (long?)value; }
internal override long? AsNullableInt64() { return (long?)value; }
internal override RedisKey AsRedisKey() { return (byte[])value; }
internal override RedisKey AsRedisKey() { return (byte[])value; }
internal override RedisKey[] AsRedisKeyArray() { return new[] { AsRedisKey() }; }
internal override RedisKey[] AsRedisKeyArray() { return new[] { AsRedisKey() }; }
internal override RedisResult[] AsRedisResultArray() { throw new InvalidCastException(); }
internal override RedisResult[] AsRedisResultArray() { throw new InvalidCastException(); }
internal override RedisValue AsRedisValue() { return value; }
internal override RedisValue AsRedisValue() { return value; }
internal override RedisValue[] AsRedisValueArray() { return new[] { AsRedisValue() }; }
internal override RedisValue[] AsRedisValueArray() { return new[] { AsRedisValue() }; }
internal override string AsString() { return (string)value; }
internal override string[] AsStringArray() { return new[] { AsString() }; } internal override string AsString() { return (string)value; }
} internal override string[] AsStringArray() { return new[] { AsString() }; }
} }
} }
}
using System; using System;
#if DNXCORE50
using System.Collections.Generic;
using System.Reflection;
#endif
using System.Text; using System.Text;
namespace StackExchange.Redis namespace StackExchange.Redis
...@@ -300,9 +304,14 @@ public int CompareTo(RedisValue other) ...@@ -300,9 +304,14 @@ public int CompareTo(RedisValue other)
{ {
if (otherType == CompareType.Int64) return thisDouble.CompareTo((double)otherInt64); if (otherType == CompareType.Int64) return thisDouble.CompareTo((double)otherInt64);
if (otherType == CompareType.Double) return thisDouble.CompareTo(otherDouble); if (otherType == CompareType.Double) return thisDouble.CompareTo(otherDouble);
} }
// otherwise, compare as strings // otherwise, compare as strings
#if !DNXCORE50
return StringComparer.InvariantCulture.Compare((string)this, (string)other); return StringComparer.InvariantCulture.Compare((string)this, (string)other);
#else
var compareInfo = System.Globalization.CultureInfo.InvariantCulture.CompareInfo;
return compareInfo.Compare((string)this, (string)other, System.Globalization.CompareOptions.IgnoreCase);
#endif
} }
catch(Exception ex) catch(Exception ex)
{ {
...@@ -601,7 +610,7 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider) ...@@ -601,7 +610,7 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
if (conversionType== null) throw new ArgumentNullException("conversionType"); if (conversionType== null) throw new ArgumentNullException("conversionType");
if (conversionType== typeof(byte[])) return (byte[])this; if (conversionType== typeof(byte[])) return (byte[])this;
if (conversionType == typeof(RedisValue)) return this; if (conversionType == typeof(RedisValue)) return this;
switch(Type.GetTypeCode(conversionType)) switch(conversionType.GetTypeCode())
{ {
case TypeCode.Boolean: return (bool)this; case TypeCode.Boolean: return (bool)this;
case TypeCode.Byte: return (byte)this; case TypeCode.Byte: return (byte)this;
...@@ -703,5 +712,49 @@ public bool TryParse(out double val) ...@@ -703,5 +712,49 @@ public bool TryParse(out double val)
return TryParseDouble(blob, out val); return TryParseDouble(blob, out val);
} }
} }
internal static class ReflectionExtensions
{
#if DNXCORE50
internal static TypeCode GetTypeCode(this Type type)
{
if (type == null) return TypeCode.Empty;
TypeCode result;
if (typeCodeLookup.TryGetValue(type, out result)) return result;
if (type.GetTypeInfo().IsEnum)
{
type = Enum.GetUnderlyingType(type);
if (typeCodeLookup.TryGetValue(type, out result)) return result;
}
return TypeCode.Object;
}
static readonly Dictionary<Type, TypeCode> typeCodeLookup = new Dictionary<Type, TypeCode>
{
{typeof(bool), TypeCode.Boolean },
{typeof(byte), TypeCode.Byte },
{typeof(char), TypeCode.Char},
{typeof(DateTime), TypeCode.DateTime},
{typeof(decimal), TypeCode.Decimal},
{typeof(double), TypeCode.Double },
{typeof(short), TypeCode.Int16 },
{typeof(int), TypeCode.Int32 },
{typeof(long), TypeCode.Int64 },
{typeof(object), TypeCode.Object},
{typeof(sbyte), TypeCode.SByte },
{typeof(float), TypeCode.Single },
{typeof(string), TypeCode.String },
{typeof(ushort), TypeCode.UInt16 },
{typeof(uint), TypeCode.UInt32 },
{typeof(ulong), TypeCode.UInt64 },
};
#else
internal static TypeCode GetTypeCode(this Type type)
{
return type.GetTypeCode();
}
#endif
}
} }
...@@ -347,7 +347,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes ...@@ -347,7 +347,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
internal sealed class ScriptLoadProcessor : ResultProcessor<byte[]> internal sealed class ScriptLoadProcessor : ResultProcessor<byte[]>
{ {
static readonly Regex sha1 = new Regex("^[0-9a-f]{40}$", RegexOptions.Compiled | RegexOptions.IgnoreCase); static readonly Regex sha1 = new Regex("^[0-9a-f]{40}$", InternalRegexCompiledOption.Default | RegexOptions.IgnoreCase);
internal static bool IsSHA1(string script) internal static bool IsSHA1(string script)
{ {
......
...@@ -23,7 +23,7 @@ public ScriptParameters(RedisKey[] keys, RedisValue[] args) ...@@ -23,7 +23,7 @@ public ScriptParameters(RedisKey[] keys, RedisValue[] args)
} }
} }
static readonly Regex ParameterExtractor = new Regex(@"@(?<paramName> ([a-z]|_) ([a-z]|_|\d)*)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); static readonly Regex ParameterExtractor = new Regex(@"@(?<paramName> ([a-z]|_) ([a-z]|_|\d)*)", InternalRegexCompiledOption.Default | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
static string[] ExtractParameters(string script) static string[] ExtractParameters(string script)
{ {
var ps = ParameterExtractor.Matches(script); var ps = ParameterExtractor.Matches(script);
...@@ -313,7 +313,11 @@ static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool, ref Loc ...@@ -313,7 +313,11 @@ static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool, ref Loc
LocalBuilder redisKeyLoc = null; LocalBuilder redisKeyLoc = null;
var loc = il.DeclareLocal(t); var loc = il.DeclareLocal(t);
il.Emit(OpCodes.Ldarg_0); // object il.Emit(OpCodes.Ldarg_0); // object
#if !DNXCORE50
if (t.IsValueType) if (t.IsValueType)
#else
if (t.GetTypeInfo().IsValueType)
#endif
{ {
il.Emit(OpCodes.Unbox_Any, t); // T il.Emit(OpCodes.Unbox_Any, t); // T
} }
...@@ -344,7 +348,11 @@ static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool, ref Loc ...@@ -344,7 +348,11 @@ static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool, ref Loc
{ {
il.Emit(OpCodes.Dup); // RedisKey[] RedisKey[] il.Emit(OpCodes.Dup); // RedisKey[] RedisKey[]
il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisKey[] int il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisKey[] int
#if !DNXCORE50
if (t.IsValueType) if (t.IsValueType)
#else
if (t.GetTypeInfo().IsValueType)
#endif
{ {
il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisKey[] int T* il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisKey[] int T*
} }
...@@ -372,7 +380,11 @@ static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool, ref Loc ...@@ -372,7 +380,11 @@ static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool, ref Loc
{ {
il.Emit(OpCodes.Dup); // RedisKey[] RedisValue[] RedisValue[] il.Emit(OpCodes.Dup); // RedisKey[] RedisValue[] RedisValue[]
il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisValue[] RedisValue[] int il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisValue[] RedisValue[] int
#if !DNXCORE50
if (t.IsValueType) if (t.IsValueType)
#else
if (t.GetTypeInfo().IsValueType)
#endif
{ {
il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisValue[] RedisValue[] int T* il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisValue[] RedisValue[] int T*
} }
......
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
[Flags] [Flags]
internal enum UnselectableFlags internal enum UnselectableFlags
{ {
None = 0, None = 0,
RedundantMaster = 1, RedundantMaster = 1,
DidNotRespond = 2, DidNotRespond = 2,
ServerType = 4 ServerType = 4
} }
internal sealed partial class ServerEndPoint : IDisposable internal sealed partial class ServerEndPoint : IDisposable
{ {
internal volatile ServerEndPoint Master; internal volatile ServerEndPoint Master;
internal volatile ServerEndPoint[] Slaves = NoSlaves; internal volatile ServerEndPoint[] Slaves = NoSlaves;
private static readonly Regex nameSanitizer = new Regex("[^!-~]", RegexOptions.Compiled); private static readonly Regex nameSanitizer = new Regex("[^!-~]", InternalRegexCompiledOption.Default);
private static readonly ServerEndPoint[] NoSlaves = new ServerEndPoint[0]; private static readonly ServerEndPoint[] NoSlaves = new ServerEndPoint[0];
private readonly EndPoint endpoint; private readonly EndPoint endpoint;
private readonly Hashtable knownScripts = new Hashtable(StringComparer.Ordinal); private readonly Hashtable knownScripts = new Hashtable(StringComparer.Ordinal);
private readonly ConnectionMultiplexer multiplexer; private readonly ConnectionMultiplexer multiplexer;
private int databases, writeEverySeconds; private int databases, writeEverySeconds;
private PhysicalBridge interactive, subscription; private PhysicalBridge interactive, subscription;
bool isDisposed; bool isDisposed;
ServerType serverType; ServerType serverType;
private bool slaveReadOnly, isSlave; private bool slaveReadOnly, isSlave;
private volatile UnselectableFlags unselectableReasons; private volatile UnselectableFlags unselectableReasons;
private Version version; private Version version;
internal void ResetNonConnected() internal void ResetNonConnected()
{ {
var tmp = interactive; var tmp = interactive;
if (tmp != null) tmp.ResetNonConnected(); if (tmp != null) tmp.ResetNonConnected();
tmp = subscription; tmp = subscription;
if (tmp != null) tmp.ResetNonConnected(); if (tmp != null) tmp.ResetNonConnected();
} }
public ServerEndPoint(ConnectionMultiplexer multiplexer, EndPoint endpoint, TextWriter log) public ServerEndPoint(ConnectionMultiplexer multiplexer, EndPoint endpoint, TextWriter log)
{ {
this.multiplexer = multiplexer; this.multiplexer = multiplexer;
this.endpoint = endpoint; this.endpoint = endpoint;
var config = multiplexer.RawConfig; var config = multiplexer.RawConfig;
version = config.DefaultVersion; version = config.DefaultVersion;
slaveReadOnly = true; slaveReadOnly = true;
isSlave = false; isSlave = false;
databases = 0; databases = 0;
writeEverySeconds = config.KeepAlive > 0 ? config.KeepAlive : 60; writeEverySeconds = config.KeepAlive > 0 ? config.KeepAlive : 60;
interactive = CreateBridge(ConnectionType.Interactive, log); interactive = CreateBridge(ConnectionType.Interactive, log);
serverType = ServerType.Standalone; serverType = ServerType.Standalone;
// overrides for twemproxy // overrides for twemproxy
if (multiplexer.RawConfig.Proxy == Proxy.Twemproxy) if (multiplexer.RawConfig.Proxy == Proxy.Twemproxy)
{ {
databases = 1; databases = 1;
serverType = ServerType.Twemproxy; serverType = ServerType.Twemproxy;
} }
} }
public ClusterConfiguration ClusterConfiguration { get; private set; } public ClusterConfiguration ClusterConfiguration { get; private set; }
public int Databases { get { return databases; } set { SetConfig(ref databases, value); } } public int Databases { get { return databases; } set { SetConfig(ref databases, value); } }
public EndPoint EndPoint { get { return endpoint; } } public EndPoint EndPoint { get { return endpoint; } }
public bool HasDatabases { get { return serverType == ServerType.Standalone; } } public bool HasDatabases { get { return serverType == ServerType.Standalone; } }
public bool IsConnected public bool IsConnected
{ {
get get
{ {
var tmp = interactive; var tmp = interactive;
return tmp != null && tmp.IsConnected; return tmp != null && tmp.IsConnected;
} }
} }
public bool IsSlave { get { return isSlave; } set { SetConfig(ref isSlave, value); } } public bool IsSlave { get { return isSlave; } set { SetConfig(ref isSlave, value); } }
public long OperationCount public long OperationCount
{ {
get get
{ {
long total = 0; long total = 0;
var tmp = interactive; var tmp = interactive;
if (tmp != null) total += tmp.OperationCount; if (tmp != null) total += tmp.OperationCount;
tmp = subscription; tmp = subscription;
if (tmp != null) total += tmp.OperationCount; if (tmp != null) total += tmp.OperationCount;
return total; return total;
} }
} }
public bool RequiresReadMode { get { return serverType == ServerType.Cluster && IsSlave; } } public bool RequiresReadMode { get { return serverType == ServerType.Cluster && IsSlave; } }
public ServerType ServerType { get { return serverType; } set { SetConfig(ref serverType, value); } } public ServerType ServerType { get { return serverType; } set { SetConfig(ref serverType, value); } }
public bool SlaveReadOnly { get { return slaveReadOnly; } set { SetConfig(ref slaveReadOnly, value); } } public bool SlaveReadOnly { get { return slaveReadOnly; } set { SetConfig(ref slaveReadOnly, value); } }
public bool AllowSlaveWrites { get; set; } public bool AllowSlaveWrites { get; set; }
public Version Version { get { return version; } set { SetConfig(ref version, value); } } public Version Version { get { return version; } set { SetConfig(ref version, value); } }
public int WriteEverySeconds { get { return writeEverySeconds; } set { SetConfig(ref writeEverySeconds, value); } } public int WriteEverySeconds { get { return writeEverySeconds; } set { SetConfig(ref writeEverySeconds, value); } }
internal ConnectionMultiplexer Multiplexer { get { return multiplexer; } } internal ConnectionMultiplexer Multiplexer { get { return multiplexer; } }
public void ClearUnselectable(UnselectableFlags flags) public void ClearUnselectable(UnselectableFlags flags)
{ {
var oldFlags = unselectableReasons; var oldFlags = unselectableReasons;
if (oldFlags != 0) if (oldFlags != 0)
{ {
unselectableReasons &= ~flags; unselectableReasons &= ~flags;
if (unselectableReasons != oldFlags) if (unselectableReasons != oldFlags)
{ {
multiplexer.Trace(unselectableReasons == 0 ? "Now usable" : ("Now unusable: " + flags), ToString()); multiplexer.Trace(unselectableReasons == 0 ? "Now usable" : ("Now unusable: " + flags), ToString());
} }
} }
} }
public void Dispose() public void Dispose()
{ {
isDisposed = true; isDisposed = true;
var tmp = interactive; var tmp = interactive;
interactive = null; interactive = null;
if (tmp != null) tmp.Dispose(); if (tmp != null) tmp.Dispose();
tmp = subscription; tmp = subscription;
subscription = null; subscription = null;
if (tmp != null) tmp.Dispose(); if (tmp != null) tmp.Dispose();
} }
public PhysicalBridge GetBridge(ConnectionType type, bool create = true, TextWriter log = null) public PhysicalBridge GetBridge(ConnectionType type, bool create = true, TextWriter log = null)
{ {
if (isDisposed) return null; if (isDisposed) return null;
switch (type) switch (type)
{ {
case ConnectionType.Interactive: case ConnectionType.Interactive:
return interactive ?? (create ? interactive = CreateBridge(ConnectionType.Interactive, log) : null); return interactive ?? (create ? interactive = CreateBridge(ConnectionType.Interactive, log) : null);
case ConnectionType.Subscription: case ConnectionType.Subscription:
return subscription ?? (create ? subscription = CreateBridge(ConnectionType.Subscription, log) : null); return subscription ?? (create ? subscription = CreateBridge(ConnectionType.Subscription, log) : null);
} }
return null; return null;
} }
public PhysicalBridge GetBridge(RedisCommand command, bool create = true) public PhysicalBridge GetBridge(RedisCommand command, bool create = true)
{ {
if (isDisposed) return null; if (isDisposed) return null;
switch (command) switch (command)
{ {
case RedisCommand.SUBSCRIBE: case RedisCommand.SUBSCRIBE:
case RedisCommand.UNSUBSCRIBE: case RedisCommand.UNSUBSCRIBE:
case RedisCommand.PSUBSCRIBE: case RedisCommand.PSUBSCRIBE:
case RedisCommand.PUNSUBSCRIBE: case RedisCommand.PUNSUBSCRIBE:
return subscription ?? (create ? subscription = CreateBridge(ConnectionType.Subscription, null) : null); return subscription ?? (create ? subscription = CreateBridge(ConnectionType.Subscription, null) : null);
default: default:
return interactive; return interactive;
} }
} }
public RedisFeatures GetFeatures() public RedisFeatures GetFeatures()
{ {
return new RedisFeatures(version); return new RedisFeatures(version);
} }
public void SetClusterConfiguration(ClusterConfiguration configuration) public void SetClusterConfiguration(ClusterConfiguration configuration)
{ {
ClusterConfiguration = configuration; ClusterConfiguration = configuration;
if (configuration != null) if (configuration != null)
{ {
multiplexer.Trace("Updating cluster ranges..."); multiplexer.Trace("Updating cluster ranges...");
multiplexer.UpdateClusterRange(configuration); multiplexer.UpdateClusterRange(configuration);
multiplexer.Trace("Resolving genealogy..."); multiplexer.Trace("Resolving genealogy...");
var thisNode = configuration.Nodes.FirstOrDefault(x => x.EndPoint == this.EndPoint); var thisNode = configuration.Nodes.FirstOrDefault(x => x.EndPoint == this.EndPoint);
if (thisNode != null) if (thisNode != null)
{ {
List<ServerEndPoint> slaves = null; List<ServerEndPoint> slaves = null;
ServerEndPoint master = null; ServerEndPoint master = null;
foreach (var node in configuration.Nodes) foreach (var node in configuration.Nodes)
{ {
if (node.NodeId == thisNode.ParentNodeId) if (node.NodeId == thisNode.ParentNodeId)
{ {
master = multiplexer.GetServerEndPoint(node.EndPoint); master = multiplexer.GetServerEndPoint(node.EndPoint);
} }
else if (node.ParentNodeId == thisNode.NodeId) else if (node.ParentNodeId == thisNode.NodeId)
{ {
if (slaves == null) slaves = new List<ServerEndPoint>(); if (slaves == null) slaves = new List<ServerEndPoint>();
slaves.Add(multiplexer.GetServerEndPoint(node.EndPoint)); slaves.Add(multiplexer.GetServerEndPoint(node.EndPoint));
} }
} }
Master = master; Master = master;
Slaves = slaves == null ? NoSlaves : slaves.ToArray(); Slaves = slaves == null ? NoSlaves : slaves.ToArray();
} }
multiplexer.Trace("Cluster configured"); multiplexer.Trace("Cluster configured");
} }
} }
public void SetUnselectable(UnselectableFlags flags) public void SetUnselectable(UnselectableFlags flags)
{ {
if (flags != 0) if (flags != 0)
{ {
var oldFlags = unselectableReasons; var oldFlags = unselectableReasons;
unselectableReasons |= flags; unselectableReasons |= flags;
if (unselectableReasons != oldFlags) if (unselectableReasons != oldFlags)
{ {
multiplexer.Trace(unselectableReasons == 0 ? "Now usable" : ("Now unusable: " + flags), ToString()); multiplexer.Trace(unselectableReasons == 0 ? "Now usable" : ("Now unusable: " + flags), ToString());
} }
} }
} }
public override string ToString() public override string ToString()
{ {
return Format.ToString(EndPoint); return Format.ToString(EndPoint);
} }
public bool TryEnqueue(Message message) public bool TryEnqueue(Message message)
{ {
var bridge = GetBridge(message.Command); var bridge = GetBridge(message.Command);
return bridge != null && bridge.TryEnqueue(message, isSlave); return bridge != null && bridge.TryEnqueue(message, isSlave);
} }
internal void Activate(ConnectionType type, TextWriter log) internal void Activate(ConnectionType type, TextWriter log)
{ {
GetBridge(type, true, log); GetBridge(type, true, log);
} }
internal void AddScript(string script, byte[] hash) internal void AddScript(string script, byte[] hash)
{ {
lock (knownScripts) lock (knownScripts)
{ {
knownScripts[script] = hash; knownScripts[script] = hash;
} }
} }
internal void AutoConfigure(PhysicalConnection connection) internal void AutoConfigure(PhysicalConnection connection)
{ {
if (serverType == ServerType.Twemproxy) if (serverType == ServerType.Twemproxy)
{ {
// don't try to detect configuration; all the config commands are disabled, and // don't try to detect configuration; all the config commands are disabled, and
// the fallback master/slave detection won't help // the fallback master/slave detection won't help
return; return;
} }
var commandMap = multiplexer.CommandMap; var commandMap = multiplexer.CommandMap;
const CommandFlags flags = CommandFlags.FireAndForget | CommandFlags.HighPriority | CommandFlags.NoRedirect; const CommandFlags flags = CommandFlags.FireAndForget | CommandFlags.HighPriority | CommandFlags.NoRedirect;
var features = GetFeatures(); var features = GetFeatures();
Message msg; Message msg;
if (commandMap.IsAvailable(RedisCommand.CONFIG)) if (commandMap.IsAvailable(RedisCommand.CONFIG))
{ {
if (multiplexer.RawConfig.KeepAlive <= 0) if (multiplexer.RawConfig.KeepAlive <= 0)
{ {
msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.timeout); msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.timeout);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
} }
msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.slave_read_only); msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.slave_read_only);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.databases); msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.databases);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
} }
if (commandMap.IsAvailable(RedisCommand.INFO)) if (commandMap.IsAvailable(RedisCommand.INFO))
{ {
lastInfoReplicationCheckTicks = Environment.TickCount; lastInfoReplicationCheckTicks = Environment.TickCount;
if (features.InfoSections) if (features.InfoSections)
{ {
msg = Message.Create(-1, flags, RedisCommand.INFO, RedisLiterals.replication); msg = Message.Create(-1, flags, RedisCommand.INFO, RedisLiterals.replication);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
msg = Message.Create(-1, flags, RedisCommand.INFO, RedisLiterals.server); msg = Message.Create(-1, flags, RedisCommand.INFO, RedisLiterals.server);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
} }
else else
{ {
msg = Message.Create(-1, flags, RedisCommand.INFO); msg = Message.Create(-1, flags, RedisCommand.INFO);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
} }
} }
else if (commandMap.IsAvailable(RedisCommand.SET)) else if (commandMap.IsAvailable(RedisCommand.SET))
{ {
// this is a nasty way to find if we are a slave, and it will only work on up-level servers, but... // this is a nasty way to find if we are a slave, and it will only work on up-level servers, but...
RedisKey key = Guid.NewGuid().ToByteArray(); RedisKey key = Guid.NewGuid().ToByteArray();
msg = Message.Create(0, flags, RedisCommand.SET, key, RedisLiterals.slave_read_only, RedisLiterals.PX, 1, RedisLiterals.NX); msg = Message.Create(0, flags, RedisCommand.SET, key, RedisLiterals.slave_read_only, RedisLiterals.PX, 1, RedisLiterals.NX);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
} }
if (commandMap.IsAvailable(RedisCommand.CLUSTER)) if (commandMap.IsAvailable(RedisCommand.CLUSTER))
{ {
msg = Message.Create(-1, flags, RedisCommand.CLUSTER, RedisLiterals.NODES); msg = Message.Create(-1, flags, RedisCommand.CLUSTER, RedisLiterals.NODES);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.ClusterNodes); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.ClusterNodes);
} }
} }
internal Task Close() internal Task Close()
{ {
var tmp = interactive; var tmp = interactive;
Task result; Task result;
if (tmp == null || !tmp.IsConnected || !multiplexer.CommandMap.IsAvailable(RedisCommand.QUIT)) if (tmp == null || !tmp.IsConnected || !multiplexer.CommandMap.IsAvailable(RedisCommand.QUIT))
{ {
result = CompletedTask<bool>.Default(null); result = CompletedTask<bool>.Default(null);
} }
else else
{ {
result = QueueDirectAsync(Message.Create(-1, CommandFlags.None, RedisCommand.QUIT), ResultProcessor.DemandOK, bridge: interactive); result = QueueDirectAsync(Message.Create(-1, CommandFlags.None, RedisCommand.QUIT), ResultProcessor.DemandOK, bridge: interactive);
} }
return result; return result;
} }
internal void FlushScriptCache() internal void FlushScriptCache()
{ {
lock (knownScripts) lock (knownScripts)
{ {
knownScripts.Clear(); knownScripts.Clear();
} }
} }
private string runId; private string runId;
internal string RunId internal string RunId
{ {
get { return runId; } get { return runId; }
set set
{ {
if (value != runId) // we only care about changes if (value != runId) // we only care about changes
{ {
// if we had an old run-id, and it has changed, then the // if we had an old run-id, and it has changed, then the
// server has been restarted; which means the script cache // server has been restarted; which means the script cache
// is toast // is toast
if (runId != null) FlushScriptCache(); if (runId != null) FlushScriptCache();
runId = value; runId = value;
} }
} }
} }
internal ServerCounters GetCounters() internal ServerCounters GetCounters()
{ {
var counters = new ServerCounters(endpoint); var counters = new ServerCounters(endpoint);
var tmp = interactive; var tmp = interactive;
if (tmp != null) tmp.GetCounters(counters.Interactive); if (tmp != null) tmp.GetCounters(counters.Interactive);
tmp = subscription; tmp = subscription;
if (tmp != null) tmp.GetCounters(counters.Subscription); if (tmp != null) tmp.GetCounters(counters.Subscription);
return counters; return counters;
} }
internal int GetOutstandingCount(RedisCommand command, out int inst, out int qu, out int qs, out int qc, out int wr, out int wq, out int @in, out int ar) internal int GetOutstandingCount(RedisCommand command, out int inst, out int qu, out int qs, out int qc, out int wr, out int wq, out int @in, out int ar)
{ {
var bridge = GetBridge(command, false); var bridge = GetBridge(command, false);
if (bridge == null) if (bridge == null)
{ {
return inst = qu = qs = qc = wr = wq = @in = ar = 0; return inst = qu = qs = qc = wr = wq = @in = ar = 0;
} }
return bridge.GetOutstandingCount(out inst, out qu, out qs, out qc, out wr, out wq, out @in, out ar); return bridge.GetOutstandingCount(out inst, out qu, out qs, out qc, out wr, out wq, out @in, out ar);
} }
internal string GetProfile() internal string GetProfile()
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append("Circular op-count snapshot; int:"); sb.Append("Circular op-count snapshot; int:");
var tmp = interactive; var tmp = interactive;
if (tmp != null) tmp.AppendProfile(sb); if (tmp != null) tmp.AppendProfile(sb);
sb.Append("; sub:"); sb.Append("; sub:");
tmp = subscription; tmp = subscription;
if (tmp != null) tmp.AppendProfile(sb); if (tmp != null) tmp.AppendProfile(sb);
return sb.ToString(); return sb.ToString();
} }
internal byte[] GetScriptHash(string script, RedisCommand command) internal byte[] GetScriptHash(string script, RedisCommand command)
{ {
var found = (byte[])knownScripts[script]; var found = (byte[])knownScripts[script];
if(found == null && command == RedisCommand.EVALSHA) if(found == null && command == RedisCommand.EVALSHA)
{ {
// the script provided is a hex sha; store and re-use the ascii for that // the script provided is a hex sha; store and re-use the ascii for that
found = Encoding.ASCII.GetBytes(script); found = Encoding.ASCII.GetBytes(script);
lock(knownScripts) lock(knownScripts)
{ {
knownScripts[script] = found; knownScripts[script] = found;
} }
} }
return found; return found;
} }
internal string GetStormLog(RedisCommand command) internal string GetStormLog(RedisCommand command)
{ {
var bridge = GetBridge(command); var bridge = GetBridge(command);
return bridge == null ? null : bridge.GetStormLog(); return bridge == null ? null : bridge.GetStormLog();
} }
internal Message GetTracerMessage(bool assertIdentity) internal Message GetTracerMessage(bool assertIdentity)
{ {
// different configurations block certain commands, as can ad-hoc local configurations, so // different configurations block certain commands, as can ad-hoc local configurations, so
// we'll do the best with what we have available. // we'll do the best with what we have available.
// note that the muxer-ctor asserts that one of ECHO, PING, TIME of GET is available // note that the muxer-ctor asserts that one of ECHO, PING, TIME of GET is available
// see also: TracerProcessor // see also: TracerProcessor
var map = multiplexer.CommandMap; var map = multiplexer.CommandMap;
Message msg; Message msg;
const CommandFlags flags = CommandFlags.NoRedirect | CommandFlags.FireAndForget; const CommandFlags flags = CommandFlags.NoRedirect | CommandFlags.FireAndForget;
if (assertIdentity && map.IsAvailable(RedisCommand.ECHO)) if (assertIdentity && map.IsAvailable(RedisCommand.ECHO))
{ {
msg = Message.Create(-1, flags, RedisCommand.ECHO, (RedisValue)multiplexer.UniqueId); msg = Message.Create(-1, flags, RedisCommand.ECHO, (RedisValue)multiplexer.UniqueId);
} }
else if (map.IsAvailable(RedisCommand.PING)) else if (map.IsAvailable(RedisCommand.PING))
{ {
msg = Message.Create(-1, flags, RedisCommand.PING); msg = Message.Create(-1, flags, RedisCommand.PING);
} }
else if (map.IsAvailable(RedisCommand.TIME)) else if (map.IsAvailable(RedisCommand.TIME))
{ {
msg = Message.Create(-1, flags, RedisCommand.TIME); msg = Message.Create(-1, flags, RedisCommand.TIME);
} }
else if (!assertIdentity && map.IsAvailable(RedisCommand.ECHO)) else if (!assertIdentity && map.IsAvailable(RedisCommand.ECHO))
{ {
// we'll use echo as a PING substitute if it is all we have (in preference to EXISTS) // we'll use echo as a PING substitute if it is all we have (in preference to EXISTS)
msg = Message.Create(-1, flags, RedisCommand.ECHO, (RedisValue)multiplexer.UniqueId); msg = Message.Create(-1, flags, RedisCommand.ECHO, (RedisValue)multiplexer.UniqueId);
} }
else else
{ {
map.AssertAvailable(RedisCommand.EXISTS); map.AssertAvailable(RedisCommand.EXISTS);
msg = Message.Create(0, flags, RedisCommand.EXISTS, (RedisValue)multiplexer.UniqueId); msg = Message.Create(0, flags, RedisCommand.EXISTS, (RedisValue)multiplexer.UniqueId);
} }
msg.SetInternalCall(); msg.SetInternalCall();
return msg; return msg;
} }
internal bool IsSelectable(RedisCommand command) internal bool IsSelectable(RedisCommand command)
{ {
var bridge = unselectableReasons == 0 ? GetBridge(command, false) : null; var bridge = unselectableReasons == 0 ? GetBridge(command, false) : null;
return bridge != null && bridge.IsConnected; return bridge != null && bridge.IsConnected;
} }
internal void OnEstablishing(PhysicalConnection connection, TextWriter log) internal void OnEstablishing(PhysicalConnection connection, TextWriter log)
{ {
try try
{ {
if (connection == null) return; if (connection == null) return;
Handshake(connection, log); Handshake(connection, log);
} }
catch (Exception ex) catch (Exception ex)
{ {
connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex); connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
} }
} }
internal void OnFullyEstablished(PhysicalConnection connection) internal void OnFullyEstablished(PhysicalConnection connection)
{ {
try try
{ {
if (connection == null) return; if (connection == null) return;
var bridge = connection.Bridge; var bridge = connection.Bridge;
if (bridge == subscription) if (bridge == subscription)
{ {
multiplexer.ResendSubscriptions(this); multiplexer.ResendSubscriptions(this);
} }
multiplexer.OnConnectionRestored(endpoint, bridge.ConnectionType); multiplexer.OnConnectionRestored(endpoint, bridge.ConnectionType);
} }
catch (Exception ex) catch (Exception ex)
{ {
connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex); connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
} }
} }
internal int LastInfoReplicationCheckSecondsAgo internal int LastInfoReplicationCheckSecondsAgo
{ {
get { return unchecked(Environment.TickCount - Thread.VolatileRead(ref lastInfoReplicationCheckTicks)) / 1000; } get { return unchecked(Environment.TickCount - VolatileWrapper.Read(ref lastInfoReplicationCheckTicks)) / 1000; }
} }
private EndPoint masterEndPoint; private EndPoint masterEndPoint;
public EndPoint MasterEndPoint public EndPoint MasterEndPoint
{ {
get { return masterEndPoint; } get { return masterEndPoint; }
set { SetConfig(ref masterEndPoint, value); } set { SetConfig(ref masterEndPoint, value); }
} }
internal bool CheckInfoReplication() internal bool CheckInfoReplication()
{ {
lastInfoReplicationCheckTicks = Environment.TickCount; lastInfoReplicationCheckTicks = Environment.TickCount;
PhysicalBridge bridge; PhysicalBridge bridge;
if (version >= RedisFeatures.v2_8_0 && multiplexer.CommandMap.IsAvailable(RedisCommand.INFO) if (version >= RedisFeatures.v2_8_0 && multiplexer.CommandMap.IsAvailable(RedisCommand.INFO)
&& (bridge = GetBridge(ConnectionType.Interactive, false)) != null) && (bridge = GetBridge(ConnectionType.Interactive, false)) != null)
{ {
var msg = Message.Create(-1, CommandFlags.FireAndForget | CommandFlags.HighPriority | CommandFlags.NoRedirect, RedisCommand.INFO, RedisLiterals.replication); var msg = Message.Create(-1, CommandFlags.FireAndForget | CommandFlags.HighPriority | CommandFlags.NoRedirect, RedisCommand.INFO, RedisLiterals.replication);
msg.SetInternalCall(); msg.SetInternalCall();
QueueDirectFireAndForget(msg, ResultProcessor.AutoConfigure, bridge); QueueDirectFireAndForget(msg, ResultProcessor.AutoConfigure, bridge);
return true; return true;
} }
return false; return false;
} }
private int lastInfoReplicationCheckTicks; private int lastInfoReplicationCheckTicks;
internal void OnHeartbeat() internal void OnHeartbeat()
{ {
try try
{ {
var tmp = interactive; var tmp = interactive;
if (tmp != null) tmp.OnHeartbeat(false); if (tmp != null) tmp.OnHeartbeat(false);
tmp = subscription; tmp = subscription;
if (tmp != null) tmp.OnHeartbeat(false); if (tmp != null) tmp.OnHeartbeat(false);
} catch(Exception ex) } catch(Exception ex)
{ {
multiplexer.OnInternalError(ex, EndPoint); multiplexer.OnInternalError(ex, EndPoint);
} }
} }
internal Task<T> QueueDirectAsync<T>(Message message, ResultProcessor<T> processor, object asyncState = null, PhysicalBridge bridge = null) internal Task<T> QueueDirectAsync<T>(Message message, ResultProcessor<T> processor, object asyncState = null, PhysicalBridge bridge = null)
{ {
var tcs = TaskSource.CreateDenyExecSync<T>(asyncState); var tcs = TaskSource.CreateDenyExecSync<T>(asyncState);
var source = ResultBox<T>.Get(tcs); var source = ResultBox<T>.Get(tcs);
message.SetSource(processor, source); message.SetSource(processor, source);
if (bridge == null) bridge = GetBridge(message.Command); if (bridge == null) bridge = GetBridge(message.Command);
if (!bridge.TryEnqueue(message, isSlave)) if (!bridge.TryEnqueue(message, isSlave))
{ {
ConnectionMultiplexer.ThrowFailed(tcs, ExceptionFactory.NoConnectionAvailable(multiplexer.IncludeDetailInExceptions, message.Command, message, this)); ConnectionMultiplexer.ThrowFailed(tcs, ExceptionFactory.NoConnectionAvailable(multiplexer.IncludeDetailInExceptions, message.Command, message, this));
} }
return tcs.Task; return tcs.Task;
} }
internal void QueueDirectFireAndForget<T>(Message message, ResultProcessor<T> processor, PhysicalBridge bridge = null) internal void QueueDirectFireAndForget<T>(Message message, ResultProcessor<T> processor, PhysicalBridge bridge = null)
{ {
if (message != null) if (message != null)
{ {
message.SetSource(processor, null); message.SetSource(processor, null);
multiplexer.Trace("Enqueue: " + message); multiplexer.Trace("Enqueue: " + message);
(bridge ?? GetBridge(message.Command)).TryEnqueue(message, isSlave); (bridge ?? GetBridge(message.Command)).TryEnqueue(message, isSlave);
} }
} }
internal void ReportNextFailure() internal void ReportNextFailure()
{ {
var tmp = interactive; var tmp = interactive;
if (tmp != null) tmp.ReportNextFailure(); if (tmp != null) tmp.ReportNextFailure();
tmp = subscription; tmp = subscription;
if (tmp != null) tmp.ReportNextFailure(); if (tmp != null) tmp.ReportNextFailure();
} }
internal Task<bool> SendTracer(TextWriter log = null) internal Task<bool> SendTracer(TextWriter log = null)
{ {
var msg = GetTracerMessage(false); var msg = GetTracerMessage(false);
msg = LoggingMessage.Create(log, msg); msg = LoggingMessage.Create(log, msg);
return QueueDirectAsync(msg, ResultProcessor.Tracer); return QueueDirectAsync(msg, ResultProcessor.Tracer);
} }
internal string Summary() internal string Summary()
{ {
var sb = new StringBuilder(Format.ToString(endpoint)) var sb = new StringBuilder(Format.ToString(endpoint))
.Append(": ").Append(serverType).Append(" v").Append(version).Append(", ").Append(isSlave ? "slave" : "master"); .Append(": ").Append(serverType).Append(" v").Append(version).Append(", ").Append(isSlave ? "slave" : "master");
if (databases > 0) sb.Append("; ").Append(databases).Append(" databases"); if (databases > 0) sb.Append("; ").Append(databases).Append(" databases");
if (writeEverySeconds > 0) if (writeEverySeconds > 0)
sb.Append("; keep-alive: ").Append(TimeSpan.FromSeconds(writeEverySeconds)); sb.Append("; keep-alive: ").Append(TimeSpan.FromSeconds(writeEverySeconds));
var tmp = interactive; var tmp = interactive;
sb.Append("; int: ").Append(tmp == null ? "n/a" : tmp.ConnectionState.ToString()); sb.Append("; int: ").Append(tmp == null ? "n/a" : tmp.ConnectionState.ToString());
tmp = subscription; tmp = subscription;
if(tmp == null) if(tmp == null)
{ {
sb.Append("; sub: n/a"); sb.Append("; sub: n/a");
} else } else
{ {
var state = tmp.ConnectionState; var state = tmp.ConnectionState;
sb.Append("; sub: ").Append(state); sb.Append("; sub: ").Append(state);
if(state == PhysicalBridge.State.ConnectedEstablished) if(state == PhysicalBridge.State.ConnectedEstablished)
{ {
sb.Append(", ").Append(tmp.SubscriptionCount).Append(" active"); sb.Append(", ").Append(tmp.SubscriptionCount).Append(" active");
} }
} }
var flags = unselectableReasons; var flags = unselectableReasons;
if (flags != 0) if (flags != 0)
{ {
sb.Append("; not in use: ").Append(flags); sb.Append("; not in use: ").Append(flags);
} }
return sb.ToString(); return sb.ToString();
} }
internal void WriteDirectOrQueueFireAndForget<T>(PhysicalConnection connection, Message message, ResultProcessor<T> processor) internal void WriteDirectOrQueueFireAndForget<T>(PhysicalConnection connection, Message message, ResultProcessor<T> processor)
{ {
if (message != null) if (message != null)
{ {
message.SetSource(processor, null); message.SetSource(processor, null);
if (connection == null) if (connection == null)
{ {
multiplexer.Trace("Enqueue: " + message); multiplexer.Trace("Enqueue: " + message);
GetBridge(message.Command).TryEnqueue(message, isSlave); GetBridge(message.Command).TryEnqueue(message, isSlave);
} }
else else
{ {
multiplexer.Trace("Writing direct: " + message); multiplexer.Trace("Writing direct: " + message);
connection.Bridge.WriteMessageDirect(connection, message); connection.Bridge.WriteMessageDirect(connection, message);
} }
} }
} }
private PhysicalBridge CreateBridge(ConnectionType type, TextWriter log) private PhysicalBridge CreateBridge(ConnectionType type, TextWriter log)
{ {
multiplexer.Trace(type.ToString()); multiplexer.Trace(type.ToString());
var bridge = new PhysicalBridge(this, type); var bridge = new PhysicalBridge(this, type);
bridge.TryConnect(log); bridge.TryConnect(log);
return bridge; return bridge;
} }
void Handshake(PhysicalConnection connection, TextWriter log) void Handshake(PhysicalConnection connection, TextWriter log)
{ {
multiplexer.LogLocked(log, "Server handshake"); multiplexer.LogLocked(log, "Server handshake");
if (connection == null) if (connection == null)
{ {
multiplexer.Trace("No connection!?"); multiplexer.Trace("No connection!?");
return; return;
} }
Message msg; Message msg;
string password = multiplexer.RawConfig.Password; string password = multiplexer.RawConfig.Password;
if (!string.IsNullOrWhiteSpace(password)) if (!string.IsNullOrWhiteSpace(password))
{ {
multiplexer.LogLocked(log, "Authenticating (password)"); multiplexer.LogLocked(log, "Authenticating (password)");
msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.AUTH, (RedisValue)password); msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.AUTH, (RedisValue)password);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.DemandOK); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.DemandOK);
} }
if (multiplexer.CommandMap.IsAvailable(RedisCommand.CLIENT)) if (multiplexer.CommandMap.IsAvailable(RedisCommand.CLIENT))
{ {
string name = multiplexer.ClientName; string name = multiplexer.ClientName;
if (!string.IsNullOrWhiteSpace(name)) if (!string.IsNullOrWhiteSpace(name))
{ {
name = nameSanitizer.Replace(name, ""); name = nameSanitizer.Replace(name, "");
if (!string.IsNullOrWhiteSpace(name)) if (!string.IsNullOrWhiteSpace(name))
{ {
multiplexer.LogLocked(log, "Setting client name: {0}", name); multiplexer.LogLocked(log, "Setting client name: {0}", name);
msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.CLIENT, RedisLiterals.SETNAME, (RedisValue)name); msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.CLIENT, RedisLiterals.SETNAME, (RedisValue)name);
msg.SetInternalCall(); msg.SetInternalCall();
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.DemandOK); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.DemandOK);
} }
} }
} }
var connType = connection.Bridge.ConnectionType; var connType = connection.Bridge.ConnectionType;
if (connType == ConnectionType.Interactive) if (connType == ConnectionType.Interactive)
{ {
multiplexer.LogLocked(log, "Auto-configure..."); multiplexer.LogLocked(log, "Auto-configure...");
AutoConfigure(connection); AutoConfigure(connection);
} }
multiplexer.LogLocked(log, "Sending critical tracer: {0}", connection.Bridge); multiplexer.LogLocked(log, "Sending critical tracer: {0}", connection.Bridge);
var tracer = GetTracerMessage(true); var tracer = GetTracerMessage(true);
tracer = LoggingMessage.Create(log, tracer); tracer = LoggingMessage.Create(log, tracer);
WriteDirectOrQueueFireAndForget(connection, tracer, ResultProcessor.EstablishConnection); WriteDirectOrQueueFireAndForget(connection, tracer, ResultProcessor.EstablishConnection);
// note: this **must** be the last thing on the subscription handshake, because after this // note: this **must** be the last thing on the subscription handshake, because after this
// we will be in subscriber mode: regular commands cannot be sent // we will be in subscriber mode: regular commands cannot be sent
if (connType == ConnectionType.Subscription) if (connType == ConnectionType.Subscription)
{ {
var configChannel = multiplexer.ConfigurationChangedChannel; var configChannel = multiplexer.ConfigurationChangedChannel;
if(configChannel != null) if(configChannel != null)
{ {
msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.SUBSCRIBE, (RedisChannel)configChannel); msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.SUBSCRIBE, (RedisChannel)configChannel);
WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.TrackSubscriptions); WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.TrackSubscriptions);
} }
} }
multiplexer.LogLocked(log, "Flushing outbound buffer"); multiplexer.LogLocked(log, "Flushing outbound buffer");
connection.Flush(); connection.Flush();
} }
private void SetConfig<T>(ref T field, T value, [CallerMemberName] string caller = null) private void SetConfig<T>(ref T field, T value, [CallerMemberName] string caller = null)
{ {
if(!EqualityComparer<T>.Default.Equals(field, value)) if(!EqualityComparer<T>.Default.Equals(field, value))
{ {
multiplexer.Trace(caller + " changed from " + field + " to " + value, "Configuration"); multiplexer.Trace(caller + " changed from " + field + " to " + value, "Configuration");
field = value; field = value;
multiplexer.ReconfigureIfNeeded(endpoint, false, caller); multiplexer.ReconfigureIfNeeded(endpoint, false, caller);
} }
} }
} }
} }
#if __MonoCS__ #if !FEATURE_SOCKET_MODE_POLL
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
......
using System; #if FEATURE_SOCKET_MODE_POLL
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
#if !__MonoCS__
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
...@@ -375,11 +375,11 @@ private void ReadImpl() ...@@ -375,11 +375,11 @@ private void ReadImpl()
} }
private void StartReader() private void StartReader()
{ {
var thread = new Thread(read, 32 * 1024); // don't need a huge stack var thread = new Thread(read, 32 * 1024); // don't need a huge stack
thread.Priority = ThreadPriority.AboveNormal; // time critical
thread.Name = name + ":Read"; thread.Name = name + ":Read";
thread.IsBackground = true; thread.IsBackground = true;
thread.Priority = ThreadPriority.AboveNormal; // time critical
thread.Start(this); thread.Start(this);
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
......
...@@ -126,8 +126,12 @@ public SocketManager(string name = null) ...@@ -126,8 +126,12 @@ public SocketManager(string name = null)
// we need a dedicated writer, because when under heavy ambient load // we need a dedicated writer, because when under heavy ambient load
// (a busy asp.net site, for example), workers are not reliable enough // (a busy asp.net site, for example), workers are not reliable enough
#if !DNXCORE50
Thread dedicatedWriter = new Thread(writeAllQueues, 32 * 1024); // don't need a huge stack; Thread dedicatedWriter = new Thread(writeAllQueues, 32 * 1024); // don't need a huge stack;
dedicatedWriter.Priority = ThreadPriority.AboveNormal; // time critical dedicatedWriter.Priority = ThreadPriority.AboveNormal; // time critical
#else
Thread dedicatedWriter = new Thread(writeAllQueues);
#endif
dedicatedWriter.Name = name + ":Write"; dedicatedWriter.Name = name + ":Write";
dedicatedWriter.IsBackground = true; // should not keep process alive dedicatedWriter.IsBackground = true; // should not keep process alive
dedicatedWriter.Start(this); // will self-exit when disposed dedicatedWriter.Start(this); // will self-exit when disposed
...@@ -217,6 +221,7 @@ internal void SetFastLoopbackOption(Socket socket) ...@@ -217,6 +221,7 @@ internal void SetFastLoopbackOption(Socket socket)
// SIO_LOOPBACK_FAST_PATH (http://msdn.microsoft.com/en-us/library/windows/desktop/jj841212%28v=vs.85%29.aspx) // SIO_LOOPBACK_FAST_PATH (http://msdn.microsoft.com/en-us/library/windows/desktop/jj841212%28v=vs.85%29.aspx)
// Speeds up localhost operations significantly. OK to apply to a socket that will not be hooked up to localhost, // Speeds up localhost operations significantly. OK to apply to a socket that will not be hooked up to localhost,
// or will be subject to WFP filtering. // or will be subject to WFP filtering.
#if !DNXCORE50
const int SIO_LOOPBACK_FAST_PATH = -1744830448; const int SIO_LOOPBACK_FAST_PATH = -1744830448;
// windows only // windows only
...@@ -230,6 +235,7 @@ internal void SetFastLoopbackOption(Socket socket) ...@@ -230,6 +235,7 @@ internal void SetFastLoopbackOption(Socket socket)
socket.IOControl(SIO_LOOPBACK_FAST_PATH, optionInValue, null); socket.IOControl(SIO_LOOPBACK_FAST_PATH, optionInValue, null);
} }
} }
#endif
} }
internal void RequestWrite(PhysicalBridge bridge, bool forced) internal void RequestWrite(PhysicalBridge bridge, bool forced)
...@@ -334,7 +340,9 @@ private void Shutdown(Socket socket) ...@@ -334,7 +340,9 @@ private void Shutdown(Socket socket)
{ {
OnShutdown(socket); OnShutdown(socket);
try { socket.Shutdown(SocketShutdown.Both); } catch { } try { socket.Shutdown(SocketShutdown.Both); } catch { }
#if !DNXCORE50
try { socket.Close(); } catch { } try { socket.Close(); } catch { }
#endif
try { socket.Dispose(); } catch { } try { socket.Dispose(); } catch { }
} }
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
/// <summary> /// <summary>
/// Describes a sorted-set element with the corresponding value /// Describes a sorted-set element with the corresponding value
/// </summary> /// </summary>
public struct SortedSetEntry : IEquatable<SortedSetEntry>, IComparable, IComparable<SortedSetEntry> public struct SortedSetEntry : IEquatable<SortedSetEntry>, IComparable, IComparable<SortedSetEntry>
{ {
internal readonly RedisValue element; internal readonly RedisValue element;
internal readonly double score; internal readonly double score;
/// <summary> /// <summary>
/// Initializes a SortedSetEntry value /// Initializes a SortedSetEntry value
/// </summary> /// </summary>
public SortedSetEntry(RedisValue element, double score) public SortedSetEntry(RedisValue element, double score)
{ {
this.element = element; this.element = element;
this.score = score; this.score = score;
} }
/// <summary> /// <summary>
/// The unique element stored in the sorted set /// The unique element stored in the sorted set
/// </summary> /// </summary>
public RedisValue Element { get { return element; } } public RedisValue Element { get { return element; } }
/// <summary> /// <summary>
/// The score against the element /// The score against the element
/// </summary> /// </summary>
public double Score { get { return score; } } public double Score { get { return score; } }
/// <summary> /// <summary>
/// The score against the element /// The score against the element
/// </summary> /// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Obsolete("Please use Score", false)] #if !DNXCORE50
public double Value { get { return score; } } [Browsable(false)]
#endif
/// <summary> [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Please use Score", false)]
/// The unique element stored in the sorted set public double Value { get { return score; } }
/// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Obsolete("Please use Element", false)] /// <summary>
public RedisValue Key { get { return element; } } /// The unique element stored in the sorted set
/// </summary>
/// <summary> #if !DNXCORE50
/// Converts to a key/value pair [Browsable(false)]
/// </summary> #endif
public static implicit operator KeyValuePair<RedisValue,double>(SortedSetEntry value) [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Please use Element", false)]
{ public RedisValue Key { get { return element; } }
return new KeyValuePair<RedisValue, double>(value.element, value.score);
} /// <summary>
/// <summary> /// Converts to a key/value pair
/// Converts from a key/value pair /// </summary>
/// </summary> public static implicit operator KeyValuePair<RedisValue,double>(SortedSetEntry value)
public static implicit operator SortedSetEntry(KeyValuePair<RedisValue, double> value) {
{ return new KeyValuePair<RedisValue, double>(value.element, value.score);
return new SortedSetEntry(value.Key, value.Value); }
} /// <summary>
/// Converts from a key/value pair
/// <summary> /// </summary>
/// See Object.ToString() public static implicit operator SortedSetEntry(KeyValuePair<RedisValue, double> value)
/// </summary> {
public override string ToString() return new SortedSetEntry(value.Key, value.Value);
{ }
return element + ": " + score;
} /// <summary>
/// <summary> /// See Object.ToString()
/// See Object.GetHashCode() /// </summary>
/// </summary> public override string ToString()
public override int GetHashCode() {
{ return element + ": " + score;
return element.GetHashCode() ^ score.GetHashCode(); }
} /// <summary>
/// <summary> /// See Object.GetHashCode()
/// Compares two values for equality /// </summary>
/// </summary> public override int GetHashCode()
public override bool Equals(object obj) {
{ return element.GetHashCode() ^ score.GetHashCode();
return obj is SortedSetEntry && Equals((SortedSetEntry)obj); }
} /// <summary>
/// Compares two values for equality
/// <summary> /// </summary>
/// Compares two values for equality public override bool Equals(object obj)
/// </summary> {
public bool Equals(SortedSetEntry value) return obj is SortedSetEntry && Equals((SortedSetEntry)obj);
{ }
return this.score == value.score && this.element == value.element;
} /// <summary>
/// Compares two values for equality
/// <summary> /// </summary>
/// Compares two values by score public bool Equals(SortedSetEntry value)
/// </summary> {
public int CompareTo(SortedSetEntry value) return this.score == value.score && this.element == value.element;
{ }
return this.score.CompareTo(value.score);
} /// <summary>
/// Compares two values by score
/// <summary> /// </summary>
/// Compares two values by score public int CompareTo(SortedSetEntry value)
/// </summary> {
public int CompareTo(object value) return this.score.CompareTo(value.score);
{ }
return value is SortedSetEntry ? CompareTo((SortedSetEntry)value) : -1;
} /// <summary>
/// Compares two values by score
/// <summary> /// </summary>
/// Compares two values for equality public int CompareTo(object value)
/// </summary> {
public static bool operator ==(SortedSetEntry x, SortedSetEntry y) return value is SortedSetEntry ? CompareTo((SortedSetEntry)value) : -1;
{ }
return x.score == y.score && x.element == y.element;
} /// <summary>
/// <summary> /// Compares two values for equality
/// Compares two values for non-equality /// </summary>
/// </summary> public static bool operator ==(SortedSetEntry x, SortedSetEntry y)
public static bool operator !=(SortedSetEntry x, SortedSetEntry y) {
{ return x.score == y.score && x.element == y.element;
return x.score != y.score || x.element != y.element; }
} /// <summary>
/// Compares two values for non-equality
} /// </summary>
} public static bool operator !=(SortedSetEntry x, SortedSetEntry y)
{
return x.score != y.score || x.element != y.element;
}
}
}
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{75CED009-AAC6-4AC1-9C38-A0530619062D}</ProjectGuid> <ProjectGuid>{75CED009-AAC6-4AC1-9C38-A0530619062D}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>StackExchange.Redis</RootNamespace> <RootNamespace>StackExchange.Redis</RootNamespace>
<AssemblyName>StackExchange.Redis.StrongName</AssemblyName> <AssemblyName>StackExchange.Redis.StrongName</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin.snk\Debug\</OutputPath> <OutputPath>bin.snk\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;NET40 STRONG_NAME</DefineConstants> <DefineConstants>TRACE;DEBUG;NET40 STRONG_NAME FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<DocumentationFile>bin.snk\Debug\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\Debug\StackExchange.Redis.StrongName.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin.snk\Release\</OutputPath> <OutputPath>bin.snk\Release\</OutputPath>
<DefineConstants>TRACE;NET40 STRONG_NAME</DefineConstants> <DefineConstants>TRACE;NET40 STRONG_NAME FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<AssemblyOriginatorKeyFile>StackExchange.Redis.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>StackExchange.Redis.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'">
<OutputPath>bin\Mono\</OutputPath> <OutputPath>bin\Mono\</OutputPath>
<DefineConstants>TRACE;NET40 STRONG_NAME</DefineConstants> <DefineConstants>TRACE;NET40 STRONG_NAME FEATURE_SERIALIZATION</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.Threading.Tasks"> <Reference Include="Microsoft.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions"> <Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop"> <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.IO"> <Reference Include="System.IO">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Net" /> <Reference Include="System.Net" />
<Reference Include="System.Runtime"> <Reference Include="System.Runtime">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Threading.Tasks"> <Reference Include="System.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\StackExchange.Redis\Properties\AssemblyInfo.cs" /> <Compile Include="..\StackExchange.Redis\Properties\AssemblyInfo.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Aggregate.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Aggregate.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Bitwise.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Bitwise.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientFlags.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientFlags.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientInfo.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientInfo.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientType.cs"> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientType.cs">
<Link>ClientType.cs</Link> <Link>ClientType.cs</Link>
</Compile> </Compile>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConcurrentProfileStorageCollection.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Compat\VolatileWrapper.cs">
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.Profiling.cs"> <Link>VolatileWrapper.cs</Link>
<DependentUpon>ConnectionMultiplexer.cs</DependentUpon> </Compile>
</Compile> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConcurrentProfileStorageCollection.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClusterConfiguration.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.Profiling.cs">
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandFlags.cs" /> <DependentUpon>ConnectionMultiplexer.cs</DependentUpon>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandMap.cs" /> </Compile>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandTrace.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClusterConfiguration.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CompletedDefaultTask.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandFlags.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CompletionManager.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandMap.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Condition.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandTrace.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConfigurationOptions.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CompletedDefaultTask.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionCounters.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CompletionManager.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionFailedEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Condition.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionFailureType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConfigurationOptions.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionCounters.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.ReaderWriter.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionFailedEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionFailureType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\DebuggingAids.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\EndPointCollection.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.ReaderWriter.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\EndPointEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExceptionFactory.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\DebuggingAids.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Exclude.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\EndPointCollection.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExportOptions.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\EndPointEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExtensionMethods.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExceptionFactory.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Format.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Exclude.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\HashEntry.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExportOptions.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\HashSlotMovedEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExtensionMethods.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IBatch.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Format.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ICompletable.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\HashEntry.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IConnectionMultiplexer.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\HashSlotMovedEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabase.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IBatch.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabaseAsync.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ICompletable.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IMultiMessage.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IConnectionMultiplexer.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalErrorEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabase.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IProfiler.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabaseAsync.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedis.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IMultiMessage.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedisAsync.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalErrorEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IServer.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalRegexCompiledOption.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ISubscriber.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IProfiler.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ITransaction.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedis.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\LoggingTextStream.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedisAsync.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Message.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IServer.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\MessageCompletable.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ISubscriber.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\MessageQueue.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ITransaction.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\MigrateOptions.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\LoggingTextStream.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ProfileContextTracker.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Message.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ProfileStorage.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\MessageCompletable.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\LuaScript.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\MessageQueue.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Order.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\MigrateOptions.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\PhysicalBridge.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ProfileContextTracker.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\PhysicalConnection.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ProfileStorage.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RawResult.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\LuaScript.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisBase.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Order.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisBatch.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\PhysicalBridge.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisChannel.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\PhysicalConnection.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisCommand.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RawResult.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisDatabase.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisBase.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisErrorEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisBatch.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisFeatures.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisChannel.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisKey.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisCommand.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisLiterals.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisDatabase.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisResult.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisErrorEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisServer.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisFeatures.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisSubscriber.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisKey.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisTransaction.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisLiterals.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisResult.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisValue.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisServer.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ReplicationChangeOptions.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisSubscriber.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultBox.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisTransaction.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultProcessor.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisValue.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ScriptParameterMapper.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ReplicationChangeOptions.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SaveType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultBox.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerCounters.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultProcessor.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerEndPoint.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerSelectionStrategy.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ScriptParameterMapper.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SaveType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SetOperation.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerCounters.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ShutdownMode.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerEndPoint.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerSelectionStrategy.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.NoPoll.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.Poll.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SetOperation.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SortedSetEntry.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ShutdownMode.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SortType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\StringSplits.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.NoPoll.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\TaskSource.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.Poll.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\When.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SortedSetEntry.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\BatchWrapper.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SortType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\DatabaseExtension.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\StringSplits.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\DatabaseWrapper.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\TaskSource.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\TransactionWrapper.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\When.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\WrapperBase.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\BatchWrapper.cs" />
</ItemGroup> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\DatabaseExtension.cs" />
<ItemGroup> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\DatabaseWrapper.cs" />
<None Include="packages.config" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\TransactionWrapper.cs" />
<None Include="StackExchange.Redis.snk" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\WrapperBase.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ItemGroup>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> <None Include="packages.config" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''"> <None Include="StackExchange.Redis.snk" />
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" /> </ItemGroup>
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Target> <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
Other similar extension points exist, see Microsoft.Common.targets. <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Target Name="BeforeBuild"> <Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target> </Target>
<Target Name="AfterBuild"> <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
</Target> Other similar extension points exist, see Microsoft.Common.targets.
--> <Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project> </Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{36CAC6B6-2B88-447F-AA35-D4DAA5E4F2C7}</ProjectGuid> <ProjectGuid>{36CAC6B6-2B88-447F-AA35-D4DAA5E4F2C7}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>StackExchange.Redis</RootNamespace> <RootNamespace>StackExchange.Redis</RootNamespace>
<AssemblyName>StackExchange.Redis</AssemblyName> <AssemblyName>StackExchange.Redis</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;NET40</DefineConstants> <DefineConstants>TRACE;DEBUG;NET40 FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<DocumentationFile>bin\Debug\StackExchange.Redis.XML</DocumentationFile> <DocumentationFile>bin\Debug\StackExchange.Redis.XML</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NET40</DefineConstants> <DefineConstants>TRACE;NET40 FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Release\StackExchange.Redis.xml</DocumentationFile> <DocumentationFile>bin\Release\StackExchange.Redis.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'">
<OutputPath>bin\Mono\</OutputPath> <OutputPath>bin\Mono\</OutputPath>
<DefineConstants>TRACE;NET40</DefineConstants> <DefineConstants>TRACE;NET40 FEATURE_SERIALIZATION</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Release\StackExchange.Redis.xml</DocumentationFile> <DocumentationFile>bin\Release\StackExchange.Redis.xml</DocumentationFile>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.Threading.Tasks"> <Reference Include="Microsoft.Threading.Tasks">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions"> <Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop"> <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.IO, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.IO, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System.Net" /> <Reference Include="System.Net" />
<Reference Include="System.Runtime, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Runtime, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System.Threading.Tasks, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Threading.Tasks, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\StackExchange.Redis\Properties\AssemblyInfo.cs" /> <Compile Include="..\StackExchange.Redis\Properties\AssemblyInfo.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Aggregate.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Aggregate.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Bitwise.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Bitwise.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientFlags.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientFlags.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientInfo.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientInfo.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientType.cs"> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientType.cs">
<Link>ClientType.cs</Link> <Link>ClientType.cs</Link>
</Compile> </Compile>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConcurrentProfileStorageCollection.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Compat\VolatileWrapper.cs">
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.Profiling.cs"> <Link>VolatileWrapper.cs</Link>
<DependentUpon>ConnectionMultiplexer.cs</DependentUpon> </Compile>
</Compile> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConcurrentProfileStorageCollection.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClusterConfiguration.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.Profiling.cs">
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandFlags.cs" /> <DependentUpon>ConnectionMultiplexer.cs</DependentUpon>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandMap.cs" /> </Compile>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandTrace.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClusterConfiguration.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CompletedDefaultTask.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandFlags.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\CompletionManager.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandMap.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Condition.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CommandTrace.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConfigurationOptions.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CompletedDefaultTask.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionCounters.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\CompletionManager.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionFailedEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Condition.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionFailureType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConfigurationOptions.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionCounters.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.ReaderWriter.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionFailedEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionFailureType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\DebuggingAids.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\EndPointCollection.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.ReaderWriter.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\EndPointEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExceptionFactory.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\DebuggingAids.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Exclude.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\EndPointCollection.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExportOptions.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\EndPointEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExtensionMethods.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExceptionFactory.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Format.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Exclude.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\HashEntry.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExportOptions.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\HashSlotMovedEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ExtensionMethods.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IBatch.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Format.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ICompletable.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\HashEntry.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IConnectionMultiplexer.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\HashSlotMovedEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabase.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IBatch.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabaseAsync.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ICompletable.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IMultiMessage.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IConnectionMultiplexer.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalErrorEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabase.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IProfiler.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabaseAsync.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedis.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IMultiMessage.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedisAsync.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalErrorEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IServer.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalRegexCompiledOption.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ISubscriber.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IProfiler.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ITransaction.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedis.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\LoggingTextStream.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedisAsync.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Message.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IServer.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\MessageCompletable.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ISubscriber.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\MessageQueue.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ITransaction.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\MigrateOptions.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\LoggingTextStream.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ProfileContextTracker.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Message.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ProfileStorage.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\MessageCompletable.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\LuaScript.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\MessageQueue.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Order.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\MigrateOptions.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\PhysicalBridge.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ProfileContextTracker.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\PhysicalConnection.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ProfileStorage.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RawResult.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\LuaScript.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisBase.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\Order.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisBatch.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\PhysicalBridge.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisChannel.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\PhysicalConnection.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisCommand.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RawResult.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisDatabase.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisBase.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisErrorEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisBatch.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisFeatures.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisChannel.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisKey.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisCommand.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisLiterals.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisDatabase.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisResult.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisErrorEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisServer.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisFeatures.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisSubscriber.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisKey.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisTransaction.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisLiterals.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisResult.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisValue.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisServer.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ReplicationChangeOptions.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisSubscriber.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultBox.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisTransaction.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultProcessor.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\RedisValue.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ScriptParameterMapper.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ReplicationChangeOptions.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SaveType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultBox.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerCounters.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultProcessor.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerEndPoint.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ResultType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerSelectionStrategy.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ScriptParameterMapper.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SaveType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SetOperation.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerCounters.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ShutdownMode.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerEndPoint.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerSelectionStrategy.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.NoPoll.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ServerType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.Poll.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SetOperation.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SortedSetEntry.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ShutdownMode.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\SortType.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\StringSplits.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.NoPoll.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\TaskSource.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SocketManager.Poll.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\When.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SortedSetEntry.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\BatchWrapper.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\SortType.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\DatabaseExtension.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\StringSplits.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\DatabaseWrapper.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\TaskSource.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\TransactionWrapper.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\When.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\WrapperBase.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\BatchWrapper.cs" />
</ItemGroup> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\DatabaseExtension.cs" />
<ItemGroup> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\DatabaseWrapper.cs" />
<None Include="app.config" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\TransactionWrapper.cs" />
<None Include="packages.config" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\KeyspaceIsolation\WrapperBase.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ItemGroup>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> <None Include="app.config" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''"> <None Include="packages.config" />
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" /> </ItemGroup>
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Target> <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
Other similar extension points exist, see Microsoft.Common.targets. <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Target Name="BeforeBuild"> <Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target> </Target>
<Target Name="AfterBuild"> <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
</Target> Other similar extension points exist, see Microsoft.Common.targets.
--> <Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project> </Project>
\ No newline at end of file
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin.snk\Debug\</OutputPath> <OutputPath>bin.snk\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;NET46 PLAT_SAFE_CONTINUATIONS STRONG_NAME</DefineConstants> <DefineConstants>TRACE;DEBUG;NET46 PLAT_SAFE_CONTINUATIONS STRONG_NAME FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin.snk\Release\</OutputPath> <OutputPath>bin.snk\Release\</OutputPath>
<DefineConstants>TRACE;NET46 PLAT_SAFE_CONTINUATIONS STRONG_NAME</DefineConstants> <DefineConstants>TRACE;NET46 PLAT_SAFE_CONTINUATIONS STRONG_NAME FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'">
<OutputPath>bin\Mono\</OutputPath> <OutputPath>bin\Mono\</OutputPath>
<DefineConstants>TRACE;NET46 PLAT_SAFE_CONTINUATIONS STRONG_NAME</DefineConstants> <DefineConstants>TRACE;NET46 PLAT_SAFE_CONTINUATIONS STRONG_NAME FEATURE_SERIALIZATION</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile> <DocumentationFile>bin.snk\Release\StackExchange.Redis.StrongName.xml</DocumentationFile>
<Optimize>true</Optimize> <Optimize>true</Optimize>
...@@ -71,6 +71,9 @@ ...@@ -71,6 +71,9 @@
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientType.cs"> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientType.cs">
<Link>ClientType.cs</Link> <Link>ClientType.cs</Link>
</Compile> </Compile>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Compat\VolatileWrapper.cs">
<Link>VolatileWrapper.cs</Link>
</Compile>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConcurrentProfileStorageCollection.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConcurrentProfileStorageCollection.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.Profiling.cs"> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.Profiling.cs">
<DependentUpon>ConnectionMultiplexer.cs</DependentUpon> <DependentUpon>ConnectionMultiplexer.cs</DependentUpon>
...@@ -106,6 +109,7 @@ ...@@ -106,6 +109,7 @@
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabaseAsync.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabaseAsync.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IMultiMessage.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IMultiMessage.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalErrorEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalErrorEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalRegexCompiledOption.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IProfiler.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IProfiler.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedis.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedis.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedisAsync.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedisAsync.cs" />
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;NET46 PLAT_SAFE_CONTINUATIONS</DefineConstants> <DefineConstants>TRACE;DEBUG;NET46 PLAT_SAFE_CONTINUATIONS FEATURE_SERIALIZATION;FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NET46 PLAT_SAFE_CONTINUATIONS</DefineConstants> <DefineConstants>TRACE;NET46 PLAT_SAFE_CONTINUATIONS FEATURE_SERIALIZATION FEATURE_SOCKET_MODE_POLL</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Mono|AnyCPU'">
<OutputPath>bin\Mono\</OutputPath> <OutputPath>bin\Mono\</OutputPath>
<DefineConstants>TRACE;NET46 PLAT_SAFE_CONTINUATIONS</DefineConstants> <DefineConstants>TRACE;NET46 PLAT_SAFE_CONTINUATIONS FEATURE_SERIALIZATION</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Release\StackExchange.Redis.xml</DocumentationFile> <DocumentationFile>bin\Release\StackExchange.Redis.xml</DocumentationFile>
<Optimize>true</Optimize> <Optimize>true</Optimize>
...@@ -65,6 +65,9 @@ ...@@ -65,6 +65,9 @@
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientType.cs"> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ClientType.cs">
<Link>ClientType.cs</Link> <Link>ClientType.cs</Link>
</Compile> </Compile>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\Compat\VolatileWrapper.cs">
<Link>VolatileWrapper.cs</Link>
</Compile>
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConcurrentProfileStorageCollection.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConcurrentProfileStorageCollection.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.Profiling.cs"> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.Profiling.cs">
<DependentUpon>ConnectionMultiplexer.cs</DependentUpon> <DependentUpon>ConnectionMultiplexer.cs</DependentUpon>
...@@ -100,6 +103,7 @@ ...@@ -100,6 +103,7 @@
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabaseAsync.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IDatabaseAsync.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IMultiMessage.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IMultiMessage.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalErrorEventArgs.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalErrorEventArgs.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\InternalRegexCompiledOption.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IProfiler.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IProfiler.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedis.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedis.cs" />
<Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedisAsync.cs" /> <Compile Include="..\StackExchange.Redis\StackExchange\Redis\IRedisAsync.cs" />
......
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>86526b5c-1163-4481-a5e2-a303a0bb1535</ProjectGuid>
<RootNamespace>StackExchange.Redis</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
\ No newline at end of file
{
"version": "1.1.0-*",
"description": "StackExchange.Redis",
"authors": [ "jeremymeng" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"compile": [
"../../StackExchange.Redis/**/*.cs"
],
"dependencies": {
},
"configurations": {
"Debug": {
"compilationOptions": {
"define": [ "DEBUG", "TRACE" ],
"allowUnsafe": true
}
},
"Release": {
"compilationOptions": {
"define": [ "TRACE" ],
"allowUnsafe": true
}
}
},
"frameworks": {
"dnxcore50": {
"dependencies": {
"System.Diagnostics.Debug": "4.0.0",
"System.Diagnostics.TraceSource": "4.0.0-beta-23409",
"System.Diagnostics.Tools": "4.0.0",
"System.IO.Compression": "4.0.0",
"System.Globalization": "4.0.10",
"System.Linq": "4.0.0",
"System.Net.Primitives": "4.0.10",
"System.Net.Sockets": "4.1.0-beta-23409",
"System.Net.Security": "4.0.0-beta-23409",
"System.Net.NameResolution": "4.0.0-beta-23409",
"System.Reflection": "4.0.0",
"System.Reflection.Emit": "4.0.0",
"System.Reflection.TypeExtensions": "4.0.0",
"System.Reflection.Primitives": "4.0.0",
"System.Reflection.Emit.Lightweight": "4.0.0",
"System.Security.Cryptography.Algorithms": "4.0.0-beta-23409",
"System.Security.Cryptography.X509Certificates": "4.0.0-beta-23409",
"System.Text.RegularExpressions": "4.0.10",
"System.Threading": "4.0.0",
"System.Threading.Thread": "4.0.0-*",
"System.Threading.ThreadPool": "4.0.10-beta-23409",
"System.Threading.Timer": "4.0.1-beta-23409",
"System.Threading.Tasks": "4.0.0"
}
}
}
}
\ No newline at end of file
{
"sdk": {
"version": "1.0.0-beta8",
"runtime": "coreclr",
"architecture": "x86"
},
"projects": [ "StackExchange.Redis_dnxcore50" ]
}
Copyright © 2002-2014 Charlie Poole
Copyright © 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov
Copyright © 2000-2002 Philip A. Craig
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment (see the following) in the product documentation is required.
Portions Copyright © 2002-2014 Charlie Poole or Copyright © 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov or Copyright © 2000-2002 Philip A. Craig
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
NUnit 3.0.0 Final Release - November 15, 2015
Issues Resolved
* 635 Mono 4.0 Support
NUnit 3.0.0 Release Candidate 3 - November 13, 2015
Engine
* The engine now only sets the config file for project.nunit to project.config if project.config exists. Otherwise, each assembly uses its own config, provided it is run in a separate AppDomain by itself.
NOTE: It is not possible for multiple assemblies in the same AppDomain to use different configs. This is not an NUnit limitation, it's just how configs work!
Issues Resolved
* 856 Extensions support for third party runners in NUnit 3.0
* 1003 Delete TeamCityEventHandler as it is not used
* 1015 Specifying .nunit project and --framework on command line causes crash
* 1017 Remove Assert.Multiple from framework
NUnit 3.0.0 Release Candidate 2 - November 8, 2015
Engine
* The IDriverFactory extensibility interface has been modified.
Issues Resolved
* 970 Define PARALLEL in CF build of nunitlite
* 978 It should be possible to determine version of NUnit using nunit console tool
* 983 Inconsistent return codes depending on ProcessModel
* 986 Update docs for parallel execution
* 988 Don't run portable tests from NUnit Console
* 990 V2 driver is passing invalid filter elements to NUnit
* 991 Mono.Options should not be exposed to public directly
* 993 Give error message when a regex filter is used with NUnit V2
* 997 Add missing XML Documentation
* 1008 NUnitLite namespace not updated in the NuGet Packages
NUnit 3.0.0 Release Candidate - November 1, 2015
Framework
* The portable build now supports ASP.NET 5 and the new Core CLR.
NOTE: The `nunit3-console` runner cannot run tests that reference the portable build.
You may run such tests using NUnitLite or a platform-specific runner.
* `TestCaseAttribute` and `TestCaseData` now allow modifying the test name without replacing it entirely.
* The Silverlight packages are now separate downloads.
NUnitLite
* The NUnitLite runner now produces the same output display and XML results as the console runner.
Engine
* The format of the XML result file has been finalized and documented.
Console Runner
* The console runner program is now called `nunit3-console`.
* Console runner output has been modified so that the summary comes at the end, to reduce the need for scrolling.
Issues Resolved
* 59 Length of generated test names should be limited
* 68 Customization of test case name generation
* 404 Split tests between nunitlite.runner and nunit.framework
* 575 Add support for ASP.NET 5 and the new Core CLR
* 783 Package separately for Silverlight
* 833 Intermittent failure of WorkItemQueueTests.StopQueue_WithWorkers
* 859 NUnit-Console output - move Test Run Summary to end
* 867 Remove Warnings from Ignored tests
* 868 Review skipped tests
* 887 Move environment and settings elements to the assembly suite in the result file
* 899 Colors for ColorConsole on grey background are too light
* 904 InternalPreserveStackTrace is not supported on all Portable platforms
* 914 Unclear error message from console runner when assembly has no tests
* 916 Console runner dies when test agent dies
* 918 Console runner --where parameter is case sensitive
* 920 Remove addins\nunit.engine.api.dll from NuGet package
* 929 Rename nunit-console.exe
* 931 Remove beta warnings from NuGet packages
* 936 Explicit skipped tests not displayed
* 939 Installer complains about .NET even if already installed
* 940 Confirm or modify list of packages for release
* 947 Breaking API change in ValueSourceAttribute
* 949 Update copyright in NUnit Console
* 954 NUnitLite XML output is not consistent with the engine's
* 955 NUnitLite does not display the where clause
* 959 Restore filter options for NUnitLite portable build
* 960 Intermittent failure of CategoryFilterTests
* 967 Run Settings Report is not being displayed.
NUnit 3.0.0 Beta 5 - October 16, 2015
Framework
* Parameterized test cases now support nullable arguments.
* The NUnit framework may now be built for the .NET Core framework. Note that this is only available through building the source code. A binary will be available in the next release.
Engine
* The engine now runs multiple test assemblies in parallel by default
* The output XML now includes more information about the test run, including the text of the command used, any engine settings and the filter used to select tests.
* Extensions may now specify data in an identifying attribute, for use by the engine in deciding whether to load that extension.
Console Runner
* The console now displays all settings used by the engine to run tests as well as the filter used to select tests.
* The console runner accepts a new option --maxagents. If multiple assemblies are run in separate processes, this value may be used to limit the number that are executed simultaneously in parallel.
* The console runner no longer accepts the --include and --exclude options. Instead, the new --where option provides a more general way to express which tests will be executed, such as --where "cat==Fast && Priority==High". See the docs for details of the syntax.
* The new --debug option causes NUnit to break in the debugger immediately before tests are run. This simplifies debugging, especially when the test is run in a separate process.
Issues Resolved
* 41 Check for zeroes in Assert messages
* 254 Finalize XML format for test results
* 275 NUnitEqualityComparer fails to compare IEquatable<T> where second object is derived from T
* 304 Run test Assemblies in parallel
* 374 New syntax for selecting tests to be run
* 515 OSPlatform.IsMacOSX doesn't work
* 573 nunit-console hangs on Mac OS X after all tests have run
* 669 TeamCity service message should have assembly name as a part of test name.
* 689 The TeamCity service message "testFinished" should have an integer value in the "duration" attribute
* 713 Include command information in XML
* 719 We have no way to configure tests for several assemblies using NUnit project file and the common installation from msi file
* 735 Workers number in xml report file cannot be found
* 784 Build Portable Framework on Linux
* 790 Allow Extensions to provide data through an attribute
* 794 Make it easier to debug tests as well as NUnit itself
* 801 NUnit calls Dispose multiple times
* 814 Support nullable types with TestCase
* 818 Possible error in Merge Pull Request #797
* 821 Wrapped method results in loss of result information
* 822 Test for Debugger in NUnitTestAssemblyRunner probably should not be in CF build
* 824 Remove unused System.Reflection using statements
* 826 Randomizer uniqueness tests fail randomly!
* 828 Merge pull request #827 (issue 826)
* 830 Add ability to report test results synchronously to test runners
* 837 Enumerators not disposed when comparing IEnumerables
* 840 Add missing copyright notices
* 844 Pull Request #835 (Issue #814) does not build in CF
* 847 Add new --process:inprocess and --inprocess options
* 850 Test runner fails if test name contains invalid xml characters
* 851 'Exclude' console option is not working in NUnit Lite
* 853 Cannot run NUnit Console from another directory
* 860 Use CDATA section for message, stack-trace and output elements of XML
* 863 Eliminate core engine
* 865 Intermittent failures of StopWatchTests
* 869 Tests that use directory separator char to determine platform misreport Linux on MaxOSX
* 870 NUnit Console Runtime Environment misreports on MacOSX
* 874 Add .NET Core Framework
* 878 Cannot exclude MacOSX or XBox platforms when running on CF
* 892 Fixed test runner returning early when executing more than one test run.
* 894 Give nunit.engine and nunit.engine.api assemblies strong names
* 896 NUnit 3.0 console runner not placing test result xml in --work directory
NUnit 3.0.0 Beta 4 - August 25, 2015
Framework
* A new RetryAttribute allows retrying of failing tests.
* New SupersetConstraint and Is.SupersetOf syntax complement SubsetConstraint.
* Tests skipped due to ExplicitAttribute are now reported as skipped.
Engine
* We now use Cecil to examine assemblies prior to loading them.
* Extensions are no longer based on Mono.Addins but use our own extension framework.
Issues Resolved
* 125 3rd-party dependencies should be downloaded on demand
* 283 What should we do when a user extension does something bad?
* 585 RetryAttribute
* 642 Restructure MSBuild script
* 649 Change how we zip packages
* 654 ReflectionOnlyLoad and ReflectionOnlyLoadFrom
* 664 Invalid "id" attribute in the report for case "test started"
* 685 In the some cases when tests cannot be started NUnit returns exit code "0"
* 728 Missing Assert.That overload
* 741 Explicit Tests get run when using --exclude
* 746 Framework should send events for all tests
* 747 NUnit should apply attributes even if test is non-runnable
* 749 Review Use of Mono.Addins for Engine Extensibility
* 750 Include Explicit Tests in Test Results
* 753 Feature request: Is.SupersetOf() assertion constraint
* 755 TimeOut attribute doesn't work with TestCaseSource Attribute
* 757 Implement some way to wait for execution to complete in ITestEngineRunner
* 760 Packaging targets do not run on Linux
* 766 Added overloads for True()/False() accepting booleans
* 778 Build and build.cmd scripts invoke nuget.exe improperly
* 780 Teamcity fix
* 782 No sources for 2.6.4
NUnit 3.0.0 Beta 3 - July 15, 2015
Framework
* The RangeAttribute has been extended to support more data types including
uint, long and ulong
* Added platform support for Windows 10 and fixed issues with Windows 8 and
8.1 support
* Added async support to the portable version of NUnit Framework
* The named members of the TestCaseSource and ValueSource attributes must now be
static.
* RandomAttribute has been extended to add support for new data types including
uint, long, ulong, short, ushort, float, byte and sbyte
* TestContext.Random has also been extended to add support for new data types including
uint, long, ulong, short, ushort, float, byte, sbyte and decimal
* Removed the dependency on Microsoft.Bcl.Async from the NUnit Framework assembly
targeting .NET 4.0. If you want to write async tests in .NET 4.0, you will need
to reference the NuGet package yourself.
* Added a new TestFixtureSource attribute which is the equivalent to TestCaseSource
but provides for instantiation of fixtures.
* Significant improvements have been made in how NUnit deduces the type arguments of
generic methods based on the arguments provided.
Engine
* If the target framework is not specified, test assemblies that are compiled
to target .NET 4.5 will no longer run in .NET 4.0 compatibility mode
Console
* If the console is run without arguments, it will now display help
Issues Resolved
* 47 Extensions to RangeAttribute
* 237 System.Uri .ctor works not properly under Nunit
* 244 NUnit should properly distinguish between .NET 4.0 and 4.5
* 310 Target framework not specified on the AppDomain when running against .Net 4.5
* 321 Rationalize how we count tests
* 472 Overflow exception and DivideByZero exception from the RangeAttribute
* 524 int and char do not compare correctly?
* 539 Truncation of string arguments
* 544 AsyncTestMethodTests for 4.5 Framework fails frequently on Travis CI
* 656 Unused parameter in Console.WriteLine found
* 670 Failing Tests in TeamCity Build
* 673 Ensure proper disposal of engine objects
* 674 Engine does not release test assemblies
* 679 Windows 10 Support
* 682 Add Async Support to Portable Framework
* 683 Make FrameworkController available in portable build
* 687 TestAgency does not launch agent process correctly if runtime type is not specified (i.e. v4.0)
* 692 PlatformAttribute_OperatingSystemBitNess fails when running in 32-bit process
* 693 Generic Test<T> Method cannot determine type arguments for fixture when passed as IEnumerable<T>
* 698 Require TestCaseSource and ValueSource named members to be static
* 703 TeamCity non-equal flowid for 'testStarted' and 'testFinished' messages
* 712 Extensions to RandomAttribute
* 715 Provide a data source attribute at TestFixture Level
* 718 RangeConstraint gives error with from and two args of differing types
* 723 Does nunit.nuspec require dependency on Microsoft.Bcl.Async?
* 724 Adds support for Nullable<bool> to Assert.IsTrue and Assert.IsFalse
* 734 Console without parameters doesn't show help
NUnit 3.0.0 Beta 2 - May 12, 2015
Framework
* The Compact Framework version of the framework is now packaged separately
and will be distributed as a ZIP file and as a NuGet package.
* The NUnit 2.x RepeatAttribute was added back into the framework.
* Added Throws.ArgumentNullException
* Added GetString methods to NUnit.Framework.Internal.RandomGenerator to
create repeatable random strings for testing
* When checking the equality of DateTimeOffset, you can now use the
WithSameOffset modifier
* Some classes intended for internal usage that were public for testing
have now been made internal. Additional classes will be made internal
for the final 3.0 release.
Engine
* Added a core engine which is a non-extensible, minimal engine for use by
devices and similar situations where reduced functionality is compensated
for by reduced size and simplicity of usage. See
https://github.com/nunit/dev/wiki/Core-Engine for more information.
Issues Resolved
* 22 Add OSArchitecture Attribute to Environment node in result xml
* 24 Assert on Dictionary Content
* 48 Explicit seems to conflict with Ignore
* 168 Create NUnit 3.0 documentation
* 196 Compare DateTimeOffsets including the offset in the comparison
* 217 New icon for the 3.0 release
* 316 NUnitLite TextUI Runner
* 320 No Tests found: Using parametrized Fixture and TestCaseSource
* 360 Better exception message when using non-BCL class in property
* 454 Rare registry configurations may cause NUnit to fail
* 478 RepeatAttribute
* 481 Testing multiple assemblies in nunitlite
* 538 Potential bug using TestContext in constructors
* 546 Enable Parallel in NUnitLite/CF (or more) builds
* 551 TextRunner not passing the NumWorkers option to the ITestAssemblyRunner
* 556 Executed tests should always return a non-zero duration
* 559 Fix text of NuGet packages
* 560 Fix PackageVersion property on wix install projects
* 562 Program.cs in NUnitLite NuGet package is incorrect
* 564 NUnitLite Nuget package is Beta 1a, Framework is Beta 1
* 565 NUnitLite Nuget package adds Program.cs to a VB Project
* 568 Isolate packaging from building
* 570 ThrowsConstraint failure message should include stack trace of actual exception
* 576 Throws.ArgumentNullException would be nice
* 577 Documentation on some members of Throws falsely claims that they return `TargetInvocationException` constraints
* 579 No documentation for recommended usage of TestCaseSourceAttribute
* 580 TeamCity Service Message Uses Incorrect Test Name with NUnit2Driver
* 582 Test Ids Are Not Unique
* 583 TeamCity service messages to support parallel test execution
* 584 Non-runnable assembly has incorrect ResultState
* 609 Add support for integration with TeamCity
* 611 Remove unused --teamcity option from CF build of NUnitLite
* 612 MaxTime doesn't work when used for TestCase
* 621 Core Engine
* 622 nunit-console fails when use --output
* 628 Modify IService interface and simplify ServiceContext
* 631 Separate packaging for the compact framework
* 646 ConfigurationManager.AppSettings Params Return Null under Beta 1
* 648 Passing 2 or more test assemblies targeting > .NET 2.0 to nunit-console fails
NUnit 3.0.0 Beta 1 - March 25, 2015
General
* There is now a master windows installer for the framework, engine and console runner.
Framework
* We no longer create a separate framework build for .NET 3.5. The 2.0 and
3.5 builds were essentially the same, so the former should now be used
under both runtimes.
* A new Constraint, DictionaryContainsKeyConstraint, may be used to test
that a specified key is present in a dictionary.
* LevelOfParallelizationAttribute has been renamed to LevelOfParallelismAttribute.
* The Silverlight runner now displays output in color and includes any
text output created by the tests.
* The class and method names of each test are included in the output xml
where applicable.
* String arguments used in test case names are now truncated to 40 rather
than 20 characters.
Engine
* The engine API has now been finalized. It permits specifying a minimum
version of the engine that a runner is able to use. The best installed
version of the engine will be loaded. Third-party runners may override
the selection process by including a copy of the engine in their
installation directory and specifying that it must be used.
* The V2 framework driver now uses the event listener and test listener
passed to it by the runner. This corrects several outstanding issues
caused by events not being received and allows selecting V2 tests to
be run from the command-line, in the same way that V3 tests are selected.
Console
* The console now defaults to not using shadowcopy. There is a new option
--shadowcopy to turn it on if needed.
Issues Resolved
* 224 Silverlight Support
* 318 TestActionAttribute: Retrieving the TestFixture
* 428 Add ExpectedExceptionAttribute to C# samples
* 440 Automatic selection of Test Engine to use
* 450 Create master install that includes the framework, engine and console installs
* 477 Assert does not work with ArraySegment
* 482 nunit-console has multiple errors related to -framework option
* 483 Adds constraint for asserting that a dictionary contains a particular key
* 484 Missing file in NUnit.Console nuget package
* 485 Can't run v2 tests with nunit-console 3.0
* 487 NUnitLite can't load assemblies by their file name
* 488 Async setup and teardown still don't work
* 497 Framework installer shold register the portable framework
* 504 Option --workers:0 is ignored
* 508 Travis builds with failure in engine tests show as successful
* 509 Under linux, not all mono profiles are listed as available
* 512 Drop the .NET 3.5 build
* 517 V2 FrameworkDriver does not make use of passed in TestEventListener
* 523 Provide an option to disable shadowcopy in NUnit v3
* 528 V2 FrameworkDriver does not make use of passed in TestFilter
* 530 Color display for Silverlight runner
* 531 Display text output from tests in Silverlight runner
* 534 Add classname and methodname to test result xml
* 541 Console help doesn't indicate defaults
NUnit 3.0.0 Alpha 5 - January 30, 2015
General
* A Windows installer is now included in the release packages.
Framework
* TestCaseAttribute now allows arguments with default values to be omitted. Additionaly, it accepts a Platform property to specify the platforms on which the test case should be run.
* TestFixture and TestCase attributes now enforce the requirement that a reason needs to be provided when ignoring a test.
* SetUp, TearDown, OneTimeSetUp and OneTimeTearDown methods may now be async.
* String arguments over 20 characters in length are truncated when used as part of a test name.
Engine
* The engine is now extensible using Mono.Addins. In this release, extension points are provided for FrameworkDrivers, ProjectLoaders and OutputWriters. The following addins are bundled as a part of NUnit:
* A FrameworkDriver that allows running NUnit V2 tests under NUnit 3.0.
* ProjectLoaders for NUnit and Visual Studio projects.
* An OutputWriter that creates XML output in NUnit V2 format.
* DomainUsage now defaults to Multiple if not specified by the runner
Console
* New options supported:
* --testlist provides a list of tests to run in a file
* --stoponerror indicates that the run should terminate when any test fails.
Issues Resolved
* 20 TestCaseAttribute needs Platform property.
* 60 NUnit should support async setup, teardown, fixture setup and fixture teardown.
* 257 TestCaseAttribute should not require parameters with default values to be specified.
* 266 Pluggable framework drivers.
* 368 Create addin model.
* 369 Project loader addins
* 370 OutputWriter addins
* 403 Move ConsoleOptions.cs and Options.cs to Common and share...
* 419 Create Windows Installer for NUnit.
* 427 [TestFixture(Ignore=true)] should not be allowed.
* 437 Errors in tests under Linux due to hard-coded paths.
* 441 NUnit-Console should support --testlist option
* 442 Add --stoponerror option back to nunit-console.
* 456 Fix memory leak in RuntimeFramework.
* 459 Remove the Mixed Platforms build configuration.
* 468 Change default domain usage to multiple.
* 469 Truncate string arguments in test names in order to limit the length.
NUnit 3.0.0 Alpha 4 - December 30, 2014
Framework
* ApartmentAttribute has been added, replacing STAAttribute and MTAAttribute.
* Unnecessary overloads of Assert.That and Assume.That have been removed.
* Multiple SetUpFixtures may be specified in a single namespace.
* Improvements to the Pairwise strategy test case generation algorithm.
* The new NUnitLite runner --testlist option, allows a list of tests to be kept in a file.
Engine
* A driver is now included, which allows running NUnit 2.x tests under NUnit 3.0.
* The engine can now load and run tests specified in a number of project formats:
* NUnit (.nunit)
* Visual Studio C# projects (.csproj)
* Visual Studio F# projects (.vjsproj)
* Visual Studio Visual Basic projects (.vbproj)
* Visual Studio solutions (.sln)
* Legacy C++ and Visual JScript projects (.csproj and .vjsproj) are also supported
* Support for the current C++ format (.csxproj) is not yet available
* Creation of output files like TestResult.xml in various formats is now a
service of the engine, available to any runner.
Console
* The command-line may now include any number of assemblies and/or supported projects.
Issues Resolved
* 37 Multiple SetUpFixtures should be permitted on same namespace
* 210 TestContext.WriteLine in an AppDomain causes an error
* 227 Add support for VS projects and solutions
* 231 Update C# samples to use NUnit 3.0
* 233 Update F# samples to use NUnit 3.0
* 234 Update C++ samples to use NUnit 3.0
* 265 Reorganize console reports for nunit-console and nunitlite
* 299 No full path to assembly in XML file under Compact Framework
* 301 Command-line length
* 363 Make Xml result output an engine service
* 377 CombiningStrategyAttributes don't work correctly on generic methods
* 388 Improvements to NUnitLite runner output
* 390 Specify exactly what happens when a test times out
* 396 ApartmentAttribute
* 397 CF nunitlite runner assembly has the wrong name
* 407 Assert.Pass() with ]]> in message crashes console runner
* 414 Simplify Assert overloads
* 416 NUnit 2.x Framework Driver
* 417 Complete work on NUnit projects
* 420 Create Settings file in proper location
NUnit 3.0.0 Alpha 3 - November 29, 2014
Breaking Changes
* NUnitLite tests must reference both the nunit.framework and nunitlite assemblies.
Framework
* The NUnit and NUnitLite frameworks have now been merged. There is no longer any distinction
between them in terms of features, although some features are not available on all platforms.
* The release includes two new framework builds: compact framework 3.5 and portable. The portable
library is compatible with .NET 4.5, Silverlight 5.0, Windows 8, Windows Phone 8.1,
Windows Phone Silverlight 8, Mono for Android and MonoTouch.
* A number of previously unsupported features are available for the Compact Framework:
- Generic methods as tests
- RegexConstraint
- TimeoutAttribute
- FileAssert, DirectoryAssert and file-related constraints
Engine
* The logic of runtime selection has now changed so that each assembly runs by default
in a separate process using the runtime for which it was built.
* On 64-bit systems, each test process is automatically created as 32-bit or 64-bit,
depending on the platform specified for the test assembly.
Console
* The console runner now runs tests in a separate process per assembly by default. They may
still be run in process or in a single separate process by use of command-line options.
* The console runner now starts in the highest version of the .NET runtime available, making
it simpler to debug tests by specifying that they should run in-process on the command-line.
* The -x86 command-line option is provided to force execution in a 32-bit process on a 64-bit system.
* A writeability check is performed for each output result file before trying to run the tests.
* The -teamcity option is now supported.
Issues Resolved
* 12 Compact framework should support generic methods
* 145 NUnit-console fails if test result message contains invalid xml characters
* 155 Create utility classes for platform-specific code
* 223 Common code for NUnitLite console runner and NUnit-Console
* 225 Compact Framework Support
* 238 Improvements to running 32 bit tests on a 64 bit system
* 261 Add portable nunitlite build
* 284 NUnitLite Unification
* 293 CF does not have a CurrentDirectory
* 306 Assure NUnit can write resultfile
* 308 Early disposal of runners
* 309 NUnit-Console should support incremental output under TeamCity
* 325 Add RegexConstraint to compact framework build
* 326 Add TimeoutAttribute to compact framework build
* 327 Allow generic test methods in the compact framework
* 328 Use .NET Stopwatch class for compact framework builds
* 331 Alpha 2 CF does not build
* 333 Add parallel execution to desktop builds of NUnitLite
* 334 Include File-related constraints and syntax in NUnitLite builds
* 335 Re-introduce 'Classic' NUnit syntax in NUnitLite
* 336 Document use of separate obj directories per build in our projects
* 337 Update Standard Defines page for .NET 3.0
* 341 Move the NUnitLite runners to separate assemblies
* 367 Refactor XML Escaping Tests
* 372 CF Build TestAssemblyRunnerTests
* 373 Minor CF Test Fixes
* 378 Correct documentation for PairwiseAttribute
* 386 Console Output Improvements
NUnit 3.0.0 Alpha 2 - November 2, 2014
Breaking Changes
* The console runner no longer displays test results in the debugger.
* The NUnitLite compact framework 2.0 build has been removed.
* All addin support has been removed from the framework. Documentation of NUnit 3.0 extensibility features will be published in time for the beta release. In the interim, please ask for support on the nunit-discuss list.
General
* A separate solution has been created for Linux
* We now have continuous integration builds under both Travis and Appveyor
* The compact framework 3.5 build is now working and will be supported in future releases.
New Features
* The console runner now automatically detects 32- versus 64-bit test assemblies.
* The NUnitLite report output has been standardized to match that of nunit-console.
* The NUnitLite command-line has been standardized to match that of nunit-console where they share the same options.
* Both nunit-console and NUnitLite now display output in color.
* ActionAttributes now allow specification of multiple targets on the attribute as designed. This didn't work in the first alpha.
* OneTimeSetUp and OneTimeTearDown failures are now shown on the test report. Individual test failures after OneTimeSetUp failure are no longer shown.
* The console runner refuses to run tests build with older versions of NUnit. A plugin will be available to run older tests in the future.
Issues Resolved
* 222 Color console for NUnitLite
* 229 Timing failures in tests
* 241 Remove reference to Microslft BCL packages
* 243 Create solution for Linux
* 245 Multiple targets on action attributes not implemented
* 246 C++ tests do not compile in VS2013
* 247 Eliminate trace display when running tests in debug
* 255 Add new result states for more precision in where failures occur
* 256 ContainsConstraint break when used with AndConstraint
* 264 Stacktrace displays too many entries
* 269 Add manifest to nunit-console and nunit-agent
* 270 OneTimeSetUp failure results in too much output
* 271 Invalid tests should be treated as errors
* 274 Command line options should be case insensitive
* 276 NUnit-console should not reference nunit.framework
* 278 New result states (ChildFailure and SetupFailure) break NUnit2XmlOutputWriter
* 282 Get tests for NUnit2XmlOutputWriter working
* 288 Set up Appveyor CI build
* 290 Stack trace still displays too many items
* 315 NUnit 3.0 alpha: Cannot run in console on my assembly
* 319 CI builds are not treating test failures as failures of the build
* 322 Remove Stopwatch tests where they test the real .NET Stopwatch
NUnit 3.0.0 Alpha 1 - September 22, 2014
Breaking Changes
* Legacy suites are no longer supported
* Assert.NullOrEmpty is no longer supported (Use Is.Null.Or.Empty)
General
* MsBuild is now used for the build rather than NAnt
* The framework test harness has been removed now that nunit-console is at a point where it can run the tests.
New Features
* Action Attributes have been added with the same features as in NUnit 2.6.3.
* TestContext now has a method that allows writing to the XML output.
* TestContext.CurrentContext.Result now provides the error message and stack trace during teardown.
* Does prefix operator supplies several added constraints.
Issues Resolved
* 6 Log4net not working with NUnit
* 13 Standardize commandline options for nunitlite runner
* 17 No allowance is currently made for nullable arguents in TestCase parameter conversions
* 33 TestCaseSource cannot refer to a parameterized test fixture
* 54 Store message and stack trace in TestContext for use in TearDown
* 111 Implement Changes to File, Directory and Path Assertions
* 112 Implement Action Attributes
* 156 Accessing multiple AppDomains within unit tests result in SerializationException
* 163 Add --trace option to NUnitLite
* 167 Create interim documentation for the alpha release
* 169 Design and implement distribution of NUnit packages
* 171 Assert.That should work with any lambda returning bool
* 175 Test Harness should return an error if any tests fail
* 180 Errors in Linux CI build
* 181 Replace NAnt with MsBuild / XBuild
* 183 Standardize commandline options for test harness
* 188 No output from NUnitLite when selected test is not found
* 189 Add string operators to Does prefix
* 193 TestWorkerTests.BusyExecutedIdleEventsCalledInSequence fails occasionally
* 197 Deprecate or remove Assert.NullOrEmpty
* 202 Eliminate legacy suites
* 203 Combine framework, engine and console runner in a single solution and repository
* 209 Make Ignore attribute's reason mandatory
* 215 Running 32-bit tests on a 64-bit OS
* 219 Teardown failures are not reported
Console Issues Resolved (Old nunit-console project, now combined with nunit)
* 2 Failure in TestFixtureSetUp is not reported correctly
* 5 CI Server for nunit-console
* 6 System.NullReferenceException on start nunit-console-x86
* 21 NUnitFrameworkDriverTests fail if not run from same directory
* 24 'Debug' value for /trace option is deprecated in 2.6.3
* 38 Confusing Excluded categories output
NUnit 2.9.7 - August 8, 2014
Breaking Changes
* NUnit no longer supports void async test methods. You should use a Task return Type instead.
* The ExpectedExceptionAttribute is no longer supported. Use Assert.Throws() or Assert.That(..., Throws) instead for a more precise specification of where the exception is expected to be thrown.
New Features
* Parallel test execution is supported down to the Fixture level. Use ParallelizableAttribute to indicate types that may be run in parallel.
* Async tests are supported for .NET 4.0 if the user has installed support for them.
* A new FileExistsConstraint has been added along with FileAssert.Exists and FileAssert.DoesNotExist
* ExpectedResult is now supported on simple (non-TestCase) tests.
* The Ignore attribute now takes a named parameter Until, which allows specifying a date after which the test is no longer ignored.
* The following new values are now recognized by PlatformAttribute: Win7, Win8, Win8.1, Win2012Server, Win2012ServerR2, NT6.1, NT6.2, 32-bit, 64-bit
* TimeoutAttribute is now supported under Silverlight
* ValuesAttribute may be used without any values on an enum or boolean argument. All possible values are used.
* You may now specify a tolerance using Within when testing equality of DateTimeOffset values.
* The XML output now includes a start and end time for each test.
Issues Resolved
* 8 [SetUpFixture] is not working as expected
* 14 CI Server for NUnit Framework
* 21 Is.InRange Constraint Ambiguity
* 27 Values attribute support for enum types
* 29 Specifying a tolerance with "Within" doesn't work for DateTimeOffset data types
* 31 Report start and end time of test execution
* 36 Make RequiresThread, RequiresSTA, RequiresMTA inheritable
* 45 Need of Enddate together with Ignore
* 55 Incorrect XML comments for CollectionAssert.IsSubsetOf
* 62 Matches(Constraint) does not work as expected
* 63 Async support should handle Task return type without state machine
* 64 AsyncStateMachineAttribute should only be checked by name
* 65 Update NUnit Wiki to show the new location of samples
* 66 Parallel Test Execution within test assemblies
* 67 Allow Expected Result on simple tests
* 70 EquivalentTo isn't compatible with IgnoreCase for dictioneries
* 75 Async tests should be supported for projects that target .NET 4.0
* 82 nunit-framework tests are timing out on Linux
* 83 Path-related tests fail on Linux
* 85 Culture-dependent NUnit tests fail on non-English machine
* 88 TestCaseSourceAttribute documentation
* 90 EquivalentTo isn't compatible with IgnoreCase for char
* 100 Changes to Tolerance definitions
* 110 Add new platforms to PlatformAttribute
* 113 Remove ExpectedException
* 118 Workarounds for missing InternalPreserveStackTrace in mono
* 121 Test harness does not honor the --worker option when set to zero
* 129 Standardize Timeout in the Silverlight build
* 130 Add FileAssert.Exists and FileAssert.DoesNotExist
* 132 Drop support for void async methods
* 153 Surprising behavior of DelayedConstraint pollingInterval
* 161 Update API to support stopping an ongoing test run
NOTE: Bug Fixes below this point refer to the number of the bug in Launchpad.
NUnit 2.9.6 - October 4, 2013
Main Features
* Separate projects for nunit-console and nunit.engine
* New builds for .NET 4.5 and Silverlight
* TestContext is now supported
* External API is now stable; internal interfaces are separate from API
* Tests may be run in parallel on separate threads
* Solutions and projects now use VS2012 (except for Compact framework)
Bug Fixes
* 463470 We should encapsulate references to pre-2.0 collections
* 498690 Assert.That() doesn't like properties with scoped setters
* 501784 Theory tests do not work correctly when using null parameters
* 531873 Feature: Extraction of unit tests from NUnit test assembly and calling appropriate one
* 611325 Allow Teardown to detect if last test failed
* 611938 Generic Test Instances disappear
* 655882 Make CategoryAttribute inherited
* 664081 Add Server2008 R2 and Windows 7 to PlatformAttribute
* 671432 Upgrade NAnt to Latest Release
* 676560 Assert.AreEqual does not support IEquatable<T>
* 691129 Add Category parameter to TestFixture
* 697069 Feature request: dynamic location for TestResult.xml
* 708173 NUnit's logic for comparing arrays - use Comparer<T[]> if it is provided
* 709062 "System.ArgumentException : Cannot compare" when the element is a list
* 712156 Tests cannot use AppDomain.SetPrincipalPolicy
* 719184 Platformdependency in src/ClientUtilities/util/Services/DomainManager.cs:40
* 719187 Using Path.GetTempPath() causes conflicts in shared temporary folders
* 735851 Add detection of 3.0, 3.5 and 4.0 frameworks to PlatformAttribute
* 736062 Deadlock when EventListener performs a Trace call + EventPump synchronisation
* 756843 Failing assertion does not show non-linear tolerance mode
* 766749 net-2.0\nunit-console-x86.exe.config should have a <startup /> element and also enable loadFromRemoteSources
* 770471 Assert.IsEmpty does not support IEnumerable
* 785460 Add Category parameter to TestCaseSourceAttribute
* 787106 EqualConstraint provides inadequate failure information for IEnumerables
* 792466 TestContext MethodName
* 794115 HashSet incorrectly reported
* 800089 Assert.Throws() hides details of inner AssertionException
* 848713 Feature request: Add switch for console to break on any test case error
* 878376 Add 'Exactly(n)' to the NUnit constraint syntax
* 882137 When no tests are run, higher level suites display as Inconclusive
* 882517 NUnit 2.5.10 doesn't recognize TestFixture if there are only TestCaseSource inside
* 885173 Tests are still executed after cancellation by user
* 885277 Exception when project calls for a runtime using only 2 digits
* 885604 Feature request: Explicit named parameter to TestCaseAttribute
* 890129 DelayedConstraint doesn't appear to poll properties of objects
* 892844 Not using Mono 4.0 profile under Windows
* 893919 DelayedConstraint fails polling properties on references which are initially null
* 896973 Console output lines are run together under Linux
* 897289 Is.Empty constraint has unclear failure message
* 898192 Feature Request: Is.Negative, Is.Positive
* 898256 IEnumerable<T> for Datapoints doesn't work
* 899178 Wrong failure message for parameterized tests that expect exceptions
* 904841 After exiting for timeout the teardown method is not executed
* 908829 TestCase attribute does not play well with variadic test functions
* 910218 NUnit should add a trailing separator to the ApplicationBase
* 920472 CollectionAssert.IsNotEmpty must dispose Enumerator
* 922455 Add Support for Windows 8 and Windows 2012 Server to PlatformAttribute
* 928246 Use assembly.Location instead of assembly.CodeBase
* 958766 For development work under TeamCity, we need to support nunit2 formatted output under direct-runner
* 1000181 Parameterized TestFixture with System.Type as constructor arguments fails
* 1000213 Inconclusive message Not in report output
* 1023084 Add Enum support to RandomAttribute
* 1028188 Add Support for Silverlight
* 1029785 Test loaded from remote folder failed to run with exception System.IODirectory
* 1037144 Add MonoTouch support to PlatformAttribute
* 1041365 Add MaxOsX and Xbox support to platform attribute
* 1057981 C#5 async tests are not supported
* 1060631 Add .NET 4.5 build
* 1064014 Simple async tests should not return Task<T>
* 1071164 Support async methods in usage scenarios of Throws constraints
* 1071343 Runner.Load fails on CF if the test assembly contains a generic method
* 1071861 Error in Path Constraints
* 1072379 Report test execution time at a higher resolution
* 1074568 Assert/Assume should support an async method for the ActualValueDelegate
* 1082330 Better Exception if SetCulture attribute is applied multiple times
* 1111834 Expose Random Object as part of the test context
* 1111838 Include Random Seed in Test Report
* 1172979 Add Category Support to nunitlite Runner
* 1203361 Randomizer uniqueness tests sometimes fail
* 1221712 When non-existing test method is specified in -test, result is still "Tests run: 1, Passed: 1"
* 1223294 System.NullReferenceException thrown when ExpectedExceptionAttribute is used in a static class
* 1225542 Standardize commandline options for test harness
Bug Fixes in 2.9.6 But Not Listed Here in the Release
* 541699 Silverlight Support
* 1222148 /framework switch does not recognize net-4.5
* 1228979 Theories with all test cases inconclusive are not reported as failures
NUnit 2.9.5 - July 30, 2010
Bug Fixes
* 483836 Allow non-public test fixtures consistently
* 487878 Tests in generic class without proper TestFixture attribute should be invalid
* 498656 TestCase should show array values in GUI
* 513989 Is.Empty should work for directories
* 519912 Thread.CurrentPrincipal Set In TestFixtureSetUp Not Maintained Between Tests
* 532488 constraints from ConstraintExpression/ConstraintBuilder are not reusable
* 590717 categorie contains dash or trail spaces is not selectable
* 590970 static TestFixtureSetUp/TestFixtureTearDown methods in base classes are not run
* 595683 NUnit console runner fails to load assemblies
* 600627 Assertion message formatted poorly by PropertyConstraint
* 601108 Duplicate test using abstract test fixtures
* 601645 Parametered test should try to convert data type from source to parameter
* 605432 ToString not working properly for some properties
* 606548 Deprecate Directory Assert in 2.5 and remove it in 3.0
* 608875 NUnit Equality Comparer incorrectly defines equality for Dictionary objects
NUnit 2.9.4 - May 4, 2010
Bug Fixes
* 419411 Fixture With No Tests Shows as Non-Runnable
* 459219 Changes to thread princpal cause failures under .NET 4.0
* 459224 Culture test failure under .NET 4.0
* 462019 Line endings needs to be better controlled in source
* 462418 Assume.That() fails if I specify a message
* 483845 TestCase expected return value cannot be null
* 488002 Should not report tests in abstract class as invalid
* 490679 Category in TestCaseData clashes with Category on ParameterizedMethodSuite
* 501352 VS2010 projects have not been updated for new directory structure
* 504018 Automatic Values For Theory Test Parameters Not Provided For bool And enum
* 505899 'Description' parameter in both TestAttribute and TestCaseAttribute is not allowed
* 523335 TestFixtureTearDown in static class not executed
* 556971 Datapoint(s)Attribute should work on IEnumerable<T> as well as on Arrays
* 561436 SetCulture broken with 2.5.4
* 563532 DatapointsAttribute should be allowed on properties and methods
NUnit 2.9.3 - October 26, 2009
Main Features
* Created new API for controlling framework
* New builds for .Net 3.5 and 4.0, compact framework 3.5
* Support for old style tests has been removed
* New adhoc runner for testing the framework
Bug Fixes
* 432805 Some Framework Tests don't run on Linux
* 440109 Full Framework does not support "Contains"
NUnit 2.9.2 - September 19, 2009
Main Features
* NUnitLite code is now merged with NUnit
* Added NUnitLite runner to the framework code
* Added Compact framework builds
Bug Fixes
* 430100 Assert.Catch<T> should return T
* 432566 NUnitLite shows empty string as argument
* 432573 Mono test should be at runtime
NUnit 2.9.1 - August 27, 2009
General
* Created a separate project for the framework and framework tests
* Changed license to MIT / X11
* Created Windows installer for the framework
Bug Fixes
* 400502 NUnitEqualityComparer.StreamsE­qual fails for same stream
* 400508 TestCaseSource attirbute is not working when Type is given
* 400510 TestCaseData variable length ctor drops values
* 417557 Add SetUICultureAttribute from NUnit 2.5.2
* 417559 Add Ignore to TestFixture, TestCase and TestCaseData
* 417560 Merge Assert.Throws and Assert.Catch changes from NUnit 2.5.2
* 417564 TimeoutAttribute on Assembly
Copyright (c) 2015 Charlie Poole
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
NUnit 3.0 is based on earlier versions of NUnit, with Portions
Copyright (c) 2002-2014 Charlie Poole or
Copyright (c) 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov or
Copyright (c) 2000-2002 Philip A. Craig
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment