Add dotnet-sql-cache

This commit is contained in:
Nate McMaster 2016-09-19 11:41:35 -07:00
parent 8f2eccbd36
commit fb54566ff5
No known key found for this signature in database
GPG Key ID: BD729980AA6A21BD
8 changed files with 302 additions and 0 deletions

View File

@ -33,6 +33,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Secret
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.SecretManager.Tools.Tests", "test\Microsoft.Extensions.SecretManager.Tools.Tests\Microsoft.Extensions.SecretManager.Tools.Tests.xproj", "{7B331122-83B1-4F08-A119-DC846959844C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Caching.SqlConfig.Tools", "src\Microsoft.Extensions.Caching.SqlConfig.Tools\Microsoft.Extensions.Caching.SqlConfig.Tools.xproj", "{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -75,6 +77,10 @@ Global
{7B331122-83B1-4F08-A119-DC846959844C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B331122-83B1-4F08-A119-DC846959844C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B331122-83B1-4F08-A119-DC846959844C}.Release|Any CPU.Build.0 = Release|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -90,5 +96,6 @@ Global
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA} = {2876B12E-5841-4792-85A8-2929AEE11885}
{8730E848-CA0F-4E0A-9A2F-BC22AD0B2C4E} = {66517987-2A5A-4330-B130-207039378FD4}
{7B331122-83B1-4F08-A119-DC846959844C} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9} = {66517987-2A5A-4330-B130-207039378FD4}
EndGlobalSection
EndGlobal

View File

@ -6,6 +6,7 @@
"packages": {
"Microsoft.DotNet.Watcher.Tools": { },
"Microsoft.DotNet.Watcher.Core": { },
"Microsoft.Extensions.Caching.SqlConfig.Tools": { },
"Microsoft.Extensions.SecretManager.Tools": { }
}
},

View File

@ -8,5 +8,6 @@ The project contains command-line tools for the .NET Core SDK.
- [dotnet-watch](src/Microsoft.DotNet.Watcher.Tools/)
- [dotnet-user-secrets](src/Microsoft.Extensions.SecretManager.Tools/)
- [dotnet-sql-cache](src/Microsoft.Extensions.Caching.SqlConfig.Tools/)
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.

View File

@ -0,0 +1,17 @@
<?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)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>53f3b53d-303a-4daa-9c38-4f55195fa5b9</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,157 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Logging;
namespace Microsoft.Extensions.Caching.SqlConfig.Tools
{
public class Program
{
private string _connectionString = null;
private string _schemaName = null;
private string _tableName = null;
private readonly ILogger _logger;
public Program()
{
var loggerFactory = new LoggerFactory();
loggerFactory.AddConsole();
_logger = loggerFactory.CreateLogger<Program>();
}
public static int Main(string[] args)
{
return new Program().Run(args);
}
public int Run(string[] args)
{
try
{
var description = "Creates table and indexes in Microsoft SQL Server database " +
"to be used for distributed caching";
var app = new CommandLineApplication();
app.Name = "dotnet-sql-cache";
app.Description = description;
app.HelpOption("-?|-h|--help");
app.Command("create", command =>
{
command.Description = description;
var connectionStringArg = command.Argument(
"[connectionString]",
"The connection string to connect to the database.");
var schemaNameArg = command.Argument("[schemaName]", "Name of the table schema.");
var tableNameArg = command.Argument("[tableName]", "Name of the table to be created.");
command.HelpOption("-?|-h|--help");
command.OnExecute(() =>
{
if (string.IsNullOrEmpty(connectionStringArg.Value)
|| string.IsNullOrEmpty(schemaNameArg.Value)
|| string.IsNullOrEmpty(tableNameArg.Value))
{
_logger.LogWarning("Invalid input");
app.ShowHelp();
return 2;
}
_connectionString = connectionStringArg.Value;
_schemaName = schemaNameArg.Value;
_tableName = tableNameArg.Value;
return CreateTableAndIndexes();
});
});
// Show help information if no subcommand/option was specified.
app.OnExecute(() =>
{
app.ShowHelp();
return 2;
});
return app.Execute(args);
}
catch (Exception exception)
{
_logger.LogCritical("An error occurred. {ErrorMessage}", exception.Message);
return 1;
}
}
private int CreateTableAndIndexes()
{
ValidateConnectionString();
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
var sqlQueries = new SqlQueries(_schemaName, _tableName);
var command = new SqlCommand(sqlQueries.TableInfo, connection);
using (var reader = command.ExecuteReader(CommandBehavior.SingleRow))
{
if (reader.Read())
{
_logger.LogWarning(
$"Table with schema '{_schemaName}' and name '{_tableName}' already exists. " +
"Provide a different table name and try again.");
return 1;
}
}
using (var transaction = connection.BeginTransaction())
{
try
{
command = new SqlCommand(sqlQueries.CreateTable, connection, transaction);
command.ExecuteNonQuery();
command = new SqlCommand(
sqlQueries.CreateNonClusteredIndexOnExpirationTime,
connection,
transaction);
command.ExecuteNonQuery();
transaction.Commit();
_logger.LogInformation("Table and index were created successfully.");
}
catch (Exception ex)
{
_logger.LogError(
"An error occurred while trying to create the table and index. {ErrorMessage}",
ex.Message);
transaction.Rollback();
return 1;
}
}
}
return 0;
}
private void ValidateConnectionString()
{
try
{
new SqlConnectionStringBuilder(_connectionString);
}
catch (Exception ex)
{
throw new ArgumentException(
$"Invalid Sql server connection string '{_connectionString}'. {ex.Message}", ex);
}
}
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Reflection;
using System.Resources;
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: NeutralResourcesLanguage("en-us")]
[assembly: AssemblyCompany("Microsoft Corporation.")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyProduct("Microsoft .NET Extensions")]

View File

@ -0,0 +1,67 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.Extensions.Caching.SqlConfig.Tools
{
internal class SqlQueries
{
private const string CreateTableFormat = "CREATE TABLE {0}(" +
// Maximum size of primary key column is 900 bytes (898 bytes from key + 2 additional bytes used by the
// Sql Server). In the case where the key is greater than 898 bytes, then it gets truncated.
// - Add collation to the key column to make it case-sensitive
"Id nvarchar(449) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, " +
"Value varbinary(MAX) NOT NULL, " +
"ExpiresAtTime datetimeoffset NOT NULL, " +
"SlidingExpirationInSeconds bigint NULL," +
"AbsoluteExpiration datetimeoffset NULL, " +
"CONSTRAINT pk_Id PRIMARY KEY (Id))";
private const string CreateNonClusteredIndexOnExpirationTimeFormat
= "CREATE NONCLUSTERED INDEX Index_ExpiresAtTime ON {0}(ExpiresAtTime)";
private const string TableInfoFormat =
"SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE " +
"FROM INFORMATION_SCHEMA.TABLES " +
"WHERE TABLE_SCHEMA = '{0}' " +
"AND TABLE_NAME = '{1}'";
public SqlQueries(string schemaName, string tableName)
{
if (string.IsNullOrEmpty(schemaName))
{
throw new ArgumentException("Schema name cannot be empty or null");
}
if (string.IsNullOrEmpty(tableName))
{
throw new ArgumentException("Table name cannot be empty or null");
}
var tableNameWithSchema = string.Format(
"{0}.{1}", DelimitIdentifier(schemaName), DelimitIdentifier(tableName));
CreateTable = string.Format(CreateTableFormat, tableNameWithSchema);
CreateNonClusteredIndexOnExpirationTime = string.Format(
CreateNonClusteredIndexOnExpirationTimeFormat,
tableNameWithSchema);
TableInfo = string.Format(TableInfoFormat, EscapeLiteral(schemaName), EscapeLiteral(tableName));
}
public string CreateTable { get; }
public string CreateNonClusteredIndexOnExpirationTime { get; }
public string TableInfo { get; }
// From EF's SqlServerQuerySqlGenerator
private string DelimitIdentifier(string identifier)
{
return "[" + identifier.Replace("]", "]]") + "]";
}
private string EscapeLiteral(string literal)
{
return literal.Replace("'", "''");
}
}
}

View File

@ -0,0 +1,41 @@
{
"version": "1.0.0-*",
"dependencies": {
"Microsoft.Extensions.CommandLineUtils": "1.1.0-*",
"Microsoft.Extensions.Logging": "1.1.0-*",
"Microsoft.Extensions.Logging.Console": "1.1.0-*",
"System.Data.SqlClient": "4.1.0-*"
},
"description": "Command line tool to create tables and indexes in a Microsoft SQL Server database for distributed caching.",
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0-*",
"type": "platform"
}
}
}
},
"buildOptions": {
"outputName": "dotnet-sql-cache",
"emitEntryPoint": true,
"warningsAsErrors": true,
"keyFile": "../../tools/Key.snk",
"nowarn": [
"CS1591"
],
"xmlDoc": true
},
"packOptions": {
"repository": {
"type": "git",
"url": "https://github.com/aspnet/DotNetTools"
},
"tags": [
"cache",
"distributedcache",
"sqlserver"
]
}
}