Unverified Commit 3f7e5466 authored by Nick Craver's avatar Nick Craver Committed by GitHub

Merge pull request #868 from StackExchange/pipelines

2.0 - change tracking PR; do not merge
parents 80dd68f6 af95d8ea
......@@ -45,6 +45,9 @@ dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
# Ignore silly if statements
dotnet_style_prefer_conditional_expression_over_return = false:none
# CSharp code style settings:
[*.cs]
# Prefer method-like constructs to have a expression-body
......@@ -74,3 +77,6 @@ csharp_new_line_before_members_in_anonymous_types = true
# Space settings
csharp_space_after_keywords_in_control_flow_statements = true:suggestion
# Language settings
csharp_prefer_simple_default_expression = false:none
\ No newline at end of file
......@@ -22,3 +22,7 @@ StackExchange.Redis.*.zip
test-results*.xml
packages/
StackExchange.Redis.Tests/*Config.json
t8.shakespeare.txt
launchSettings.json
*.vsp
*.diagsession
\ No newline at end of file
using StackExchange.Redis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System;
[assembly: AssemblyVersion("1.0.0")]
namespace BasicTest
{
internal static class Program
{
public static void Main()
{
using (var conn = ConnectionMultiplexer.Connect("127.0.0.1:6379"))
{
var db = conn.GetDatabase();
RedisKey key = Me();
db.KeyDelete(key);
db.StringSet(key, "abc");
string s = (string)db.ScriptEvaluate(@"
local val = redis.call('get', KEYS[1])
redis.call('del', KEYS[1])
return val", new RedisKey[] { key }, flags: CommandFlags.NoScriptCache);
Console.WriteLine(s);
Console.WriteLine(db.KeyExists(key));
}
}
internal static string Me([CallerMemberName] string caller = null)
{
return caller;
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<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')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{6756F911-BD09-4226-B597-67871DEB8ED5}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ConnectionWatcher</RootNamespace>
<AssemblyName>ConnectionWatcher</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Verbose\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Verbose|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Verbose\</OutputPath>
<DefineConstants>TRACE;DEBUG;VERBOSE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Log Output|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Log Output\</OutputPath>
<DefineConstants>TRACE;DEBUG;LOGOUTPUT</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StackExchange.Redis_Net45\StackExchange.Redis_Net45.csproj">
<Project>{7cec07f2-8c03-4c42-b048-738b215824c1}</Project>
<Name>StackExchange.Redis_Net45</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="label1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="label2.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="label3.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="label4.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="label5.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="ticker.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>
\ No newline at end of file
using System;
using System.Windows.Forms;
namespace ConnectionWatcher
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ConnectionWatcher")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ConnectionWatcher")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f494c35a-e665-4f46-9906-dbb873e00b51")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34011
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ConnectionWatcher.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ConnectionWatcher.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34011
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ConnectionWatcher.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
<Project>
<PropertyGroup>
<VersionPrefix>1.2.7</VersionPrefix>
<VersionPrefix>2.0.0</VersionPrefix>
<Copyright>2017 Stack Exchange, Inc.</Copyright>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyOriginatorKeyFile>../StackExchange.Redis.snk</AssemblyOriginatorKeyFile>
<LibTargetFrameworks>net461;netstandard2.0</LibTargetFrameworks>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)StackExchange.Redis.snk</AssemblyOriginatorKeyFile>
<PackageId>$(AssemblyName)</PackageId>
<Features>strict</Features>
<Authors>Stack Exchange, Inc.; marc.gravell</Authors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CodeAnalysisRuleset>$(MSBuildThisFileDirectory)Shared.ruleset</CodeAnalysisRuleset>
<MSBuildWarningsAsMessages>NETSDK1069</MSBuildWarningsAsMessages>
<NoWarn>NU5105</NoWarn>
<PackageReleaseNotes>https://stackexchange.github.io/StackExchange.Redis/ReleaseNotes</PackageReleaseNotes>
<PackageProjectUrl>https://github.com/StackExchange/StackExchange.Redis/</PackageProjectUrl>
<PackageLicenseUrl>https://raw.github.com/StackExchange/StackExchange.Redis/master/LICENSE</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/StackExchange/StackExchange.Redis/</RepositoryUrl>
<DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType>
<DefaultLanguage>en-US</DefaultLanguage>
<IncludeSymbols>false</IncludeSymbols>
<LibraryTargetFrameworks>net45;net46;netstandard2.0</LibraryTargetFrameworks>
<CoreFxVersion>4.3.0</CoreFxVersion>
<xUnitVersion>2.4.0-beta.2.build3981</xUnitVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="2.1.23" PrivateAssets="all" />
<PackageReference Include="SourceLink.Create.GitHub" Version="2.8.2" PrivateAssets="All" />
<DotNetCliToolReference Include="dotnet-sourcelink" Version="2.8.2" />
<DotNetCliToolReference Include="dotnet-sourcelink-git" Version="2.8.2" />
</ItemGroup>
</Project>
// .NET port of https://github.com/RedisLabs/JRediSearch/
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("NRediSearch.Test")]
......@@ -2,6 +2,7 @@
<configuration>
<packageSources>
<clear/>
<!--<add key="localNuget" value="c:\code\LocalNuget" />-->
<add key="xUnit" value="https://www.myget.org/F/xunit/api/v3/index.json" />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
</packageSources>
......
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Rules for StackOverflow" Description="Code analysis rules for the StackOverflow solution." ToolsVersion="15.0">
<IncludeAll Action="Warning" />
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features">
<Rule Id="IDE0045" Action="None" />
<Rule Id="IDE0046" Action="None" />
</Rules>
<Rules AnalyzerId="Roslynator.CSharp.Analyzers" RuleNamespace="Roslynator.CSharp.Analyzers">
<Rule Id="RCS1047" Action="None" />
<Rule Id="RCS1057" Action="None" />
<Rule Id="RCS1130" Action="None" />
<Rule Id="RCS1146" Action="None" />
<Rule Id="RCS1154" Action="None" />
<Rule Id="RCS1180" Action="None" />
<Rule Id="RCS1191" Action="None" />
</Rules>
</RuleSet>
\ No newline at end of file
This diff is collapsed.
// .NET port of https://github.com/RedisLabs/JRediSearch/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StackExchange.Redis;
namespace StackExchange.Redis.Modules.RediSearch
{
/// <summary>
/// Document represents a single indexed document or entity in the engine
/// </summary>
public class Document
{
public string Id { get; }
public double Score { get; }
public byte[] Payload { get; }
private Dictionary<String, RedisValue> properties = new Dictionary<string, RedisValue>();
public Document(string id, double score, byte[] payload)
{
Id = id;
Score = score;
Payload = payload;
}
public static Document Load(string id, double score, byte[] payload, RedisValue[] fields)
{
Document ret = new Document(id, score, payload);
if (fields != null)
{
for (int i = 0; i < fields.Length; i += 2)
{
ret[(string)fields[i]] = fields[i + 1];
}
}
return ret;
}
public RedisValue this[string key]
{
get { return properties.TryGetValue(key, out var val) ? val : default(RedisValue); }
internal set { properties[key] = value; }
}
public bool HasProperty(string key) => properties.ContainsKey(key);
}
}
using StackExchange.Redis;
using System.Collections;
namespace StackExchange.Redis.Modules.RediSearch
{
/// <summary>
/// Cache to ensure we encode and box literals once only
/// </summary>
internal static class Literals
{
private static Hashtable _boxed = new Hashtable();
private static object _null = RedisValue.Null;
/// <summary>
/// Obtain a lazily-cached pre-encoded and boxed representation of a string
/// </summary>
/// <remarks>This shoul donly be used for fixed values, not user data (the cache is never reclaimed, so it will be a memory leak)</remarks>
public static object Literal(this string value)
{
if (value == null) return _null;
object boxed = _boxed[value];
if (boxed == null)
{
lock (_boxed)
{
boxed = _boxed[value];
if (boxed == null)
{
boxed = (RedisValue)value;
_boxed.Add(value, boxed);
}
}
}
return boxed;
}
}
}
// .NET port of https://github.com/RedisLabs/JRediSearch/
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Globalization;
namespace StackExchange.Redis.Modules.RediSearch
{
/// <summary>
/// Query represents query parameters and filters to load results from the engine
/// </summary>
public class Query
{
/// <summary>
/// Filter represents a filtering rules in a query
/// </summary>
public abstract class Filter
{
public string Property { get; }
internal abstract void SerializeRedisArgs(List<object> args);
internal Filter(string property)
{
Property = property;
}
}
/// <summary>
/// NumericFilter wraps a range filter on a numeric field. It can be inclusive or exclusive
/// </summary>
public class NumericFilter : Filter
{
private readonly double min, max;
private readonly bool exclusiveMin, exclusiveMax;
public NumericFilter(string property, double min, bool exclusiveMin, double max, bool exclusiveMax) : base(property)
{
this.min = min;
this.max = max;
this.exclusiveMax = exclusiveMax;
this.exclusiveMin = exclusiveMin;
}
public NumericFilter(string property, double min, double max) : this(property, min, false, max, false) { }
internal override void SerializeRedisArgs(List<object> args)
{
RedisValue FormatNum(double num, bool exclude)
{
if (!exclude || double.IsInfinity(num))
{
return (RedisValue)num; // can use directly
}
// need to add leading bracket
return "(" + num.ToString("G17", NumberFormatInfo.InvariantInfo);
}
args.Add("FILTER".Literal());
args.Add(Property);
args.Add(FormatNum(min, exclusiveMin));
args.Add(FormatNum(max, exclusiveMax));
}
}
/// <summary>
/// GeoFilter encapsulates a radius filter on a geographical indexed fields
/// </summary>
public class GeoFilter : Filter
{
private readonly double lon, lat, radius;
private GeoUnit unit;
public GeoFilter(string property, double lon, double lat, double radius, GeoUnit unit) : base(property)
{
this.lon = lon;
this.lat = lat;
this.radius = radius;
this.unit = unit;
}
internal override void SerializeRedisArgs(List<object> args)
{
args.Add("GEOFILTER".Literal());
args.Add(Property);
args.Add(lon);
args.Add(lat);
args.Add(radius);
switch (unit)
{
case GeoUnit.Feet: args.Add("ft".Literal()); break;
case GeoUnit.Kilometers: args.Add("km".Literal()); break;
case GeoUnit.Meters: args.Add("m".Literal()); break;
case GeoUnit.Miles: args.Add("mi".Literal()); break;
default: throw new InvalidOperationException($"Unknown unit: {unit}");
}
}
}
private struct Paging
{
public int Offset { get; }
public int Count { get; }
public Paging(int offset, int count)
{
Offset = offset;
Count = count;
}
}
/// <summary>
/// The query's filter list. We only support AND operation on all those filters
/// </summary>
List<Filter> _filters = new List<Filter>();
/// <summary>
/// The textual part of the query
/// </summary>
public string QueryString { get; }
/// <summary>
/// The sorting parameters
/// </summary>
Paging _paging = new Paging(0, 10);
/// <summary>
/// Set the query to verbatim mode, disabling stemming and query expansion
/// </summary>
public bool Verbatim { get; set; }
/// <summary>
/// Set the query not to return the contents of documents, and rather just return the ids
/// </summary>
public bool NoContent { get; set; }
/// <summary>
/// Set the query not to filter for stopwords. In general this should not be used
/// </summary>
public bool NoStopwords { get; set; }
/// <summary>
/// Set the query to return a factored score for each results. This is useful to merge results from multiple queries.
/// </summary>
public bool WithScores { get; set; }
/// <summary>
/// Set the query to return object payloads, if any were given
/// </summary>
public bool WithPayloads { get; set; }
/// <summary>
/// Set the query language, for stemming purposes; see http://redisearch.io for documentation on languages and stemming
/// </summary>
public string Language { get; set; }
protected String[] _fields = null;
/// <summary>
/// Set the query payload to be evaluated by the scoring function
/// </summary>
public byte[] Payload { get; set; }
/// <summary>
/// Create a new index
/// </summary>
public Query(String queryString)
{
QueryString = queryString;
}
internal void SerializeRedisArgs(List<object> args)
{
args.Add(QueryString);
if (Verbatim)
{
args.Add("VERBATIM".Literal());
}
if (NoContent)
{
args.Add("NOCONTENT".Literal());
}
if (NoStopwords)
{
args.Add("NOSTOPWORDS".Literal());
}
if (WithScores)
{
args.Add("WITHSCORES".Literal());
}
if (WithPayloads)
{
args.Add("WITHPAYLOADS".Literal());
}
if (Language != null)
{
args.Add("LANGUAGE".Literal());
args.Add(Language);
}
if (_fields != null && _fields.Length > 0)
{
args.Add("INFIELDS".Literal());
args.Add(_fields.Length);
args.AddRange(_fields);
}
if (Payload != null)
{
args.Add("PAYLOAD".Literal());
args.Add(Payload);
}
if (_paging.Offset != 0 || _paging.Count != 10)
{
args.Add("LIMIT".Literal());
args.Add(_paging.Offset);
args.Add(_paging.Count);
}
if (_filters != null && _filters.Count > 0)
{
foreach (var f in _filters)
{
f.SerializeRedisArgs(args);
}
}
}
/// <summary>
/// Limit the results to a certain offset and limit
/// </summary>
/// <param name="offset">the first result to show, zero based indexing</param>
/// <param name="limit">how many results we want to show</param>
/// <returns>the query itself, for builder-style syntax</returns>
public Query Limit(int offset, int count)
{
_paging = new Paging(offset, count);
return this;
}
/// <summary>
/// Add a filter to the query's filter list
/// </summary>
/// <param name="f">either a numeric or geo filter object</param>
/// <returns>the query itself</returns>
public Query AddFilter(Filter f)
{
_filters.Add(f);
return this;
}
/// <summary>
/// Limit the query to results that are limited to a specific set of fields
/// </summary>
/// <param name="fields">a list of TEXT fields in the schemas</param>
/// <returns>the query object itself</returns>
public Query LimitFields(params string[] fields)
{
this._fields = fields;
return this;
}
}
}
// .NET port of https://github.com/RedisLabs/JRediSearch/
using System;
using System.Collections.Generic;
namespace StackExchange.Redis.Modules.RediSearch
{
/// <summary>
/// Schema abstracts the schema definition when creating an index.
/// Documents can contain fields not mentioned in the schema, but the index will only index pre-defined fields
/// </summary>
public sealed class Schema
{
public enum FieldType
{
FullText,
Geo,
Numeric
}
public class Field
{
public String Name { get; }
public FieldType Type { get; }
internal Field(string name, FieldType type)
{
Name = name;
Type = type;
}
internal virtual void SerializeRedisArgs(List<object> args)
{
object GetForRedis(FieldType type)
{
switch (type)
{
case FieldType.FullText: return "TEXT".Literal();
case FieldType.Geo: return "GEO".Literal();
case FieldType.Numeric: return "NUMERIC".Literal();
default: throw new ArgumentOutOfRangeException(nameof(type));
}
}
args.Add(Name);
args.Add(GetForRedis(Type));
}
}
public class TextField : Field
{
public double Weight { get; }
internal TextField(string name, double weight = 1.0) : base(name, FieldType.FullText)
{
Weight = weight;
}
internal override void SerializeRedisArgs(List<object> args)
{
base.SerializeRedisArgs(args);
if (Weight != 1.0)
{
args.Add("WEIGHT".Literal());
args.Add(Weight);
}
}
}
public List<Field> Fields { get; } = new List<Field>();
/// <summary>
/// Add a text field to the schema with a given weight
/// </summary>
/// <param name="name">the field's name</param>
/// <param name="weight">its weight, a positive floating point number</param>
/// <returns>the schema object</returns>
public Schema AddTextField(string name, double weight = 1.0)
{
Fields.Add(new TextField(name, weight));
return this;
}
/// <summary>
/// Add a numeric field to the schema
/// </summary>
/// <param name="name">the field's name</param>
/// <returns>the schema object</returns>
public Schema AddGeoField(string name)
{
Fields.Add(new Field(name, FieldType.Geo));
return this;
}
/// <summary>
/// Add a numeric field to the schema
/// </summary>
/// <param name="name">the field's name</param>
/// <returns>the schema object</returns>
public Schema AddNumericField(string name)
{
Fields.Add(new Field(name, FieldType.Numeric));
return this;
}
}
}
// .NET port of https://github.com/RedisLabs/JRediSearch/
using StackExchange.Redis;
using System.Collections.Generic;
namespace StackExchange.Redis.Modules.RediSearch
{
/// <summary>
/// SearchResult encapsulates the returned result from a search query.
/// It contains publically accessible fields for the total number of results, and an array of <see cref="Document"/>
/// objects conatining the actual returned documents.
/// </summary>
public class SearchResult
{
public long TotalResults { get; }
public List<Document> Documents { get; }
internal SearchResult(RedisResult[] resp, bool hasContent, bool hasScores, bool hasPayloads)
{
// Calculate the step distance to walk over the results.
// The order of results is id, score (if withScore), payLoad (if hasPayloads), fields
int step = 1;
int scoreOffset = 0;
int contentOffset = 1;
int payloadOffset = 0;
if (hasScores)
{
step += 1;
scoreOffset = 1;
contentOffset += 1;
}
if (hasContent)
{
step += 1;
if (hasPayloads)
{
payloadOffset = scoreOffset + 1;
step += 1;
contentOffset += 1;
}
}
// the first element is always the number of results
TotalResults = (long)resp[0];
var docs = new List<Document>((resp.Length - 1) / step);
Documents = docs;
for (int i = 1; i < resp.Length; i += step)
{
var id = (string)resp[i];
double score = 1.0;
byte[] payload = null;
RedisValue[] fields = null;
if (hasScores)
{
score = (double)resp[i + scoreOffset];
}
if (hasPayloads)
{
payload = (byte[])resp[i + payloadOffset];
}
if (hasContent)
{
fields = (RedisValue[])resp[i + contentOffset];
}
docs.Add(Document.Load(id, score, payload, fields));
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<VersionPrefix>0.1</VersionPrefix>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<PackageTags>Redis;Search;Modules;RediSearch</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\StackExchange.Redis\StackExchange.Redis.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
using System;
using System.Linq;
using System.Threading.Tasks;
namespace StackExchange.Redis.Modules.Throttling
{
public static class ThrottlingExtensions
{
public static ThrottleResult Throttle(
this IDatabase db, RedisKey key, int maxBurst,
int maxPerInterval,
int intervalSeconds = 60, int count = 1)
{
return new ThrottleResult(db.Execute("CL.THROTTLE",
key, maxBurst.Boxed(), maxPerInterval.Boxed(), intervalSeconds.Boxed(), count.Boxed()));
}
public async static Task<ThrottleResult> ThrottleAsync(
this IDatabaseAsync db, RedisKey key, int maxBurst,
int maxPerInterval,
int intervalSeconds = 60, int count = 1)
{
return new ThrottleResult(await db.ExecuteAsync("CL.THROTTLE",
key, maxBurst.Boxed(), maxPerInterval.Boxed(), intervalSeconds.Boxed(), count.Boxed()));
}
static readonly object[] _boxedInt32 = Enumerable.Range(-1, 128).Select(i => (object)i).ToArray();
internal static object Boxed(this int value)
=> value >= -1 && value <= 126 ? _boxedInt32[value + 1] : (object)value;
}
public struct ThrottleResult
{
internal ThrottleResult(RedisResult result)
{
var arr = (int[])result;
Permitted = arr[0] == 0;
TotalLimit = arr[1];
RemainingLimit = arr[2];
RetryAfterSeconds = arr[3];
ResetAfterSeconds = arr[4];
}
/// <summary>Whether the action was limited</summary>
public bool Permitted {get;}
/// <summary>The total limit of the key (max_burst + 1). This is equivalent to the common `X-RateLimit-Limit` HTTP header.</summary>
public int TotalLimit {get;}
/// <summary>The remaining limit of the key. Equivalent to `X-RateLimit-Remaining`.</summary>
public int RemainingLimit {get;}
/// <summary>The number of seconds until the user should retry, and always -1 if the action was allowed. Equivalent to `Retry-After`.</summary>
public int RetryAfterSeconds {get;}
/// <summary>The number of seconds until the limit will reset to its maximum capacity. Equivalent to `X-RateLimit-Reset`.</summary>
public int ResetAfterSeconds {get;}
}
}
\ No newline at end of file
dotnet msbuild "/t:Restore;Build;Pack" "/p:NuGetBuildTasksPackTargets='000'" "/p:PackageOutputPath=nupkgs" "/p:Configuration=Release"
using StackExchange.Redis.Tests.Helpers;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Booksleeve
{
public class BookSleeveTestBase
{
public ITestOutputHelper Output { get; }
public BookSleeveTestBase(ITestOutputHelper output)
{
Output = output;
Output.WriteFrameworkVersion();
}
static BookSleeveTestBase()
{
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
Trace.WriteLine(args.Exception, "UnobservedTaskException");
args.SetObserved();
};
}
public static string CreateUniqueName() => Guid.NewGuid().ToString("N");
internal static IServer GetServer(ConnectionMultiplexer conn) => conn.GetServer(conn.GetEndPoints()[0]);
private static readonly SocketManager socketManager = new SocketManager();
internal static ConnectionMultiplexer GetRemoteConnection(bool open = true, bool allowAdmin = false, bool waitForOpen = false, int syncTimeout = 5000, int ioTimeout = 5000)
{
return GetConnection(TestConfig.Current.RemoteServer, TestConfig.Current.RemotePort, open, allowAdmin, waitForOpen, syncTimeout, ioTimeout);
}
private static ConnectionMultiplexer GetConnection(string host, int port, bool open = true, bool allowAdmin = false, bool waitForOpen = false, int syncTimeout = 5000, int ioTimeout = 5000)
{
var options = new ConfigurationOptions
{
EndPoints = { { host, port } },
AllowAdmin = allowAdmin,
SyncTimeout = syncTimeout,
SocketManager = socketManager,
ResponseTimeout = ioTimeout
};
var conn = ConnectionMultiplexer.Connect(options);
conn.InternalError += (s, args) => Trace.WriteLine(args.Exception.Message, args.Origin);
if (open && waitForOpen)
{
conn.GetDatabase().Ping();
}
return conn;
}
internal static ConnectionMultiplexer GetUnsecuredConnection(bool open = true, bool allowAdmin = false, bool waitForOpen = false, int syncTimeout = 5000, int ioTimeout = 5000)
{
return GetConnection(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort, open, allowAdmin, waitForOpen, syncTimeout, ioTimeout);
}
internal static ConnectionMultiplexer GetSecuredConnection()
{
Skip.IfNoConfig(nameof(TestConfig.Config.SecureServer), TestConfig.Current.SecureServer);
var options = new ConfigurationOptions
{
EndPoints = { { TestConfig.Current.SecureServer, TestConfig.Current.SecurePort } },
Password = "changeme",
SyncTimeout = 6000,
SocketManager = socketManager
};
var conn = ConnectionMultiplexer.Connect(options);
conn.InternalError += (s, args) => Trace.WriteLine(args.Exception.Message, args.Origin);
return conn;
}
internal static RedisFeatures GetFeatures(ConnectionMultiplexer muxer) => GetServer(muxer).Features;
internal static void AssertNearlyEqual(double x, double y)
{
if (Math.Abs(x - y) > 0.00001) Assert.Equal(x, y);
}
}
}
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
using System.Security.Authentication;
namespace StackExchange.Redis.Tests.Booksleeve
{
public class Config : BookSleeveTestBase
{
public Config(ITestOutputHelper output) : base(output) { }
[Fact]
public void CanOpenUnsecuredConnection()
{
using (var conn = GetUnsecuredConnection(false))
{
var server = GetServer(conn);
server.Ping();
}
}
[Fact]
public void CanOpenSecuredConnection()
{
using (var conn = GetSecuredConnection())
{
var server = GetServer(conn);
server.Ping();
}
}
[Fact]
public void CanNotOpenNonsenseConnection_IP()
{
Assert.Throws<RedisConnectionException>(() =>
{
var log = new StringWriter();
try
{
using (var conn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":6500")) { }
}
finally
{
Output.WriteLine(log.ToString());
}
});
}
[Fact]
public async Task CanNotOpenNonsenseConnection_DNS()
{
var ex = await Assert.ThrowsAsync<RedisConnectionException>(async () =>
{
var log = new StringWriter();
try
{
using (var conn = await ConnectionMultiplexer.ConnectAsync($"doesnot.exist.ds.{Guid.NewGuid():N}.com:6500", log).ForAwait())
{
}
}
finally
{
Output.WriteLine(log.ToString());
}
}).ForAwait();
Output.WriteLine(ex.ToString());
}
[Fact]
public void CreateDisconnectedNonsenseConnection_IP()
{
var log = new StringWriter();
try
{
using (var conn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":6500,abortConnect=false"))
{
Assert.False(conn.GetServer(conn.GetEndPoints().Single()).IsConnected);
Assert.False(conn.GetDatabase().IsConnected(default(RedisKey)));
}
}
finally
{
Output.WriteLine(log.ToString());
}
}
[Fact]
public void CreateDisconnectedNonsenseConnection_DNS()
{
var log = new StringWriter();
try
{
using (var conn = ConnectionMultiplexer.Connect($"doesnot.exist.ds.{Guid.NewGuid():N}.com:6500, abortConnect=false", log))
{
Assert.False(conn.GetServer(conn.GetEndPoints().Single()).IsConnected);
Assert.False(conn.GetDatabase().IsConnected(default(RedisKey)));
}
}
finally
{
Output.WriteLine(log.ToString());
}
}
[Fact]
public void SslProtocols_SingleValue()
{
var log = new StringWriter();
var options = ConfigurationOptions.Parse("myhost,sslProtocols=Tls11");
Assert.Equal(SslProtocols.Tls11, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_MultipleValues()
{
var log = new StringWriter();
var options = ConfigurationOptions.Parse("myhost,sslProtocols=Tls11|Tls12");
Assert.Equal(SslProtocols.Tls11 | SslProtocols.Tls12, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_UsingIntegerValue()
{
var log = new StringWriter();
// The below scenario is for cases where the *targeted*
// .NET framework version (e.g. .NET 4.0) doesn't define an enum value (e.g. Tls11)
// but the OS has been patched with support
const int integerValue = (int)(SslProtocols.Tls11 | SslProtocols.Tls12);
var options = ConfigurationOptions.Parse("myhost,sslProtocols=" + integerValue);
Assert.Equal(SslProtocols.Tls11 | SslProtocols.Tls12, options.SslProtocols.Value);
}
[Fact]
public void SslProtocols_InvalidValue()
{
var log = new StringWriter();
Assert.Throws<ArgumentOutOfRangeException>(() => ConfigurationOptions.Parse("myhost,sslProtocols=InvalidSslProtocol"));
}
[Fact]
public void ConfigurationOptionsDefaultForAzure()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.windows.net");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsForAzureWhenSpecified()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.windows.net,abortConnect=true, version=2.1.1");
Assert.True(options.DefaultVersion.Equals(new Version(2, 1, 1)));
Assert.True(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureChina()
{
// added a few upper case chars to validate comparison
var options = ConfigurationOptions.Parse("contoso.REDIS.CACHE.chinacloudapi.cn");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureGermany()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.cloudapi.de");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForAzureUSGov()
{
var options = ConfigurationOptions.Parse("contoso.redis.cache.usgovcloudapi.net");
Assert.True(options.DefaultVersion.Equals(new Version(3, 0, 0)));
Assert.False(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultForNonAzure()
{
var options = ConfigurationOptions.Parse("redis.contoso.com");
Assert.True(options.DefaultVersion.Equals(new Version(2, 0, 0)));
Assert.True(options.AbortOnConnectFail);
}
[Fact]
public void ConfigurationOptionsDefaultWhenNoEndpointsSpecifiedYet()
{
var options = new ConfigurationOptions();
Assert.True(options.DefaultVersion.Equals(new Version(2, 0, 0)));
Assert.True(options.AbortOnConnectFail);
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests
{
public class Databases : TestBase
{
public Databases(ITestOutputHelper output) : base (output) { }
[Fact]
public async Task CountKeys()
{
using (var muxer = Create(allowAdmin: true))
{
var server = GetAnyMaster(muxer);
server.FlushDatabase(61, CommandFlags.FireAndForget);
server.FlushDatabase(62, CommandFlags.FireAndForget);
}
using (var muxer = Create())
{
RedisKey key = Me();
var db61 = muxer.GetDatabase(61);
var db62 = muxer.GetDatabase(62);
db61.StringSet("abc", "def", flags: CommandFlags.FireAndForget);
db61.StringIncrement(key, flags: CommandFlags.FireAndForget);
db62.StringIncrement(key, flags: CommandFlags.FireAndForget);
var server = GetAnyMaster(muxer);
var c0 = server.DatabaseSizeAsync(61);
var c1 = server.DatabaseSizeAsync(62);
Assert.Equal(2, await c0);
Assert.Equal(1, await c1);
}
}
[Fact]
public void MultiDatabases()
{
using (var muxer = Create())
{
RedisKey key = Me();
var db0 = muxer.GetDatabase(0);
var db1 = muxer.GetDatabase(1);
var db2 = muxer.GetDatabase(2);
db0.Ping();
db0.KeyDelete(key, CommandFlags.FireAndForget);
db1.KeyDelete(key, CommandFlags.FireAndForget);
db2.KeyDelete(key, CommandFlags.FireAndForget);
muxer.WaitAll(
db0.StringSetAsync(key, "a"),
db1.StringSetAsync(key, "b"),
db2.StringSetAsync(key, "c")
);
var a = db0.StringGetAsync(key);
var b = db1.StringGetAsync(key);
var c = db2.StringGetAsync(key);
muxer.WaitAll(a, b, c);
Assert.Equal("a", muxer.Wait(a)); // db:0
Assert.Equal("b", muxer.Wait(b)); // db:1
Assert.Equal("c", muxer.Wait(c)); // db:2
}
}
}
}
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Issues
{
public class Issue791 : TestBase
{
public Issue791(ITestOutputHelper output) : base(output) { }
[Fact]
public void PreserveAsyncOrderImplicitValue_ParsedFromConnectionString()
{
var options = ConfigurationOptions.Parse("preserveAsyncOrder=true");
Assert.True(options.PreserveAsyncOrder);
Assert.Equal("preserveAsyncOrder=True", options.ToString());
options = ConfigurationOptions.Parse("preserveAsyncOrder=false");
Assert.False(options.PreserveAsyncOrder);
Assert.Equal("preserveAsyncOrder=False", options.ToString());
}
[Fact]
public void DefaultValue_IsTrue()
{
var options = ConfigurationOptions.Parse("ssl=true");
Assert.True(options.PreserveAsyncOrder);
}
[Fact]
public void PreserveAsyncOrder_SetConnectionMultiplexerProperty()
{
var multiplexer = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServerAndPort + ",preserveAsyncOrder=false");
Assert.False(multiplexer.PreserveAsyncOrder);
}
}
}
using System.Linq;
using System.Text;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests
{
public class Keys : TestBase
{
public Keys(ITestOutputHelper output) : base (output) { }
[Fact]
public void TestScan()
{
using (var muxer = Create(allowAdmin: true))
{
const int Database = 43;
var db = muxer.GetDatabase(Database);
var server = GetAnyMaster(muxer);
server.FlushDatabase(flags: CommandFlags.FireAndForget);
const int Count = 1000;
for (int i = 0; i < Count; i++)
db.StringSet("x" + i, "y" + i, flags: CommandFlags.FireAndForget);
var count = server.Keys(Database).Count();
Assert.Equal(Count, count);
}
}
[Fact]
public void RandomKey()
{
using (var conn = Create(allowAdmin: true))
{
var db = conn.GetDatabase();
conn.GetServer(TestConfig.Current.MasterServerAndPort).FlushDatabase();
string anyKey = db.KeyRandom();
Assert.Null(anyKey);
db.StringSet("abc", "def");
byte[] keyBytes = db.KeyRandom();
Assert.Equal("abc", Encoding.UTF8.GetString(keyBytes));
}
}
[Fact]
public void Zeros()
{
using (var conn = Create())
{
var db = conn.GetDatabase();
db.KeyDelete("abc");
db.StringSet("abc", 123);
int k = (int)db.StringGet("abc");
Assert.Equal(123, k);
db.KeyDelete("abc");
int i = (int)db.StringGet("abc");
Assert.Equal(0, i);
Assert.True(db.StringGet("abc").IsNull);
int? value = (int?)db.StringGet("abc");
Assert.False(value.HasValue);
}
}
[Fact]
public void PrependAppend()
{
{
// simple
RedisKey key = "world";
var ret = key.Prepend("hello");
Assert.Equal("helloworld", (string)ret);
}
{
RedisKey key1 = "world";
RedisKey key2 = Encoding.UTF8.GetBytes("hello");
var key3 = key1.Prepend(key2);
Assert.True(object.ReferenceEquals(key1.KeyValue, key3.KeyValue));
Assert.True(object.ReferenceEquals(key2.KeyValue, key3.KeyPrefix));
Assert.Equal("helloworld", (string)key3);
}
{
RedisKey key = "hello";
var ret = key.Append("world");
Assert.Equal("helloworld", (string)ret);
}
{
RedisKey key1 = Encoding.UTF8.GetBytes("hello");
RedisKey key2 = "world";
var key3 = key1.Append(key2);
Assert.True(object.ReferenceEquals(key2.KeyValue, key3.KeyValue));
Assert.True(object.ReferenceEquals(key1.KeyValue, key3.KeyPrefix));
Assert.Equal("helloworld", (string)key3);
}
}
}
}
This diff is collapsed.
using System.Linq;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests
{
public class Sets : TestBase
{
public Sets(ITestOutputHelper output) : base (output) { }
[Fact]
public void SScan()
{
using (var conn = Create())
{
var server = GetAnyMaster(conn);
RedisKey key = "a";
var db = conn.GetDatabase();
db.KeyDelete(key);
int totalUnfiltered = 0, totalFiltered = 0;
for (int i = 0; i < 1000; i++)
{
db.SetAdd(key, i);
totalUnfiltered += i;
if (i.ToString().Contains("3")) totalFiltered += i;
}
var unfilteredActual = db.SetScan(key).Select(x => (int)x).Sum();
Assert.Equal(totalUnfiltered, unfilteredActual);
if (server.Features.Scan)
{
var filteredActual = db.SetScan(key, "*3*").Select(x => (int)x).Sum();
Assert.Equal(totalFiltered, filteredActual);
}
}
}
}
}
using System;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace StackExchange.Redis.Tests
{
public class TaskTests
{
#if DEBUG
[Theory]
[InlineData(SourceOrign.NewTCS)]
[InlineData(SourceOrign.Create)]
public void VerifyIsSyncSafe(SourceOrign origin)
{
var source = Create<int>(origin);
// Yes this looks stupid, but it's the proper pattern for how we statically init now
// ...and if we're dropping NET45 support, we can just nuke it all.
#if NET462
Assert.True(TaskSource.IsSyncSafe(source.Task));
#elif NETCOREAPP2_0
Assert.True(TaskSource.IsSyncSafe(source.Task));
#endif
}
private static TaskCompletionSource<T> Create<T>(SourceOrign origin)
{
switch (origin)
{
case SourceOrign.NewTCS: return new TaskCompletionSource<T>();
case SourceOrign.Create: return TaskSource.Create<T>(null);
default: throw new ArgumentOutOfRangeException(nameof(origin));
}
}
[Theory]
// regular framework behaviour: 2 out of 3 cause hijack
[InlineData(SourceOrign.NewTCS, AttachMode.ContinueWith, true)]
[InlineData(SourceOrign.NewTCS, AttachMode.ContinueWithExecSync, false)]
[InlineData(SourceOrign.NewTCS, AttachMode.Await, true)]
// Create is just a wrapper of ^^^; expect the same
[InlineData(SourceOrign.Create, AttachMode.ContinueWith, true)]
[InlineData(SourceOrign.Create, AttachMode.ContinueWithExecSync, false)]
[InlineData(SourceOrign.Create, AttachMode.Await, true)]
public void TestContinuationHijacking(SourceOrign origin, AttachMode attachMode, bool expectHijack)
{
TaskCompletionSource<int> source = Create<int>(origin);
int settingThread = Environment.CurrentManagedThreadId;
var state = new AwaitState();
state.Attach(source.Task, attachMode);
source.TrySetResult(123);
state.Wait(); // waits for the continuation to run
int from = state.Thread;
Assert.NotEqual(-1, from); // not set
if (expectHijack)
{
Assert.True(settingThread != from, $"expected hijack; didn't happen, Origin={settingThread}, Final={from}");
}
else
{
Assert.True(settingThread == from, $"setter was hijacked, Origin={settingThread}, Final={from}");
}
}
public enum SourceOrign
{
NewTCS,
Create
}
public enum AttachMode
{
ContinueWith,
ContinueWithExecSync,
Await
}
private class AwaitState
{
public int Thread => continuationThread;
private volatile int continuationThread = -1;
private readonly ManualResetEventSlim evt = new ManualResetEventSlim();
public void Wait()
{
if (!evt.Wait(5000)) throw new TimeoutException();
}
public void Attach(Task task, AttachMode attachMode)
{
switch (attachMode)
{
case AttachMode.ContinueWith:
task.ContinueWith(Continue);
break;
case AttachMode.ContinueWithExecSync:
task.ContinueWith(Continue, TaskContinuationOptions.ExecuteSynchronously);
break;
case AttachMode.Await:
DoAwait(task);
break;
default:
throw new ArgumentOutOfRangeException(nameof(attachMode));
}
}
private void Continue(Task task)
{
continuationThread = Environment.CurrentManagedThreadId;
evt.Set();
}
private async void DoAwait(Task task)
{
await task.ConfigureAwait(false);
continuationThread = Environment.CurrentManagedThreadId;
evt.Set();
}
}
#endif
}
}
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests
{
public class VPNTest : TestBase
{
public VPNTest(ITestOutputHelper output) : base (output) { }
public static IEnumerable<object[]> GetVPNConfigs =>
TestConfig.Current.VPNConfigs?.Select(c => new object[] { c })
?? new[] { new[] { "" } }; // xUnit errors on an empty theory and I'm tired of it
[Theory]
[MemberData(nameof(GetVPNConfigs))]
public void Execute(string config)
{
if (string.IsNullOrEmpty(config))
{
Skip.IfNoConfig(nameof(TestConfig.Config.VPNConfigs), TestConfig.Current.VPNConfigs);
}
for (int i = 0; i < 50; i++)
{
var log = new StringWriter();
try
{
var options = ConfigurationOptions.Parse(config);
options.SyncTimeout = 3000;
options.ConnectRetry = 5;
using (var conn = ConnectionMultiplexer.Connect(options, log))
{
var ttl = conn.GetDatabase().Ping();
Output.WriteLine(ttl.ToString());
}
}
catch
{
Output.WriteLine(log.ToString());
throw;
}
Output.WriteLine("");
Output.WriteLine("===");
Output.WriteLine("");
}
}
}
}
This diff is collapsed.
// Yes, this is embarassing. However, in .NET Core the including AssemblyInfo (ifdef'd or not) will screw with
// your version numbers. Therefore, we need to move the attribute out into another file...this file.
// When .csproj merges in, this should be able to return to Properties/AssemblyInfo.cs
#if !STRONG_NAME
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("StackExchange.Redis.Tests")]
#endif
\ No newline at end of file
namespace StackExchange.Redis
{
public partial class ConnectionMultiplexer
{
internal SocketManager SocketManager => socketManager;
private SocketManager socketManager;
private bool ownsSocketManager;
partial void OnCreateReaderWriter(ConfigurationOptions configuration)
{
ownsSocketManager = configuration.SocketManager == null;
socketManager = configuration.SocketManager ?? new SocketManager(ClientName, configuration.HighPrioritySocketThreads);
}
partial void OnCloseReaderWriter()
{
if (ownsSocketManager) socketManager?.Dispose();
socketManager = null;
}
internal void RequestWrite(PhysicalBridge bridge, bool forced)
{
if (bridge == null) return;
var tmp = SocketManager;
if (tmp != null)
{
Trace("Requesting write: " + bridge.Name);
tmp.RequestWrite(bridge, forced);
}
}
partial void OnWriterCreated();
}
}
This diff is collapsed.
dotnet msbuild "/t:Restore;Build;Pack" "/p:NuGetBuildTasksPackTargets='000'" "/p:PackageOutputPath=nupkgs" "/p:Configuration=Release"
$key = Import-StrongNameKeyPair -KeyFile StackExchange.Redis.snk
dir StackExchange.Redis*/bin/Release/StackExchange.Redis.dll | Set-StrongName -KeyPair $key -Verbose -NoBackup -Force
nuget pack StackExchange.Redis.StrongName.nuspec
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
1.2.7-alpha
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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