Merge pull request #249 from Azure/andreabatina/netstandard
Updated Elastic Scale Client project to target .Net Standard 2.0 along with .net6.0 and updated sample apps to support AAD auth
This commit is contained in:
Коммит
8838b77f8d
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29709.97
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.9.34728.123
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.SqlDatabase.ElasticScale.Client", "Src\ElasticScale.Client\Microsoft.Azure.SqlDatabase.ElasticScale.Client.csproj", "{4C3B3EC4-5702-469E-800E-313FB27A0A2B}"
|
||||
EndProject
|
||||
|
@ -11,7 +11,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.SqlDatabase
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.SqlDatabase.ElasticScale.Query.UnitTests", "Test\ElasticScale.Query.UnitTests\Microsoft.Azure.SqlDatabase.ElasticScale.Query.UnitTests.csproj", "{74CEE77F-D2C7-4B8B-9411-8F97F4E803FA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElasticScaleStarterKit", "Samples\ElasticScaleStarterKit\ElasticScaleStarterKit.csproj", "{115A0283-AC42-4D37-97F2-106D168E04D2}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElasticScaleStarterKit", "Samples\ElasticScaleStarterKit\ElasticScaleStarterKit.csproj", "{115A0283-AC42-4D37-97F2-106D168E04D2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkMultiTenant", "Samples\EFMultiTenant\EntityFrameworkMultiTenant.csproj", "{BC17F3EF-FEB4-4186-9342-E2D18F1E56AB}"
|
||||
EndProject
|
||||
|
@ -21,6 +21,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElasticDapper", "Samples\Da
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShardSqlCmd", "Samples\ShardSqlCmd\ShardSqlCmd.csproj", "{B210D6E5-7171-4117-9C77-3F2CB59D04D8}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{F7A2F005-3B53-495D-B0EC-CE41FAB4DC75}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9C78C56E-7738-4D3B-9722-67A4CB028A75}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{A0CBF252-A093-4448-BC10-1A1EF10DB64E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Cover|Any CPU = Cover|Any CPU
|
||||
|
@ -143,6 +149,17 @@ Global
|
|||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{4C3B3EC4-5702-469E-800E-313FB27A0A2B} = {A0CBF252-A093-4448-BC10-1A1EF10DB64E}
|
||||
{9336E9E7-19BF-49AC-92E3-19FA6B98921E} = {9C78C56E-7738-4D3B-9722-67A4CB028A75}
|
||||
{BEA6F911-BA98-462C-99AF-3B0595DE2307} = {9C78C56E-7738-4D3B-9722-67A4CB028A75}
|
||||
{74CEE77F-D2C7-4B8B-9411-8F97F4E803FA} = {9C78C56E-7738-4D3B-9722-67A4CB028A75}
|
||||
{115A0283-AC42-4D37-97F2-106D168E04D2} = {F7A2F005-3B53-495D-B0EC-CE41FAB4DC75}
|
||||
{BC17F3EF-FEB4-4186-9342-E2D18F1E56AB} = {F7A2F005-3B53-495D-B0EC-CE41FAB4DC75}
|
||||
{8EB66613-D5A2-4683-B91A-6AF904FD6B70} = {F7A2F005-3B53-495D-B0EC-CE41FAB4DC75}
|
||||
{AC76C04B-881E-4CB9-B491-4D19B68459F1} = {F7A2F005-3B53-495D-B0EC-CE41FAB4DC75}
|
||||
{B210D6E5-7171-4117-9C77-3F2CB59D04D8} = {F7A2F005-3B53-495D-B0EC-CE41FAB4DC75}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {24237160-521B-4CD0-87AB-8994A5E50BE2}
|
||||
EndGlobalSection
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace ElasticDapper
|
||||
{
|
||||
// Let's use the standard blogging class
|
||||
public class Blog
|
||||
{
|
||||
public int BlogId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace ElasticDapper
|
||||
{
|
||||
// Let's use the standard blogging class
|
||||
public class Blog
|
||||
{
|
||||
public long BlogId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,14 +5,13 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Build.props))\Build.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EnterpriseLibrary.TransientFaultHandling.Data.NetCore" Version="6.0.1312" />
|
||||
<PackageReference Include="EnterpriseLibrary.TransientFaultHandling.NetCore" Version="6.0.1312" />
|
||||
<PackageReference Include="Microsoft.Azure.SqlDatabase.ElasticScale.Client" version="2.3.0" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
|
||||
<PackageReference Include="Dapper" Version="2.1.28" />
|
||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||
<PackageReference Include="DapperExtensions" Version="1.7.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="LICENSE" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Src\ElasticScale.Client\Microsoft.Azure.SqlDatabase.ElasticScale.Client.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft
|
||||
Copyright (c) 2024 Microsoft
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using Dapper;
|
||||
using DapperExtensions;
|
||||
using Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// This sample illustrates the adjustments that need to be made to use Dapper
|
||||
|
@ -28,9 +28,8 @@ namespace ElasticDapper
|
|||
private static string s_shardmapmgrdb = "[YourShardMapManagerDatabaseName]";
|
||||
private static string s_shard1 = "[YourShard01DatabaseName]";
|
||||
private static string s_shard2 = "[YourShard02DatabaseName]";
|
||||
private static string s_userName = "YourUserName";
|
||||
private static string s_password = "YourPassword";
|
||||
private static string s_applicationName = "ESC_Dapv1.0";
|
||||
private static SqlAuthenticationMethod s_authenticationMethod = SqlAuthenticationMethod.ActiveDirectoryDefault;
|
||||
|
||||
// Just two tenants for now.
|
||||
// Those we will allocate to shards.
|
||||
|
@ -39,19 +38,14 @@ namespace ElasticDapper
|
|||
|
||||
public static void Main()
|
||||
{
|
||||
SqlConnectionStringBuilder connStrBldr = new SqlConnectionStringBuilder
|
||||
{
|
||||
UserID = s_userName,
|
||||
Password = s_password,
|
||||
ApplicationName = s_applicationName
|
||||
};
|
||||
SqlConnectionStringBuilder connStrBldr = GetConnectionStringBuilder();
|
||||
|
||||
// Bootstrap the shard map manager, register shards, and store mappings of tenants to shards
|
||||
// Note that you can keep working with existing shard maps. There is no need to
|
||||
// re-create and populate the shard map from scratch every time.
|
||||
Sharding shardingLayer = new Sharding(s_server, s_shardmapmgrdb, connStrBldr.ConnectionString);
|
||||
shardingLayer.RegisterNewShard(s_server, s_shard1, connStrBldr.ConnectionString, s_tenantId1);
|
||||
shardingLayer.RegisterNewShard(s_server, s_shard2, connStrBldr.ConnectionString, s_tenantId2);
|
||||
shardingLayer.RegisterNewShard(s_server, s_shard1, s_tenantId1);
|
||||
shardingLayer.RegisterNewShard(s_server, s_shard2, s_tenantId2);
|
||||
|
||||
// Create schema on each shard.
|
||||
foreach (string shard in new[] {s_shard1, s_shard2})
|
||||
|
@ -67,89 +61,74 @@ namespace ElasticDapper
|
|||
Console.Write("Enter a name for a new Blog: ");
|
||||
var name = Console.ReadLine();
|
||||
|
||||
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(
|
||||
key: s_tenantId1,
|
||||
connectionString: connStrBldr.ConnectionString,
|
||||
options: ConnectionOptions.Validate))
|
||||
{
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(
|
||||
key: s_tenantId1,
|
||||
connectionString: connStrBldr.ConnectionString,
|
||||
options: ConnectionOptions.Validate))
|
||||
{
|
||||
var blog = new Blog { Name = name };
|
||||
sqlconn.Insert(blog);
|
||||
}
|
||||
});
|
||||
var blog = new Blog { Name = name };
|
||||
sqlconn.Insert(blog);
|
||||
}
|
||||
|
||||
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(
|
||||
key: s_tenantId1,
|
||||
connectionString: connStrBldr.ConnectionString,
|
||||
options: ConnectionOptions.Validate))
|
||||
{
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(
|
||||
key: s_tenantId1,
|
||||
connectionString: connStrBldr.ConnectionString,
|
||||
options: ConnectionOptions.Validate))
|
||||
{
|
||||
// Display all Blogs for tenant 1
|
||||
IEnumerable<Blog> result = sqlconn.Query<Blog>(@"
|
||||
SELECT *
|
||||
FROM Blog
|
||||
ORDER BY Name");
|
||||
// Display all Blogs for tenant 1
|
||||
IEnumerable<Blog> result = sqlconn.Query<Blog>(@"
|
||||
SELECT *
|
||||
FROM Blog
|
||||
ORDER BY Name");
|
||||
|
||||
Console.WriteLine("All blogs for tenant id {0}:", s_tenantId1);
|
||||
foreach (var item in result)
|
||||
{
|
||||
Console.WriteLine(item.Name);
|
||||
}
|
||||
Console.WriteLine("All blogs for tenant id {0}:", s_tenantId1);
|
||||
foreach (var item in result)
|
||||
{
|
||||
Console.WriteLine(item.Name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Do work for tenant 2 :-)
|
||||
// Here I am going to illustrate how to integrate
|
||||
// with DapperExtensions which saves us the T-SQL
|
||||
//
|
||||
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(
|
||||
key: s_tenantId2,
|
||||
connectionString: connStrBldr.ConnectionString,
|
||||
options: ConnectionOptions.Validate))
|
||||
{
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(
|
||||
key: s_tenantId2,
|
||||
connectionString: connStrBldr.ConnectionString,
|
||||
options: ConnectionOptions.Validate))
|
||||
// Display all Blogs for tenant 2
|
||||
IEnumerable<Blog> result = sqlconn.GetList<Blog>();
|
||||
Console.WriteLine("All blogs for tenant id {0}:", s_tenantId2);
|
||||
foreach (var item in result)
|
||||
{
|
||||
// Display all Blogs for tenant 2
|
||||
IEnumerable<Blog> result = sqlconn.GetList<Blog>();
|
||||
Console.WriteLine("All blogs for tenant id {0}:", s_tenantId2);
|
||||
foreach (var item in result)
|
||||
{
|
||||
Console.WriteLine(item.Name);
|
||||
}
|
||||
Console.WriteLine(item.Name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create and save a new Blog
|
||||
Console.Write("Enter a name for a new Blog: ");
|
||||
var name2 = Console.ReadLine();
|
||||
|
||||
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(
|
||||
key: s_tenantId2,
|
||||
connectionString: connStrBldr.ConnectionString,
|
||||
options: ConnectionOptions.Validate))
|
||||
{
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(
|
||||
key: s_tenantId2,
|
||||
connectionString: connStrBldr.ConnectionString,
|
||||
options: ConnectionOptions.Validate))
|
||||
{
|
||||
var blog = new Blog { Name = name2 };
|
||||
sqlconn.Insert(blog);
|
||||
}
|
||||
});
|
||||
var blog = new Blog { Name = name2 };
|
||||
sqlconn.Insert(blog);
|
||||
}
|
||||
|
||||
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(s_tenantId2, connStrBldr.ConnectionString, ConnectionOptions.Validate))
|
||||
{
|
||||
using (SqlConnection sqlconn = shardingLayer.ShardMap.OpenConnectionForKey(s_tenantId2, connStrBldr.ConnectionString, ConnectionOptions.Validate))
|
||||
// Display all Blogs for tenant 2
|
||||
IEnumerable<Blog> result = sqlconn.GetList<Blog>();
|
||||
Console.WriteLine("All blogs for tenant id {0}:", s_tenantId2);
|
||||
foreach (var item in result)
|
||||
{
|
||||
// Display all Blogs for tenant 2
|
||||
IEnumerable<Blog> result = sqlconn.GetList<Blog>();
|
||||
Console.WriteLine("All blogs for tenant id {0}:", s_tenantId2);
|
||||
foreach (var item in result)
|
||||
{
|
||||
Console.WriteLine(item.Name);
|
||||
}
|
||||
Console.WriteLine(item.Name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Console.WriteLine("Press any key to exit...");
|
||||
Console.ReadKey();
|
||||
|
@ -157,14 +136,10 @@ namespace ElasticDapper
|
|||
|
||||
private static void CreateSchema(string shardName)
|
||||
{
|
||||
SqlConnectionStringBuilder connStrBldr = new SqlConnectionStringBuilder
|
||||
{
|
||||
UserID = s_userName,
|
||||
Password = s_password,
|
||||
ApplicationName = s_applicationName,
|
||||
DataSource = s_server,
|
||||
InitialCatalog = shardName
|
||||
};
|
||||
SqlConnectionStringBuilder connStrBldr = GetConnectionStringBuilder();
|
||||
|
||||
connStrBldr.DataSource = s_server;
|
||||
connStrBldr.InitialCatalog = shardName;
|
||||
|
||||
using (SqlConnection conn = new SqlConnection(connStrBldr.ToString()))
|
||||
{
|
||||
|
@ -178,5 +153,19 @@ namespace ElasticDapper
|
|||
)");
|
||||
}
|
||||
}
|
||||
|
||||
private static SqlConnectionStringBuilder GetConnectionStringBuilder()
|
||||
{
|
||||
var connBuilder = new SqlConnectionStringBuilder
|
||||
{
|
||||
Authentication = s_authenticationMethod,
|
||||
ApplicationName = s_applicationName,
|
||||
CommandTimeout = 60,
|
||||
ConnectTimeout = 60,
|
||||
TrustServerCertificate = true
|
||||
};
|
||||
|
||||
return connBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace ElasticDapper
|
||||
{
|
||||
|
@ -25,41 +25,41 @@ namespace ElasticDapper
|
|||
ShardMapManager smm;
|
||||
if (!ShardMapManagerFactory.TryGetSqlShardMapManager(connStrBldr.ConnectionString, ShardMapManagerLoadPolicy.Lazy, out smm))
|
||||
{
|
||||
this.ShardMapManager = ShardMapManagerFactory.CreateSqlShardMapManager(connStrBldr.ConnectionString);
|
||||
ShardMapManager = ShardMapManagerFactory.CreateSqlShardMapManager(connStrBldr.ConnectionString);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ShardMapManager = smm;
|
||||
ShardMapManager = smm;
|
||||
}
|
||||
|
||||
ListShardMap<int> sm;
|
||||
if (!ShardMapManager.TryGetListShardMap<int>("ElasticScaleWithDapper", out sm))
|
||||
{
|
||||
this.ShardMap = ShardMapManager.CreateListShardMap<int>("ElasticScaleWithDapper");
|
||||
ShardMap = ShardMapManager.CreateListShardMap<int>("ElasticScaleWithDapper");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ShardMap = sm;
|
||||
ShardMap = sm;
|
||||
}
|
||||
}
|
||||
|
||||
// Enter a new shard - i.e. an empty database - to the shard map, allocate a first tenant to it
|
||||
public void RegisterNewShard(string server, string database, string connstr, int key)
|
||||
public void RegisterNewShard(string server, string database, int key)
|
||||
{
|
||||
Shard shard;
|
||||
ShardLocation shardLocation = new ShardLocation(server, database);
|
||||
|
||||
if (!this.ShardMap.TryGetShard(shardLocation, out shard))
|
||||
if (!ShardMap.TryGetShard(shardLocation, out shard))
|
||||
{
|
||||
shard = this.ShardMap.CreateShard(shardLocation);
|
||||
shard = ShardMap.CreateShard(shardLocation);
|
||||
}
|
||||
|
||||
// Register the mapping of the tenant to the shard in the shard map.
|
||||
// After this step, DDR on the shard map can be used
|
||||
PointMapping<int> mapping;
|
||||
if (!this.ShardMap.TryGetMappingForKey(key, out mapping))
|
||||
if (!ShardMap.TryGetMappingForKey(key, out mapping))
|
||||
{
|
||||
this.ShardMap.CreatePointMapping(key, shard);
|
||||
ShardMap.CreatePointMapping(key, shard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling;
|
||||
|
||||
namespace ElasticDapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper methods for interacting with SQL Databases.
|
||||
/// </summary>
|
||||
internal static class SqlDatabaseUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the retry policy to use for connections to SQL Server.
|
||||
/// </summary>
|
||||
public static RetryPolicy SqlRetryPolicy
|
||||
{
|
||||
get { return new RetryPolicy<SqlDatabaseTransientErrorDetectionStrategy>(10, TimeSpan.FromSeconds(5)); }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<!--
|
||||
The SQL server to connect to. Defaults to localdb default instance.
|
||||
When using Azure SQL DB, use the fully qualified DNS name like "abcdefghij.database.windows.net"
|
||||
-->
|
||||
<!-- Azure SQL server: <add key="ServerName" value="your-server-name.database.windows.net" /> -->
|
||||
<!-- Local SQL server: <add key="ServerName" value="." /> -->
|
||||
<add key="ServerName" value="." />
|
||||
|
||||
<!--
|
||||
If TrustServerCertificate=true, the connection process skips the trust chain validation.
|
||||
In this case, the application connects even if the certificate cannot be verified.
|
||||
Using TrustServerCertificate=false enforces certificate validation and is a best practice.
|
||||
-->
|
||||
<add key="TrustServerCertificate" value="true" />
|
||||
|
||||
<!--
|
||||
Set to one of the 10 SqlAuthenticationMethod enum 'Authentication Modes':
|
||||
|
||||
https://learn.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlauthenticationmethod
|
||||
|
||||
At time of writing, these are:
|
||||
|
||||
ActiveDirectoryDefault
|
||||
ActiveDirectoryMSI (NOTE: Is the old name of the below)
|
||||
ActiveDirectoryManagedIdentity (NOTE: Is the new name for MSI (above))
|
||||
ActiveDirectoryDeviceCodeFlow
|
||||
ActiveDirectoryServicePrincipal
|
||||
ActiveDirectoryInteractive
|
||||
ActiveDirectoryIntegrated (NOTE: behaves as Windows Auth locally)
|
||||
ActiveDirectoryPassword
|
||||
SqlPassword
|
||||
NotSpecified (NOTE: behaves as SqlPassword, i.e. SQL Auth)
|
||||
|
||||
-->
|
||||
<add key="SqlAuthenticationMethod" value="ActiveDirectoryIntegrated" />
|
||||
|
||||
<!--
|
||||
Credentials for connecting to your SQL server if using ActiveDirectoryPassword, SqlPassword (or NotSpecified which is the same as SqlPassword) or ActiveDirectoryServicePrincipal.
|
||||
|
||||
- For ActiveDirectoryServicePrincipal the UserName is the Azure Entra 'App Registration' 'Application (client) ID' and the Password is 'Value' of an 'App Registration' 'Secret'.
|
||||
- For ActiveDirectoryManagedIdentity/ActiveDirectoryMSI, if "user-assigned", the UserName is the Client ID (a GUID) of the Managed Identity (and Password must be "")
|
||||
-->
|
||||
<add key="UserName" value="" />
|
||||
<add key="Password" value="" />
|
||||
|
||||
<!--
|
||||
The database edition to use when creating databases for this sample in Azure SQL DB.
|
||||
-->
|
||||
<add key="DatabaseEdition" value="Basic" />
|
||||
</appSettings>
|
||||
</configuration>
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Data.SqlClient;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace ElasticScaleStarterKit
|
||||
{
|
||||
|
@ -76,17 +77,21 @@ namespace ElasticScaleStarterKit
|
|||
string userId = ConfigurationManager.AppSettings["UserName"] ?? string.Empty;
|
||||
string password = ConfigurationManager.AppSettings["Password"] ?? string.Empty;
|
||||
|
||||
// Get Integrated Security from the app.config file.
|
||||
// If it exists, then parse it (throw exception on failure), otherwise default to false.
|
||||
string integratedSecurityString = ConfigurationManager.AppSettings["IntegratedSecurity"];
|
||||
bool integratedSecurity = integratedSecurityString != null && bool.Parse(integratedSecurityString);
|
||||
string trustServerCertificateString = ConfigurationManager.AppSettings["TrustServerCertificate"] ?? string.Empty;
|
||||
|
||||
var trustServerCertificate = trustServerCertificateString != null && bool.Parse(trustServerCertificateString);
|
||||
|
||||
// Get Sql Auth method from the app.config file.
|
||||
SqlAuthenticationMethod authMethod;
|
||||
var enumString = ConfigurationManager.AppSettings["SqlAuthenticationMethod"];
|
||||
if (!Enum.TryParse(enumString, out authMethod))
|
||||
{
|
||||
throw new ArgumentException("Invalid SqlAuthenticationMethod in app.config");
|
||||
}
|
||||
|
||||
SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder
|
||||
{
|
||||
// DDR and MSQ require credentials to be set
|
||||
UserID = userId,
|
||||
Password = password,
|
||||
IntegratedSecurity = integratedSecurity,
|
||||
Authentication = authMethod,
|
||||
|
||||
// DataSource and InitialCatalog cannot be set for DDR and MSQ APIs, because these APIs will
|
||||
// determine the DataSource and InitialCatalog for you.
|
||||
|
@ -97,10 +102,52 @@ namespace ElasticScaleStarterKit
|
|||
//
|
||||
// Other SqlClient ConnectionString keywords are supported.
|
||||
|
||||
TrustServerCertificate = trustServerCertificate,
|
||||
|
||||
ApplicationName = "ESC_SKv1.0",
|
||||
ConnectTimeout = 30
|
||||
|
||||
// Set to 120 if ActiveDirectoryDeviceCodeFlow
|
||||
// not even the fastest cut and pasters can get the device code
|
||||
// into the browser and click through in 30 seconds.
|
||||
ConnectTimeout = authMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow ? 120 : 30,
|
||||
};
|
||||
|
||||
// DEVNOTE: NotSpecified behaves the same as SqlPassword (i.e. Sql Auth)
|
||||
if (authMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity ||
|
||||
authMethod == SqlAuthenticationMethod.ActiveDirectoryMSI ||
|
||||
authMethod == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal ||
|
||||
authMethod == SqlAuthenticationMethod.ActiveDirectoryPassword ||
|
||||
authMethod == SqlAuthenticationMethod.SqlPassword ||
|
||||
authMethod == SqlAuthenticationMethod.NotSpecified)
|
||||
{
|
||||
// DDR and MSQ require credentials to be set
|
||||
|
||||
// ActiveDirectoryManagedIdentity / ActiveDirectoryMSI when using a System Managed System Identify does not use a UserID
|
||||
if (authMethod != SqlAuthenticationMethod.ActiveDirectoryManagedIdentity &&
|
||||
authMethod != SqlAuthenticationMethod.ActiveDirectoryMSI)
|
||||
{
|
||||
if (userId == string.Empty)
|
||||
{
|
||||
throw new ArgumentException("UserName must be specified in app.config");
|
||||
}
|
||||
}
|
||||
|
||||
connStr.UserID = userId;
|
||||
|
||||
// ActiveDirectoryManagedIdentity/ActiveDirectoryMSI does not use a Password.
|
||||
if (authMethod != SqlAuthenticationMethod.ActiveDirectoryManagedIdentity &&
|
||||
authMethod != SqlAuthenticationMethod.ActiveDirectoryMSI)
|
||||
{
|
||||
if (password == string.Empty)
|
||||
{
|
||||
throw new ArgumentException("Password must be specified in app.config");
|
||||
}
|
||||
|
||||
connStr.Password = password;
|
||||
}
|
||||
}
|
||||
|
||||
return connStr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@
|
|||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace ElasticScaleStarterKit
|
||||
{
|
||||
|
@ -64,21 +64,23 @@ namespace ElasticScaleStarterKit
|
|||
// Open and execute the command with retry for transient faults. Note that if the command fails, the connection is closed, so
|
||||
// the entire block is wrapped in a retry. This means that only one command should be executed per block, since if we had multiple
|
||||
// commands then the first command may be executed multiple times if later commands fail.
|
||||
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
|
||||
|
||||
// Looks up the key in the shard map and opens a connection to the shard
|
||||
using (SqlConnection conn = shardMap.OpenConnectionForKey(customerId, credentialsConnectionString))
|
||||
{
|
||||
// Looks up the key in the shard map and opens a connection to the shard
|
||||
using (SqlConnection conn = shardMap.OpenConnectionForKey(customerId, credentialsConnectionString))
|
||||
// Create a simple command that will insert or update the customer information
|
||||
using (SqlCommand cmd = conn.CreateCommand())
|
||||
{
|
||||
// Create a simple command that will insert or update the customer information
|
||||
SqlCommand cmd = conn.CreateCommand();
|
||||
cmd.RetryLogicProvider = SqlDatabaseUtils.SqlRetryProvider;
|
||||
|
||||
cmd.CommandText = @"
|
||||
IF EXISTS (SELECT 1 FROM Customers WHERE CustomerId = @customerId)
|
||||
UPDATE Customers
|
||||
SET Name = @name, RegionId = @regionId
|
||||
WHERE CustomerId = @customerId
|
||||
ELSE
|
||||
INSERT INTO Customers (CustomerId, Name, RegionId)
|
||||
VALUES (@customerId, @name, @regionId)";
|
||||
IF EXISTS (SELECT 1 FROM Customers WHERE CustomerId = @customerId)
|
||||
UPDATE Customers
|
||||
SET Name = @name, RegionId = @regionId
|
||||
WHERE CustomerId = @customerId
|
||||
ELSE
|
||||
INSERT INTO Customers (CustomerId, Name, RegionId)
|
||||
VALUES (@customerId, @name, @regionId)";
|
||||
cmd.Parameters.AddWithValue("@customerId", customerId);
|
||||
cmd.Parameters.AddWithValue("@name", name);
|
||||
cmd.Parameters.AddWithValue("@regionId", regionId);
|
||||
|
@ -87,7 +89,7 @@ namespace ElasticScaleStarterKit
|
|||
// Execute the command
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -99,13 +101,13 @@ namespace ElasticScaleStarterKit
|
|||
int customerId,
|
||||
int productId)
|
||||
{
|
||||
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
|
||||
// Looks up the key in the shard map and opens a connection to the shard
|
||||
using (SqlConnection conn = shardMap.OpenConnectionForKey(customerId, credentialsConnectionString))
|
||||
{
|
||||
// Looks up the key in the shard map and opens a connection to the shard
|
||||
using (SqlConnection conn = shardMap.OpenConnectionForKey(customerId, credentialsConnectionString))
|
||||
// Create a simple command that will insert a new order
|
||||
using (SqlCommand cmd = conn.CreateCommand())
|
||||
{
|
||||
// Create a simple command that will insert a new order
|
||||
SqlCommand cmd = conn.CreateCommand();
|
||||
cmd.RetryLogicProvider = SqlDatabaseUtils.SqlRetryProvider;
|
||||
|
||||
// Create a simple command
|
||||
cmd.CommandText = @"INSERT INTO dbo.Orders (CustomerId, OrderDate, ProductId)
|
||||
|
@ -118,7 +120,7 @@ namespace ElasticScaleStarterKit
|
|||
// Execute the command
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ConsoleUtils.WriteInfo("Inserted order for customer ID: {0}", customerId);
|
||||
}
|
||||
|
@ -137,4 +139,4 @@ namespace ElasticScaleStarterKit
|
|||
return s_r.Next(0, maxid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,9 +5,6 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Build.props))\Build.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EnterpriseLibrary.TransientFaultHandling.Data.NetCore" Version="6.0.1312" />
|
||||
<PackageReference Include="EnterpriseLibrary.TransientFaultHandling.NetCore" Version="6.0.1312" />
|
||||
<PackageReference Include="Microsoft.Azure.SqlDatabase.ElasticScale.Client" Version="2.3.0" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -18,6 +15,11 @@
|
|||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json" />
|
||||
<ProjectReference Include="..\..\Src\ElasticScale.Client\Microsoft.Azure.SqlDatabase.ElasticScale.Client.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="App.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft
|
||||
Copyright (c) 2024 Microsoft
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement;
|
||||
|
@ -19,6 +20,8 @@ namespace ElasticScaleStarterKit
|
|||
Console.WriteLine("*** Welcome to Elastic Database Tools Starter Kit ***");
|
||||
Console.WriteLine("***********************************************************");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Authentication method used: {0}", ConfigurationManager.AppSettings["SqlAuthenticationMethod"]);
|
||||
Console.WriteLine();
|
||||
|
||||
// Verify that we can connect to the Sql Database that is specified in App.config settings
|
||||
if (!SqlDatabaseUtils.TryConnectToSqlDatabase())
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
|
||||
Prerequisites:
|
||||
- Visual Studio 2012 or later, Professional Edition or higher
|
||||
- Nuget 2.7 or later
|
||||
- .NET Framework 4.5 or later
|
||||
- Microsoft Azure SQL Database
|
||||
Prerequisites
|
||||
-------------
|
||||
- .Net 6.0 or later
|
||||
- Visual Studio 2022
|
||||
- Microsoft Azure SQL Database or Microsoft SQL Server instance on local machine
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
Before running this project, please fill in the values in App.config.
|
||||
|
||||
For detailed instructions and background information, please refer to Getting Started web page
|
||||
for Azure SQL Database Elastic Scale: http://go.microsoft.com/fwlink/?LinkID=510913
|
||||
|
||||
Authentication Info
|
||||
-------------------
|
||||
https://learn.microsoft.com/en-us/sql/connect/ado-net/sql/azure-active-directory-authentication?view=sql-server-ver16#setting-microsoft-entra-authentication
|
||||
https://learn.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-overview?view=azuresql#connect-by-using-microsoft-entra-identities
|
|
@ -3,12 +3,10 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace ElasticScaleStarterKit
|
||||
{
|
||||
|
@ -22,6 +20,22 @@ namespace ElasticScaleStarterKit
|
|||
/// </summary>
|
||||
public const string MasterDatabaseName = "master";
|
||||
|
||||
// Create a retry logic provider
|
||||
public static SqlRetryLogicBaseProvider SqlRetryProvider = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(SqlRetryPolicy);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the retry policy to use for connections to SQL Server.
|
||||
/// </summary>
|
||||
private static SqlRetryLogicOption SqlRetryPolicy => new()
|
||||
{
|
||||
// Tries 5 times before throwing an exception
|
||||
NumberOfTries = 5,
|
||||
// Preferred gap time to delay before retry
|
||||
DeltaTime = TimeSpan.FromSeconds(1),
|
||||
// Maximum gap time for each delay time before retry
|
||||
MaxTimeInterval = TimeSpan.FromSeconds(20)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if we can connect to the database.
|
||||
/// </summary>
|
||||
|
@ -34,11 +48,9 @@ namespace ElasticScaleStarterKit
|
|||
|
||||
try
|
||||
{
|
||||
using (ReliableSqlConnection conn = new ReliableSqlConnection(
|
||||
connectionString,
|
||||
SqlRetryPolicy,
|
||||
SqlRetryPolicy))
|
||||
using (SqlConnection conn = new SqlConnection(connectionString))
|
||||
{
|
||||
conn.RetryLogicProvider = SqlRetryProvider;
|
||||
conn.Open();
|
||||
}
|
||||
|
||||
|
@ -55,91 +67,96 @@ namespace ElasticScaleStarterKit
|
|||
|
||||
public static bool DatabaseExists(string server, string db)
|
||||
{
|
||||
using (ReliableSqlConnection conn = new ReliableSqlConnection(
|
||||
Configuration.GetConnectionString(server, MasterDatabaseName),
|
||||
SqlRetryPolicy,
|
||||
SqlRetryPolicy))
|
||||
using (SqlConnection conn = new SqlConnection(Configuration.GetConnectionString(server, MasterDatabaseName)))
|
||||
{
|
||||
conn.RetryLogicProvider = SqlRetryProvider;
|
||||
conn.Open();
|
||||
|
||||
SqlCommand cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "select count(*) from sys.databases where name = @dbname";
|
||||
cmd.Parameters.AddWithValue("@dbname", db);
|
||||
cmd.CommandTimeout = 60;
|
||||
int count = conn.ExecuteCommand<int>(cmd);
|
||||
using (SqlCommand cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.RetryLogicProvider = SqlRetryProvider;
|
||||
|
||||
bool exists = count > 0;
|
||||
return exists;
|
||||
cmd.CommandText = "select count(*) from sys.databases where name = @dbname";
|
||||
cmd.Parameters.AddWithValue("@dbname", db);
|
||||
cmd.CommandTimeout = 60;
|
||||
|
||||
int count = (int)cmd.ExecuteScalar();
|
||||
|
||||
bool exists = count > 0;
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DatabaseIsOnline(string server, string db)
|
||||
{
|
||||
using (ReliableSqlConnection conn = new ReliableSqlConnection(
|
||||
Configuration.GetConnectionString(server, MasterDatabaseName),
|
||||
SqlRetryPolicy,
|
||||
SqlRetryPolicy))
|
||||
using (SqlConnection conn = new SqlConnection(Configuration.GetConnectionString(server, MasterDatabaseName)))
|
||||
{
|
||||
conn.RetryLogicProvider = SqlRetryProvider;
|
||||
conn.Open();
|
||||
|
||||
SqlCommand cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "select count(*) from sys.databases where name = @dbname and state = 0"; // online
|
||||
cmd.Parameters.AddWithValue("@dbname", db);
|
||||
cmd.CommandTimeout = 60;
|
||||
int count = conn.ExecuteCommand<int>(cmd);
|
||||
using (SqlCommand cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.RetryLogicProvider = SqlRetryProvider;
|
||||
|
||||
bool exists = count > 0;
|
||||
return exists;
|
||||
cmd.CommandText = "select count(*) from sys.databases where name = @dbname and state = 0"; // online
|
||||
cmd.Parameters.AddWithValue("@dbname", db);
|
||||
cmd.CommandTimeout = 60;
|
||||
|
||||
int count = (int)cmd.ExecuteScalar();
|
||||
|
||||
bool exists = count > 0;
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateDatabase(string server, string db)
|
||||
{
|
||||
ConsoleUtils.WriteInfo("Creating database {0}", db);
|
||||
using (ReliableSqlConnection conn = new ReliableSqlConnection(
|
||||
Configuration.GetConnectionString(server, MasterDatabaseName),
|
||||
SqlRetryPolicy,
|
||||
SqlRetryPolicy))
|
||||
using (SqlConnection conn = new SqlConnection(Configuration.GetConnectionString(server, MasterDatabaseName)))
|
||||
{
|
||||
conn.RetryLogicProvider = SqlRetryProvider;
|
||||
conn.Open();
|
||||
SqlCommand cmd = conn.CreateCommand();
|
||||
|
||||
// Determine if we are connecting to Azure SQL DB
|
||||
cmd.CommandText = "SELECT SERVERPROPERTY('EngineEdition')";
|
||||
cmd.CommandTimeout = 60;
|
||||
int engineEdition = conn.ExecuteCommand<int>(cmd);
|
||||
|
||||
if (engineEdition == 5)
|
||||
using (SqlCommand cmd = conn.CreateCommand())
|
||||
{
|
||||
// Azure SQL DB
|
||||
SqlRetryPolicy.ExecuteAction(() =>
|
||||
{
|
||||
if (!DatabaseExists(server, db))
|
||||
{
|
||||
// Begin creation (which is async for Standard/Premium editions)
|
||||
cmd.CommandText = string.Format(
|
||||
"CREATE DATABASE {0} (EDITION = '{1}')",
|
||||
BracketEscapeName(db),
|
||||
Configuration.DatabaseEdition);
|
||||
cmd.CommandTimeout = 60;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
});
|
||||
// Determine if we are connecting to Azure SQL DB
|
||||
cmd.CommandText = "SELECT SERVERPROPERTY('EngineEdition')";
|
||||
cmd.CommandTimeout = 60;
|
||||
cmd.RetryLogicProvider = SqlRetryProvider;
|
||||
|
||||
// Wait for the operation to complete
|
||||
while (!DatabaseIsOnline(server, db))
|
||||
int engineEdition = (int)cmd.ExecuteScalar();
|
||||
|
||||
if (engineEdition == 5)
|
||||
{
|
||||
ConsoleUtils.WriteInfo("Waiting for database {0} to come online...", db);
|
||||
Thread.Sleep(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
// Azure SQL DB
|
||||
if (!DatabaseExists(server, db))
|
||||
{
|
||||
// Begin creation (which is async for Standard/Premium editions)
|
||||
cmd.CommandText = string.Format(
|
||||
"CREATE DATABASE {0} (EDITION = '{1}')",
|
||||
BracketEscapeName(db),
|
||||
Configuration.DatabaseEdition);
|
||||
cmd.CommandTimeout = 180;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
ConsoleUtils.WriteInfo("Database {0} is online", db);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other edition of SQL DB
|
||||
cmd.CommandText = string.Format("CREATE DATABASE {0}", BracketEscapeName(db));
|
||||
conn.ExecuteCommand(cmd);
|
||||
// Wait for the operation to complete
|
||||
while (!DatabaseIsOnline(server, db))
|
||||
{
|
||||
ConsoleUtils.WriteInfo("Waiting for database {0} to come online...", db);
|
||||
Thread.Sleep(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
ConsoleUtils.WriteInfo("Database {0} is online", db);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other edition of SQL DB
|
||||
cmd.CommandText = string.Format("CREATE DATABASE {0}", BracketEscapeName(db));
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,34 +164,33 @@ namespace ElasticScaleStarterKit
|
|||
public static void DropDatabase(string server, string db)
|
||||
{
|
||||
ConsoleUtils.WriteInfo("Dropping database {0}", db);
|
||||
using (ReliableSqlConnection conn = new ReliableSqlConnection(
|
||||
Configuration.GetConnectionString(server, MasterDatabaseName),
|
||||
SqlRetryPolicy,
|
||||
SqlRetryPolicy))
|
||||
using (SqlConnection conn = new SqlConnection(Configuration.GetConnectionString(server, MasterDatabaseName)))
|
||||
{
|
||||
conn.RetryLogicProvider = SqlRetryProvider;
|
||||
conn.Open();
|
||||
SqlCommand cmd = conn.CreateCommand();
|
||||
|
||||
// Determine if we are connecting to Azure SQL DB
|
||||
cmd.CommandText = "SELECT SERVERPROPERTY('EngineEdition')";
|
||||
cmd.CommandTimeout = 60;
|
||||
int engineEdition = conn.ExecuteCommand<int>(cmd);
|
||||
|
||||
// Drop the database
|
||||
if (engineEdition == 5)
|
||||
using (SqlCommand cmd = conn.CreateCommand())
|
||||
{
|
||||
// Azure SQL DB
|
||||
// Determine if we are connecting to Azure SQL DB
|
||||
cmd.CommandText = "SELECT SERVERPROPERTY('EngineEdition')";
|
||||
cmd.CommandTimeout = 60;
|
||||
int engineEdition = (int)cmd.ExecuteScalar();
|
||||
|
||||
cmd.CommandText = string.Format("DROP DATABASE {0}", BracketEscapeName(db));
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd.CommandText = string.Format(
|
||||
@"ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE
|
||||
DROP DATABASE {0}",
|
||||
BracketEscapeName(db));
|
||||
cmd.ExecuteNonQuery();
|
||||
// Drop the database
|
||||
if (engineEdition == 5)
|
||||
{
|
||||
// Azure SQL DB
|
||||
cmd.CommandText = string.Format("DROP DATABASE {0}", BracketEscapeName(db));
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd.CommandText = string.Format(
|
||||
@"ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE
|
||||
DROP DATABASE {0}",
|
||||
BracketEscapeName(db));
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,22 +198,24 @@ namespace ElasticScaleStarterKit
|
|||
public static void ExecuteSqlScript(string server, string db, string schemaFile)
|
||||
{
|
||||
ConsoleUtils.WriteInfo("Executing script {0}", schemaFile);
|
||||
using (ReliableSqlConnection conn = new ReliableSqlConnection(
|
||||
Configuration.GetConnectionString(server, db),
|
||||
SqlRetryPolicy,
|
||||
SqlRetryPolicy))
|
||||
using (SqlConnection conn = new SqlConnection(Configuration.GetConnectionString(server, db)))
|
||||
{
|
||||
conn.RetryLogicProvider = SqlRetryProvider;
|
||||
conn.Open();
|
||||
SqlCommand cmd = conn.CreateCommand();
|
||||
|
||||
// Read the commands from the sql script file
|
||||
IEnumerable<string> commands = ReadSqlScript(schemaFile);
|
||||
|
||||
foreach (string command in commands)
|
||||
using (SqlCommand cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = command;
|
||||
cmd.CommandTimeout = 60;
|
||||
conn.ExecuteCommand(cmd);
|
||||
cmd.RetryLogicProvider = SqlRetryProvider;
|
||||
|
||||
// Read the commands from the sql script file
|
||||
IEnumerable<string> commands = ReadSqlScript(schemaFile);
|
||||
|
||||
foreach (string command in commands)
|
||||
{
|
||||
cmd.CommandText = command;
|
||||
cmd.CommandTimeout = 60;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,69 +251,5 @@ namespace ElasticScaleStarterKit
|
|||
{
|
||||
return '[' + sqlName.Replace("]", "]]") + ']';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the retry policy to use for connections to SQL Server.
|
||||
/// </summary>
|
||||
public static RetryPolicy SqlRetryPolicy
|
||||
{
|
||||
get
|
||||
{
|
||||
return new RetryPolicy<ExtendedSqlDatabaseTransientErrorDetectionStrategy>(10, TimeSpan.FromSeconds(5));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extended sql transient error detection strategy that performs additional transient error
|
||||
/// checks besides the ones done by the enterprise library.
|
||||
/// </summary>
|
||||
private class ExtendedSqlDatabaseTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// Enterprise transient error detection strategy.
|
||||
/// </summary>
|
||||
private SqlDatabaseTransientErrorDetectionStrategy _sqltransientErrorDetectionStrategy = new SqlDatabaseTransientErrorDetectionStrategy();
|
||||
|
||||
/// <summary>
|
||||
/// Checks with enterprise library's default handler to see if the error is transient, additionally checks
|
||||
/// for such errors using the code in the in <see cref="IsTransientException"/> function.
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception being checked.</param>
|
||||
/// <returns><c>true</c> if exception is considered transient, <c>false</c> otherwise.</returns>
|
||||
public bool IsTransient(Exception ex)
|
||||
{
|
||||
return _sqltransientErrorDetectionStrategy.IsTransient(ex) || IsTransientException(ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects transient errors not currently considered as transient by the enterprise library's strategy.
|
||||
/// </summary>
|
||||
/// <param name="ex">Input exception.</param>
|
||||
/// <returns><c>true</c> if exception is considered transient, <c>false</c> otherwise.</returns>
|
||||
private static bool IsTransientException(Exception ex)
|
||||
{
|
||||
SqlException se = ex as SqlException;
|
||||
|
||||
if (se != null && se.InnerException != null)
|
||||
{
|
||||
Win32Exception we = se.InnerException as Win32Exception;
|
||||
|
||||
if (we != null)
|
||||
{
|
||||
switch (we.NativeErrorCode)
|
||||
{
|
||||
case 0x102:
|
||||
// Transient wait expired error resulting in timeout
|
||||
return true;
|
||||
case 0x121:
|
||||
// Transient semaphore wait expired error resulting in timeout
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"ServerName": "(localdb)\\v11.0",
|
||||
"IntegratedSecurity": true,
|
||||
"UserName": "MyUserName",
|
||||
"Password": "MyPassword",
|
||||
"DatabaseEdition": "Basic"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft
|
||||
Copyright (c) 2024 Microsoft
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -5,12 +5,12 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Azure.SqlDatabase.ElasticScale.Query;
|
||||
using Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace ShardSqlCmd
|
||||
{
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Build.props))\Build.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.SqlDatabase.ElasticScale.Client" version="2.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="LICENSE" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Src\ElasticScale.Client\Microsoft.Azure.SqlDatabase.ElasticScale.Client.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
|
||||
<AssemblyTitle>Microsoft Azure SQL Database: Elastic Database Client Library</AssemblyTitle>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<Version>2.4.1</Version>
|
||||
<Version>2.4.2-preview1</Version>
|
||||
<Authors>Microsoft</Authors>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
|
||||
<PackageTags>Microsoft;Elastic;Scale;Azure;SQL;DB;Database;Shard;Sharding;Management;Query;azureofficial</PackageTags>
|
||||
<PackageReleaseNotes>Updated dependent nugets (due to CVEs), added support for ActiveDirectoryDefault Entra auth</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>Elastic Scale Client now targets .Net Standard 2.0 along with .Net 6.0 and sample apps are updated to support Entra auth</PackageReleaseNotes>
|
||||
<PackageIcon>Icon.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/Azure/elastic-db-tools</PackageProjectUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<NoWarn>$(NoWarn);SYSLIB0011;</NoWarn>
|
||||
</PropertyGroup>
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('build.props'))" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0;net8.0;net481</TargetFrameworks>
|
||||
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoWarn>$(NoWarn);CS8073;</NoWarn>
|
||||
|
@ -8,7 +8,6 @@
|
|||
<Import Project="$([MSBuild]::GetPathOfFileAbove('build.props'))" />
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('strongname.props'))" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.5" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.1" />
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -32,7 +32,7 @@ namespace Microsoft.Azure.SqlDatabase.ElasticScale.Query.UnitTests
|
|||
/// <summary>
|
||||
/// User password to use when connecting to shards during a fanout query.
|
||||
/// </summary>
|
||||
private static string s_testPassword = "dogmat1C";
|
||||
private static string s_testPassword = "J8X2ndQTZ8cvu1r";
|
||||
|
||||
/// <summary>
|
||||
/// Table name for the sharded table we will issue fanout queries against.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0;net8.0;net481</TargetFrameworks>
|
||||
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoWarn>0649;$(NoWarn)</NoWarn>
|
||||
|
@ -8,7 +8,6 @@
|
|||
<Import Project="$([MSBuild]::GetPathOfFileAbove('build.props'))" />
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('strongname.props'))" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.1" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче