Skip to content

Commit

Permalink
Add DbProviderFactoryRepository to Utilities
Browse files Browse the repository at this point in the history
From th0masj0
  • Loading branch information
Martin Willey committed Mar 9, 2016
1 parent 92d0e3f commit e12586a
Show file tree
Hide file tree
Showing 7 changed files with 370 additions and 2 deletions.
2 changes: 2 additions & 0 deletions DatabaseSchemaReader/DatabaseSchemaReader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@
<Compile Include="SqlGen\SqlServer\SqlServerDataTypeMapper.cs" />
<Compile Include="SqlGen\SqlTranslator.cs" />
<Compile Include="CancellationToken.cs" />
<Compile Include="Utilities\DbProviderFactoryDescription.cs" />
<Compile Include="Utilities\DbProviderFactoryRepository.cs" />
<Compile Include="Utilities\DiscoverProviderFactory.cs" />
<Compile Include="Utilities\DummyDataCreator.cs" />
<Compile Include="Utilities\EntityFrameworkImporter.cs" />
Expand Down
2 changes: 2 additions & 0 deletions DatabaseSchemaReader/DatabaseSchemaReader2008.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@
<Compile Include="SqlGen\SqlServer\SqlServerDataTypeMapper.cs" />
<Compile Include="SqlGen\SqlServer\SqlServerMigrationGenerator.cs" />
<Compile Include="SqlGen\SqlTranslator.cs" />
<Compile Include="Utilities\DbProviderFactoryDescription.cs" />
<Compile Include="Utilities\DbProviderFactoryRepository.cs" />
<Compile Include="Utilities\DiscoverProviderFactory.cs" />
<Compile Include="Utilities\DummyDataCreator.cs" />
<Compile Include="Utilities\EntityFrameworkImporter.cs" />
Expand Down
78 changes: 78 additions & 0 deletions DatabaseSchemaReader/Utilities/DbProviderFactoryDescription.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Data;

namespace DatabaseSchemaReader.Utilities
{
/// <summary>
/// Description of a DbProviderFactory for Repository.
/// </summary>
public class DbProviderFactoryDescription
{

/// <summary>
/// Initialize the description.
/// </summary>
public DbProviderFactoryDescription() {}

/// <summary>
/// Initialize the description.
/// </summary>
/// <param name="name"></param>
/// <param name="description"></param>
/// <param name="invariant"></param>
/// <param name="type"></param>
public DbProviderFactoryDescription(string name, string description, string invariant, string type)
{
Name = name;
Description = description;
InvariantName = invariant;
AssemblyQualifiedName = type;
}

/// <summary>
/// Initialize the description based on a row.
/// </summary>
/// <param name="row">The row.</param>
internal DbProviderFactoryDescription(DataRow row)
{
Name = row["Name"].ToString();
Description = row["Description"].ToString();
InvariantName = row["InvariantName"].ToString();
AssemblyQualifiedName = row["AssemblyQualifiedName"].ToString();
}

/// <summary>
/// Gets or sets the assemblyQualifiedName.
/// </summary>
/// <value>The assemblyQualifiedName.</value>
public string AssemblyQualifiedName { get; set; }

/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>The description.</value>
public string Description { get; set; }

/// <summary>
/// Gets or sets the invariantName.
/// </summary>
/// <value>The invariantName.</value>
public string InvariantName { get; set; }

/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }

/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
return InvariantName;
}
}
}
235 changes: 235 additions & 0 deletions DatabaseSchemaReader/Utilities/DbProviderFactoryRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Reflection;

namespace DatabaseSchemaReader.Utilities
{
/// <summary>
///Extension of DbProviderFactories for allowing programmatically adding external dll dataprovider which are not
///declared at app.config or machine.config. Basically extracted from
///http://sandrinodimattia.net/dbproviderfactoryrepository-managing-dbproviderfactories-in-code/
/// </summary>
public static class DbProviderFactoryRepository
{
/// <summary>
///The table containing all the data.
/// </summary>
private static DataTable _dbProviderFactoryTable;

/// <summary>
///Initialize the repository.
/// </summary>
static DbProviderFactoryRepository()
{
LoadDbProviderFactories();
}

/// <summary>
///Gets all providers.
/// </summary>
/// <returns></returns>
public static IEnumerable<DbProviderFactoryDescription> GetAllDescriptions()
{
return _dbProviderFactoryTable.Rows.Cast<DataRow>().Select(o => new DbProviderFactoryDescription(o));
}

/// <summary>
///Get provider by invariant.
/// </summary>
/// <param name="invariant"></param>
/// <returns></returns>
public static DbProviderFactoryDescription GetDescriptionByInvariant(string invariant)
{
var row =
_dbProviderFactoryTable.Rows.Cast<DataRow>()
.FirstOrDefault(o => o["InvariantName"] != null && o["InvariantName"].ToString() == invariant);
return row != null ? new DbProviderFactoryDescription(row) : null;
}

/// <summary>
///Gets the factory.
/// </summary>
/// <param name="description">The description.</param>
/// <returns></returns>
public static DbProviderFactory GetFactory(DbProviderFactoryDescription description)
{
var providerType = //AssemblyHelper.LoadTypeFrom(description.AssemblyQualifiedName);
Type.GetType(description.AssemblyQualifiedName);
if (providerType == null) return null;

var providerInstance = providerType.GetField("Instance",
BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
if (providerInstance == null) return null;
if (!providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))) return null;
try
{
var factory = providerInstance.GetValue(null);
return factory != null ? (DbProviderFactory)factory : null;
}
catch (TargetInvocationException)
{
return null;
}
}

/// <summary>
///Gets the factory.
/// </summary>
/// <param name="invariant">The invariant.</param>
/// <returns></returns>
public static DbProviderFactory GetFactory(string invariant)
{
if (string.IsNullOrEmpty(invariant))
{
throw new ArgumentNullException("invariant");
}

var desc = GetDescriptionByInvariant(invariant);
return desc != null ? GetFactory(desc) : null;
}

/// <summary>
///Loads the external database provider assemblies.
/// </summary>
/// <param name="path">The path.</param>
/// <exception cref="System.ArgumentNullException"></exception>
/// <exception cref="System.ArgumentException">$Path does not {path} exist.</exception>
public static void LoadExternalDbProviderAssemblies(string path)
{
LoadExternalDbProviderAssemblies(path, true);
}

/// <summary>
///Loads the external database provider assemblies.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="includeSubfolders">if set to <c>true</c> [include subfolders].</param>
/// <exception cref="System.ArgumentNullException"></exception>
/// <exception cref="System.ArgumentException">$Path does not {path} exist.</exception>
public static void LoadExternalDbProviderAssemblies(string path, bool includeSubfolders)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}

if (!Directory.Exists(path))
{
throw new ArgumentException(String.Format("Path {0} does not exist.", path), "path");
}

// main directory
var mainDirectory = new DirectoryInfo(path);
var directories = new List<DirectoryInfo> { mainDirectory };

// also search in direct subfolders
if (includeSubfolders)
{
directories.AddRange(mainDirectory.GetDirectories());
}

// iterate over all directories and search for dll libraries
foreach (var directory in directories)
{
foreach (var file in directory.GetFiles().Where(file =>
String.Equals(file.Extension, ".dll", StringComparison.OrdinalIgnoreCase)))
{
// This will work to load only the file from other directory without dependencies! But at access time the dependecies are necessary!
//var assembly = Assembly.LoadFile(file.FullName);

// Load all assemblies from directory in current AppDomain. This is necessary for accessing all types. Other
// opertunities like Assembly.LoadFile will only load one file temporary (later access will not have dependecy finding)
// and Assembly.ReflectionOnlyLoad will load all dependencies at beginning what will not work in other directories as bin.
AssemblyName assemblyName;
try
{
assemblyName = AssemblyName.GetAssemblyName(file.FullName);
}
catch (BadImageFormatException)
{
//dll isn't .net (eg SQLite.Interop.dll)
continue;
}
var assembly = AppDomain.CurrentDomain.Load(assemblyName);
foreach (var type in assembly.GetLoadableTypes())
{
if (type.IsClass)
{
if (typeof(DbProviderFactory).IsAssignableFrom(type))
{
// Ignore already existing provider
if (GetDescriptionByInvariant(type.Namespace) == null)
{
var newDescription = new DbProviderFactoryDescription
{
Description = ".Net Framework Data Provider for " + type.Name,
InvariantName = type.Namespace,
Name = type.Name + " Data Provider",
AssemblyQualifiedName = type.AssemblyQualifiedName
};
Add(newDescription);
}
}
}
}
}
}
}

/// <summary>
/// Gets the loadable types.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">assembly</exception>
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
{
if (assembly == null) throw new ArgumentNullException("assembly");
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}

/// <summary>
///Adds the specified provider.
/// </summary>
/// <param name="provider">The provider.</param>
public static void Add(DbProviderFactoryDescription provider)
{
Delete(provider);
_dbProviderFactoryTable.Rows.Add(provider.Name, provider.Description, provider.InvariantName, provider.AssemblyQualifiedName);
}

/// <summary>
///Deletes the specified provider if present.
/// </summary>
/// <param name="provider">The provider.</param>
private static void Delete(DbProviderFactoryDescription provider)
{
var row =
_dbProviderFactoryTable.Rows.Cast<DataRow>()
.FirstOrDefault(o => o["InvariantName"] != null && o["InvariantName"].ToString() == provider.InvariantName);
if (row != null)
{
_dbProviderFactoryTable.Rows.Remove(row);
}
}

/// <summary>
///Opens the table.
/// </summary>
private static void LoadDbProviderFactories()
{
_dbProviderFactoryTable = DbProviderFactories.GetFactoryClasses();
}
}
}
5 changes: 4 additions & 1 deletion DatabaseSchemaReaderTest/DatabaseSchemaReaderTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
</Reference>
<Reference Include="Microsoft.Build" />
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Mono.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Npgsql.2.2.7\lib\net40\Mono.Security.dll</HintPath>
<Private>True</Private>
Expand Down Expand Up @@ -228,6 +230,7 @@
</Compile>
<Compile Include="Utilities\EF\EntityImporterTest.cs" />
<Compile Include="Utilities\SchemaTablesSorterTest.cs" />
<Compile Include="Utilities\DbProviderFactoryRepositoryTest.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="DatabaseScripts\create_database_northwind.sql" />
Expand Down
3 changes: 2 additions & 1 deletion DatabaseSchemaReaderTest/DatabaseSchemaReaderTest2008.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{1114FF99-4A7A-4369-9C38-18FCECF773A3}</ProjectGuid>
<OutputType>Library</OutputType>
Expand Down Expand Up @@ -187,6 +187,7 @@
<Compile Include="SqlGen\WritingSprocsTest.cs" />
<Compile Include="TestCategoryAttribute.cs" />
<Compile Include="TestHelper.cs" />
<Compile Include="Utilities\DbProviderFactoryRepositoryTest.cs" />
<Compile Include="Utilities\SchemaTablesSorterTest.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
Loading

0 comments on commit e12586a

Please sign in to comment.