Добавить DbProviderFactory без App.Config - PullRequest
37 голосов
/ 13 июля 2009

Я использую DbProviderFactories в своем слое данных (на основе Entity Framework) и использую SQLite для своей базы данных, но мне не нужно иметь App.Config, чтобы иметь следующий код:

<configuration>
  <system.data>
    <DbProviderFactories>
      <remove invariant="System.Data.SQLite"/>
      <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
    </DbProviderFactories>
  </system.data>
</configuration>

Вместо этого я хотел бы, чтобы мой слой данных вставлял это программно. Кто-нибудь знает способ сделать это?

РЕДАКТИРОВАТЬ:

Причина этого заключается в том, что я использую контейнер IoC для выбора слоя данных, и некоторые из моих слоев данных не нуждаются в значениях App.Config или жестко привязаны к слою данных.

Ответы [ 8 ]

52 голосов
/ 04 ноября 2009

Следующее может вызвать солнечные пятна и свергнуть западную цивилизацию. Это может даже вызвать дебаты о программировании клейкой ленты (остановите это!), Но это работает (пока)

try
{
    var dataSet = ConfigurationManager.GetSection("system.data") as System.Data.DataSet;
    dataSet.Tables[0].Rows.Add("SQLite Data Provider"
    , ".Net Framework Data Provider for SQLite"
    , "System.Data.SQLite"
    , "System.Data.SQLite.SQLiteFactory, System.Data.SQLite");
}
catch (System.Data.ConstraintException) { }
5 голосов
/ 19 декабря 2013

JoshRivers выше опубликовал решение для SQLite. На самом деле это можно использовать и для других адаптеров - я смог заставить его работать на MySQL, используя его пример. Я обернул это во что-то более общее. Это должно выполняться после запуска приложения для версии 6.6.5.0 коннектора .NET (но я думаю, что это хорошо и для других версий).

string dataProvider = @"MySql.Data.MySqlClient";
string dataProviderDescription = @".Net Framework Data Provider for MySQL";
string dataProviderName = @"MySQL Data Provider";
string dataProviderType = @"MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.6.5.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d";

bool addProvider = true;
var dataSet = ConfigurationManager.GetSection("system.data") as DataSet;
foreach (DataRow row in dataSet.Tables[0].Rows)
{
    if ((row["InvariantName"] as string) == dataProvider)
    {
        // it is already in the config, no need to add.
        addProvider = false;
        break;
    }
}

if (addProvider)
    dataSet.Tables[0].Rows.Add(dataProviderName, dataProviderDescription, dataProvider, dataProviderType);
3 голосов
/ 25 августа 2014

Обновление для EF 6.0 +

Вы можете добавить DbProviderFactory, зарегистрировав IDbDependencyResolver и указав тип DbProviderFactory. Пример этого ниже:

static class Program
{
    [STAThread]
    static void Main()
    {
        System.Data.Entity.DbConfiguration.Loaded += (_, a) => {
            a.AddDependencyResolver(new MyDependencyResolver(), true);
        };  

        Application.Run(new Form1());
    }
}

class MyDependencyResolver : System.Data.Entity.Infrastructure.DependencyResolution.IDbDependencyResolver {

    public object GetService(Type type, object key) {

        // Output the service attempting to be resolved along with it's key 
        System.Diagnostics.Debug.WriteLine(string.Format("MyDependencyResolver.GetService({0}, {1})", type.Name, key == null ? "" : key.ToString()));

        if (type == typeof(System.Data.Common.DbProviderFactory)) {

            // Return whatever DbProviderFactory is relevant
            return new MyDbProviderFactory(); 

        }else if(type == typeof(System.Data.Entity.Infrastructure.IProviderInvariantName) && key != null && key == "MyDbProviderFactory"){

            // Return the Provider's invariant name for the MyDbProviderFactory
            return new MyProviderInvariantName();

        }

        return null;
    }

    public IEnumerable<object> GetServices(Type type, object key) {
        return new object[] { GetService(type, key) }.ToList().Where(o => o != null);
    }
}

вам также может потребоваться разрешить некоторые дополнительные типы в зависимости от того, какой тип переопределения вам нужно сделать и как настроен ваш проект. По сути, просто начните с приведенного выше кода и продолжайте отладку, пока не определите все службы, которые необходимо разрешить для ваших конкретных требований.

Подробнее о разрешении зависимостей EF можно прочитать по ссылкам ниже:

Кроме того, вы можете сделать эту конфигурацию, переопределив DbConfiguration, как описано в первой ссылке выше.

3 голосов
/ 02 ноября 2013

ПОЗДНО ОТВЕТ:

Вы всегда можете сразу получить завод, подобный этому:

DbProviderFactory factory = System.Data.SQLite.SQLiteFactory.Instance;
// (note that the rest of the code is still provider-agnostic.)

Или используйте контейнер IoC для разрешения DbProviderFactory, например ::10000

container.RegisterInstance<DbProviderFactory>(SQLiteFactory.Instance);

Я предпочитаю , а не использовать DbProviderFactories.GetFactory из-за его ограничения на требование файла конфигурации (или взлома, как в @ JoshRiver's answer ).

Все, что DbProviderFactories.GetFactory делает, это ищет зарегистрированное имя с указанием сборки типа фабрики, используя имя провайдера, и затем получает значение статического свойства Instance, используя отражение.

Если вы не хотите использовать конфигурацию, один из описанных выше способов может быть более удобным в зависимости от вашего варианта использования.

2 голосов
/ 13 июля 2009

Программный выбор фабрики провайдера БД в значительной степени противоречит цели. Вы могли бы также использовать классы, специфичные для SQLite, вместо всех этих интерфейсов, нет?

0 голосов
/ 06 мая 2019

В .NET Core 2.1 и более поздних версиях вы можете использовать DbProviderFactories.RegisterFactory для программной регистрации DbProviderFactory.

0 голосов
/ 29 декабря 2016

Даже позже Ответ

Получите его, используя конфигурацию, как указано выше. Я обнаружил, что для этого требуется, чтобы сборка провайдера находилась где-то, чтобы работающая программа могла ее найти.

    /// <summary>
    /// Creates a DbProviderFactory instance without needing configuration file
    /// </summary>
    /// <param name="lsProviderName">Name of the provider.  Like "System.Data.SQLite"</param>
    /// <param name="lsClass">Class and assembly information.  Like "System.Data.SQLite.SQLiteFactory, System.Data.SQLite"</param>
    /// <returns>A specific DbProviderFactory instance, or null if one can't be found</returns>
    protected static DbProviderFactory GetDbProviderFactoryFromConfigRow(string lsProviderName, string lsClass)
    {
        if (string.Empty != lsProviderName && string.Empty != lsClass)
        {
            DataRow loConfig = null;
            DataSet loDataSet = ConfigurationManager.GetSection("system.data") as DataSet;
            foreach (DataRow loRow in loDataSet.Tables[0].Rows)
            {
                if ((loRow["InvariantName"] as string) == lsProviderName)
                {
                    loConfig = loRow;
                }
            }

            if (null == loConfig)
            {
                loConfig = loDataSet.Tables[0].NewRow();
                loConfig["InvariantName"] = lsProviderName;
                loConfig["Description"] = "Dynamically added";
                loConfig["Name"] = lsProviderName + "Name";
                loConfig["AssemblyQualifiedName"] = lsClass;
                loDataSet.Tables[0].Rows.Add(loConfig);
            }

            try
            {
                DbProviderFactory loDbProviderFactoryByRow = DbProviderFactories.GetFactory(loConfig);
                return loDbProviderFactoryByRow;
            }
            catch (Exception loE)
            {
                //// Handled exception if needed, otherwise, null is returned and another method can be tried.
            }
        }

Другой метод, который получает поле Instance непосредственно из сборки. Он работает, даже если DLL находится где-то еще в системе.

    /// <summary>
    /// Creates a DbProviderFactory instance without needing configuration file
    /// </summary>
    /// <param name="lsClass">Class and assembly information.  Like "System.Data.SQLite.SQLiteFactory, System.Data.SQLite"</param>
    /// <param name="lsAssemblyFile">Full path to the assembly DLL. Like "c:\references\System.Data.SQLite.dll"</param>
    /// <returns>A specific DbProviderFactory instance, or null if one can't be found</returns>
    protected static DbProviderFactory GetDbProviderFactoryFromAssembly(string lsClass, string lsAssemblyFile)
    {
        if (lsAssemblyFile != string.Empty && lsClass != string.Empty)
        {
            Assembly loAssembly = System.Reflection.Assembly.LoadFrom(lsAssemblyFile);
            if (null != loAssembly)
            {
                string[] laAssembly = lsClass.Split(new char[] { ',' });
                Type loType = loAssembly.GetType(laAssembly[0].Trim());
                FieldInfo loInfo = loType.GetField("Instance");
                if (null != loInfo)
                {
                    object loInstance = loInfo.GetValue(null);
                    if (null != loInstance)
                    {
                        if (loInstance is System.Data.Common.DbProviderFactory)
                        {
                            return loInstance as DbProviderFactory;
                        }
                    }
                }
            }
        }

        return null;
    }
0 голосов
/ 05 мая 2011

см. Следующий фрагмент

    public DataProviderManager(string ProviderName)
    {

       var  _Provider = DbProviderFactories.GetFactory(ProviderName);

    }

вам нужно передать ProviderName, который в вашем случае является "System.Data.SQLite".

вам не нужно создавать раздел конфигурации приложения. Этот раздел создается SQLite в machine.config после установки поставщика SQLite.net.

вся цель раздела appconfig - помочь вам получить список настроенных провайдеров .net при вызове следующей команды:

public GetProvidersList () { DataTable table = DbProviderFactories.GetFactoryClasses (); } * +1010 *

...