Commit 7c2b7245 authored by Helgi Hafþórsson's avatar Helgi Hafþórsson
Browse files

Work in progress. Going .NET Standard 2.0, major changes

parent 573c64e8
Pipeline #14231604 failed with stage

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.14
VisualStudioVersion = 15.0.27004.2009
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marsonsoft.Database", "src\Marsonsoft.Database\Marsonsoft.Database.csproj", "{5A8FB352-10B9-4EB2-9297-49C9A66A65CE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marsonsoft.Database", "src\Marsonsoft.Database\Marsonsoft.Database.csproj", "{5A8FB352-10B9-4EB2-9297-49C9A66A65CE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{871C6815-F7D6-4747-8B65-F39063EF1DBF}"
EndProject
......@@ -28,6 +28,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample2", "samples\Sample2\Sample2.csproj", "{AA40D389-895A-481D-A924-06411DF667B3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample3", "samples\Sample3\Sample3.csproj", "{EC5BDE99-C537-42DC-8DDB-D654471123A8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -50,6 +52,10 @@ Global
{AA40D389-895A-481D-A924-06411DF667B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA40D389-895A-481D-A924-06411DF667B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA40D389-895A-481D-A924-06411DF667B3}.Release|Any CPU.Build.0 = Release|Any CPU
{EC5BDE99-C537-42DC-8DDB-D654471123A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EC5BDE99-C537-42DC-8DDB-D654471123A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EC5BDE99-C537-42DC-8DDB-D654471123A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EC5BDE99-C537-42DC-8DDB-D654471123A8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -59,5 +65,9 @@ Global
{7D9B2CBD-DB01-4EE6-9743-68B0590D844D} = {22873875-9BB7-423E-9B7B-E8565A69D525}
{83B95ECA-576D-44F1-9FDA-31E582D61187} = {ED35E57D-7432-4DFB-99E3-9EAFD4812242}
{AA40D389-895A-481D-A924-06411DF667B3} = {ED35E57D-7432-4DFB-99E3-9EAFD4812242}
{EC5BDE99-C537-42DC-8DDB-D654471123A8} = {ED35E57D-7432-4DFB-99E3-9EAFD4812242}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C1FECD20-9ABB-464F-A4FB-7AD5F68A9E27}
EndGlobalSection
EndGlobal
......@@ -6,11 +6,12 @@ namespace Sample1
{
static void Main(string[] args)
{
var dataEnvironment = DataEnvironmentProvider.Default.GetDefault();
using (var connection = dataEnvironment.ConnectionProvider.OpenConnection())
{
//...
}
//var dataEnvironment = DataEnvironmentProvider
// .Default.GetDefault();
//using (var connection = dataEnvironment.ConnectionProvider.OpenConnection())
//{
// //...
//}
}
}
}
......@@ -6,11 +6,11 @@ namespace Sample2
{
static void Main(string[] args)
{
var dataEnvironment = DataEnvironmentProvider.Default.GetByConnectionStringName("OtherConnection");
using (var connection = dataEnvironment.ConnectionProvider.OpenConnection())
{
//...
}
//var dataEnvironment = DataEnvironmentProvider.Default.GetByConnectionStringName("OtherConnection");
//using (var connection = dataEnvironment.ConnectionProvider.OpenConnection())
//{
// //...
//}
}
}
}
using Marsonsoft.Database;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;
using System.IO;
namespace Sample3
{
class Program
{
static void Main(string[] args)
{
// Build up a configuration from our json file
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json", optional: true)
.Build();
// Get a DatabaseConfiguration from our configuration
var databaseConfiguration = configuration.Get<Marsonsoft.Database.Configuration.DatabaseConfiguration>();
// Create a DataEnvironmentProvider for the DatabaseConfiguration
var dataEnvironmentProvider = DataEnvironmentProviderFactory.Create(databaseConfiguration);
// Get the default IDataEnvironment for our configuration
var dataEnvironment = dataEnvironmentProvider.GetDefault();
// Start using the dataEnvironment
using (var connection = dataEnvironment.ConnectionProvider.OpenConnection())
{
// Use the IDbConnection to create the IDbCommand
using (var command = connection.CreateCommand("select id, name from customers where type = :type"))
{
// Extension method to add a parameter
command.AddParameter("type", "good");
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
// Use extension methods to get typed + named values from the reader
Debug.WriteLine($"id: {reader.GetValue<long>("id")}, name: {reader.GetValue<string>("name")}");
}
}
}
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Marsonsoft.Database\Marsonsoft.Database.csproj" />
</ItemGroup>
</Project>
{
"connections": [
{
"name": "defaultConnection",
"providerName": "Oracle.ManagedDataAccess.Client",
"connectionString": "Data Source=myOracle;User Id=myUser;Password=myPass;"
}
],
"providers": [
{
"name": "Oracle.ManagedDataAccess.Client",
"type": "Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess, Version=4.122.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342"
}
]
}
using System;
using System.Collections.Generic;
using System.Configuration;
namespace Marsonsoft.Database
{
/// <summary>
/// Provider of database connection strings by reading configuration.
/// </summary>
public class ConfigConnectionStringInfoProvider : IConnectionStringInfoProvider
{
/// <summary>
/// Name of fallback default connection string.
/// </summary>
public const string FallbackDefaultConnectionStringName = "DefaultConnection";
private readonly string appKeyPrefix;
private Lazy<List<ConnectionStringInfo>> lazyList;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigConnectionStringInfoProvider"/> class.
/// </summary>
public ConfigConnectionStringInfoProvider()
{
appKeyPrefix = GetType().Namespace;
lazyList = new Lazy<List<ConnectionStringInfo>>(() => CreateConnectionStringInfos(), false);
}
/// <summary>
/// Gets the default connection string info's name, either specified in config file or the fallback one.
/// </summary>
public string DefaultName
{
get
{
return ConfigurationManager.AppSettings[appKeyPrefix + ".DatabaseConnection"] ?? FallbackDefaultConnectionStringName;
}
}
/// <summary>
/// Gets the default provider name, if specified.
/// </summary>
public string DefaultProviderName
{
get
{
return ConfigurationManager.AppSettings[appKeyPrefix + ".ProviderInvariantName"];
}
}
/// <summary>
/// Gets the connection string infos.
/// </summary>
public IEnumerable<ConnectionStringInfo> ConnectionStringInfos
{
get
{
return lazyList.Value;
}
}
private List<ConnectionStringInfo> CreateConnectionStringInfos()
{
string providerName = DefaultProviderName;
var connectionStrings = new List<ConnectionStringInfo>();
foreach (ConnectionStringSettings connectionStringSettings in ConfigurationManager.ConnectionStrings)
{
var info = new ConnectionStringInfo
{
Name = connectionStringSettings.Name,
ProviderName = connectionStringSettings.ProviderName,
ConnectionString = connectionStringSettings.ConnectionString
};
if (string.IsNullOrEmpty(info.ProviderName))
{
if (!string.IsNullOrEmpty(providerName))
{
info.ProviderName = providerName;
}
else
{
throw new ConfigurationErrorsException("No default ProviderInvariantName is specified and at least one connection string settings has no provider defined.");
}
}
connectionStrings.Add(info);
}
return connectionStrings;
}
}
}
namespace Marsonsoft.Database
using System;
using System.Collections.Generic;
using System.Text;
namespace Marsonsoft.Database.Configuration
{
/// <summary>
/// Represents a single, named connection string.
/// </summary>
public class ConnectionStringInfo
public class Connection
{
/// <summary>
/// Initializes a new instance of the <see cref="ConnectionStringInfo"/> class.
/// </summary>
public ConnectionStringInfo()
{
}
/// <summary>
/// Gets or sets the name of the connection string.
/// </summary>
......@@ -26,5 +23,6 @@
/// Gets or sets the connection string.
/// </summary>
public string ConnectionString { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Marsonsoft.Database.Configuration
{
public class DatabaseConfiguration
{
/// <summary>
/// Gets or sets the default connection strings' name. Required, cannot be null.
/// </summary>
public string DefaultName { get; set; }
/// <summary>
/// Gets or sets the default provider name if specified, null if none is specified..
/// </summary>
public string DefaultProviderName { get; set; }
/// <summary>
/// Gets or sets the connection information list.
/// </summary>
public List<Connection> Connections { get; set; }
public List<Provider> Providers { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Marsonsoft.Database.Configuration
{
public class Provider
{
public string Name { get; set; }
public string Type { get; set; }
}
}
......@@ -10,52 +10,27 @@ namespace Marsonsoft.Database
/// <summary>
/// Provides objects implementing <see cref="IDataEnvironment"/>.
/// </summary>
public class DataEnvironmentProvider
public class DataEnvironmentProvider : IDataEnvironmentProvider
{
private static readonly Lazy<DataEnvironmentProvider> defaultInstanceLazy = new Lazy<DataEnvironmentProvider>(CreateDefaultEnvironmentProvider, false);
private readonly IConnectionStringInfoProvider connectionStringInfoProvider;
private readonly IDbProviderFactoryFactory dbProviderFactories;
private readonly Dictionary<string, Type> dataEnvironmentDictionary = new Dictionary<string, Type>();
/// <summary>
/// Initializes a new instance of the <see cref="DataEnvironmentProvider"/> class.
/// </summary>
/// <param name="infoProvider">The information provider to use.</param>
/// <param name="dbProviderFactories">The DB provider factory factory to use.</param>
/// <exception cref="System.ArgumentNullException">infoProvider</exception>
public DataEnvironmentProvider(IConnectionStringInfoProvider infoProvider)
protected internal DataEnvironmentProvider(Configuration.DatabaseConfiguration configuration, IDbProviderFactoryFactory dbProviderFactories)
{
connectionStringInfoProvider = infoProvider ?? throw new ArgumentNullException(nameof(infoProvider));
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
this.dbProviderFactories = dbProviderFactories ?? throw new ArgumentNullException(nameof(dbProviderFactories));
}
/// <summary>
/// Initializes a new instance of the <see cref="DataEnvironmentProvider"/> class using values from the configuration file.
/// Gets the database configuration.
/// </summary>
private DataEnvironmentProvider()
: this(new ConfigConnectionStringInfoProvider())
{
}
/// <summary>
/// Gets the default instance. The default instance uses values from the configuration file.
/// </summary>
public static DataEnvironmentProvider Default
{
get
{
return defaultInstanceLazy.Value;
}
}
/// <summary>
/// Gets the connection string information provider.
/// </summary>
public IConnectionStringInfoProvider ConnsectionStringInfoProvider
{
get
{
return connectionStringInfoProvider;
}
}
public Configuration.DatabaseConfiguration Configuration { get; }
/// <summary>
/// Adds an environment type to the list of types provided. The <see cref="DbProviderFactory"/> types supported are
......@@ -114,15 +89,15 @@ namespace Marsonsoft.Database
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
public IDataEnvironment GetDefault()
{
ConnectionStringInfo defaultInfo = connectionStringInfoProvider.ConnectionStringInfos.FirstOrDefault(p => p.Name == connectionStringInfoProvider.DefaultName);
string providerName = defaultInfo?.ProviderName ?? connectionStringInfoProvider.DefaultProviderName;
Configuration.Connection defaultConnection = Configuration.Connections.FirstOrDefault(p => p.Name == Configuration.DefaultName);
string providerName = defaultConnection?.ProviderName ?? Configuration.DefaultProviderName;
if (string.IsNullOrEmpty(providerName))
{
throw new InvalidOperationException("No default provider name was found.");
}
return CreateDataEnvironment(providerName, defaultInfo.ConnectionString);
return CreateDataEnvironment(providerName, defaultConnection.ConnectionString);
}
/// <summary>
......@@ -137,39 +112,17 @@ namespace Marsonsoft.Database
/// <exception cref="System.ArgumentException">A connection string with the value specified in <paramref name="name"/> was not found.</exception>
public IDataEnvironment GetByConnectionStringName(string name)
{
ConnectionStringInfo info = connectionStringInfoProvider.ConnectionStringInfos.FirstOrDefault(p => p.Name == name);
if (info == null)
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "No connection string with name '{0}' found.", name));
}
return CreateDataEnvironment(info.ProviderName, info.ConnectionString);
}
private static DataEnvironmentProvider CreateDefaultEnvironmentProvider()
{
var defaultDataEnvironmentProvider = new DataEnvironmentProvider();
var dataEnvironmentTypes = GetAllTypesImplementingIDataEnvironment();
foreach (var dataEnvironmentType in dataEnvironmentTypes)
Configuration.Connection connection = Configuration.Connections.FirstOrDefault(p => p.Name == name);
if (connection == null)
{
var providerNames = GetAllProviderNamesSupportedBy(dataEnvironmentType);
foreach (var providerName in providerNames)
{
defaultDataEnvironmentProvider.Add(providerName, dataEnvironmentType);
}
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "No connection string with name '{0}' found.", name));
}
return defaultDataEnvironmentProvider;
}
private static IEnumerable<Type> GetAllTypesImplementingIDataEnvironment()
{
return from type in Assembly.GetExecutingAssembly().GetTypes()
where type.IsClass && !type.IsAbstract && type.GetInterfaces().Contains(typeof(IDataEnvironment))
select type;
return CreateDataEnvironment(connection.ProviderName, connection.ConnectionString);
}
private static IEnumerable<string> GetAllProviderNamesSupportedBy(Type dataEnvironmentType)
internal static IEnumerable<string> GetAllProviderNamesSupportedBy(Type dataEnvironmentType)
{
return
from attribute in dataEnvironmentType.GetCustomAttributes<ProviderSupportAttribute>(true)
......@@ -191,7 +144,7 @@ namespace Marsonsoft.Database
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "No type implementing {0} was found supporting provider '{1}'.", nameof(IDataEnvironment), providerInvariantName));
}
DbProviderFactory dbProviderFactory = DbProviderFactories.GetFactory(providerInvariantName);
DbProviderFactory dbProviderFactory = dbProviderFactories.GetFactory(providerInvariantName);
IConnectionProvider connectionProvider = new ConnectionProvider(dbProviderFactory, connectionString);
return Activator.CreateInstance(dataEnvironmentType, dbProviderFactory, connectionProvider) as IDataEnvironment;
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Marsonsoft.Database
{
/// <summary>
/// Responsible for creating <see cref="IDataEnvironmentProvider"/> objects.
/// </summary>
public static class DataEnvironmentProviderFactory
{
/// <summary>
/// Creates an instance of a class implementing the <see cref="IDataEnvironmentProvider"/> interface using the
/// specified database configuration.
/// </summary>
/// <param name="databaseConfiguration">The database configuration to use.</param>
/// <returns>An instance of a class implementing the <see cref="IDataEnvironmentProvider"/> interface</returns>
public static IDataEnvironmentProvider Create(Configuration.DatabaseConfiguration databaseConfiguration)
{
return Create(databaseConfiguration, new DbProviderFactories(databaseConfiguration.Providers));
}
/// <summary>
/// Creates an instance of a class implementing the <see cref="IDataEnvironmentProvider"/> interface using the
/// specified database configuration and DB provider factory factory.
/// </summary>
/// <param name="databaseConfiguration">The database configuration to use.</param>
/// <param name="dbProviderFactories">The DB provider factory factory to use.</param>
/// <returns>An instance of a class implementing the <see cref="IDataEnvironmentProvider"/> interface</returns>
/// <remarks>
/// The <see cref="Configuration.Provider"/> part of the <paramref name="databaseConfiguration"/> is ignored in
/// this method (although the class behind <paramref name="dbProviderFactories"/> may actually be using it).
/// </remarks>
private static DataEnvironmentProvider Create(Configuration.DatabaseConfiguration databaseConfiguration, IDbProviderFactoryFactory dbProviderFactories)
{
var defaultDataEnvironmentProvider = new DataEnvironmentProvider(databaseConfiguration, dbProviderFactories);
var dataEnvironmentTypes = GetAllTypesImplementingIDataEnvironment();
foreach (var dataEnvironmentType in dataEnvironmentTypes)
{
var providerNames = DataEnvironmentProvider.GetAllProviderNamesSupportedBy(dataEnvironmentType);
foreach (var providerName in providerNames)
{
defaultDataEnvironmentProvider.Add(providerName, dataEnvironmentType);
}
}
return defaultDataEnvironmentProvider;
}
private static IEnumerable<Type> GetAllTypesImplementingIDataEnvironment()
{
return from type in Assembly.GetExecutingAssembly().GetTypes()
where type.IsClass && !type.IsAbstract && type.GetInterfaces().Contains(typeof(IDataEnvironment))
select type;
}
}
}
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Marsonsoft.Database
{
internal class DbProviderFactories : IDbProviderFactoryFactory
{
private class ProviderFactory
{
public Configuration.Provider Provider;
public DbProviderFactory DbProviderFactory;
}
private readonly Dictionary<string, ProviderFactory> providerFactories;
public DbProviderFactories(IEnumerable<Configuration.Provider> providerDefinitions)
{
if (providerDefinitions == null)
{
return;
}
providerFactories = providerDefinitions.Select(p => new ProviderFactory { Provider = p }).ToDictionary(f => f.Provider.Name);
}
public DbProviderFactory GetFactory(string providerInvariantName)
{
if (!providerFactories.TryGetValue(providerInvariantName, out ProviderFactory providerFactory))
{
return null;
}
if (providerFactory.DbProviderFactory == null)
{
var dbProviderType = Type.GetType(providerInvariantName);
var instance = Activator.CreateInstance(dbProviderType) as DbProviderFactory;
Interlocked.CompareExchange<DbProviderFactory>(ref providerFactory.DbProviderFactory, instance, null);
}
return providerFactory.DbProviderFactory;
}
}
}
using System.Collections.Generic;
namespace Marsonsoft.Database
{
/// <summary>
/// Represents a provider of database connection information.
/// </summary>
public interface IConnectionStringInfoProvider
{
/// <summary>
/// Gets the default connection strings' name. Required, cannot be null.
/// </summary>
string DefaultName { get; }