Добавить DbProviderFactory без App.Config

Я использую 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 или жестко привязаны к уровню данных.


person JasonRShaver    schedule 13.07.2009    source источник
comment
Почему у вас нет app.config. Если вы этого не сделаете (возможно, вы являетесь библиотекой классов), то это сделает вызывающее приложение. Поместите туда конфигурацию.   -  person John Saunders    schedule 13.07.2009
comment
Интересно, нашел ли Джейсон когда-нибудь ответ на этот вопрос ... У меня есть HTA, который вызывает наши сборки .Net как объекты COM. Итак, app.config нет. Мы используем SQL CE и сталкиваемся с той же проблемой.   -  person harley.333    schedule 04.11.2009
comment
@JohnSaunders: Я пытался заставить несколько exe использовать один и тот же файл конфигурации. EntityFramework, похоже, не соблюдает AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", @"C:\Shared\app.config"); Отсюда и полезность этого вопроса.   -  person teynon    schedule 07.10.2013


Ответы (8)


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

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) { }
person JoshRivers    schedule 04.11.2009
comment
Хехе, работает. Не очень красиво, но это единственное, что есть на свете. Спасибо! - person JasonRShaver; 29.11.2009

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

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);
person A.Konzel    schedule 19.12.2013

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

Вы всегда можете напрямую получить такую ​​фабрику:

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

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

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

Я предпочитаю не использовать DbProviderFactories.GetFactory из-за его ограничения, требующего файла конфигурации (или взлома, как в @ Ответ Джоша Ривера).

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

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

person Eren Ersönmez    schedule 02.11.2013

Обновление для 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, как описано в первой ссылке выше.

person Ryan Griffith    schedule 25.08.2014

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

person 0xced    schedule 06.05.2019
comment
RegisterFactory не существует в .NET 5 по неизвестной причине. Я разместил его в stackoverflow.com/questions/65465068/ - person Andrus; 27.12.2020

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

person Steven Sudit    schedule 13.07.2009
comment
На самом деле, это очень полезно, когда ваш app.config не читается, что имеет место при использовании F # interactive. - person John Reynolds; 01.07.2011

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

    public DataProviderManager(string ProviderName)
    {

       var  _Provider = DbProviderFactories.GetFactory(ProviderName);

    }

вам необходимо передать ProviderName, которое в вашем случае - «System.Data.SQLite».

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

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

общедоступный GetProvidersList () {Таблица DataTable = DbProviderFactories.GetFactoryClasses (); }

person Jose Manuel Ojeda    schedule 04.05.2011

Даже более поздний ответ

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

    /// <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;
    }
person Brian Lakstins    schedule 29.12.2016