39

我在我的数据层(基于实体框架)中使用 DbProviderFactories 并在我的数据库中使用 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 值,或者让它们与数据层硬绑定。

4

8 回答 8

57

以下可能会导致太阳黑子并推翻西方文明。它甚至可能引起关于胶带编程的争论(让它停止!),但它有效(现在)

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) { }
于 2009-11-04T00:00:32.570 回答
6

上面的 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);
于 2013-12-19T00:34:41.037 回答
3

迟到的答案:

你总是可以直接得到这样的工厂:

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因为它需要配置文件(或像@JoshRiver's answer中的黑客攻击)。

所做的只是,它使用提供者名称DbProviderFactories.GetFactory查找已注册的工厂类型的程序集限定名称,然后Instance使用反射获取静态属性的值。

如果您不想使用配置,根据您的用例,上述方法之一可能更方便。

于 2013-11-02T09:59:02.783 回答
3

EF 6.0+ 的更新

您可以DbProviderFactory通过注册 aIDbDependencyResolver并解析 type来添加 a 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此外,您可以通过上面第一个链接中所述的覆盖来执行此配置。

于 2014-08-25T16:12:49.233 回答
3

在 .NET Core 2.1 及更高版本中,您可以使用DbProviderFactories.RegisterFactory以编程方式注册DbProviderFactory.

于 2019-05-06T00:22:56.013 回答
2

以编程方式选择数据库提供程序工厂几乎违背了目的。您不妨使用特定于 SQLite 的类而不是所有这些接口,不是吗?

于 2009-07-13T04:31:12.220 回答
0

请参阅以下代码段

    public DataProviderManager(string ProviderName)
    {

       var  _Provider = DbProviderFactories.GetFactory(ProviderName);

    }

您需要传递在您的情况下为“System.Data.SQLite”的 ProviderName。

您不需要创建应用程序配置部分。该部分由 SQLite 在安装 SQLite.net 提供程序后在 machine.config 中创建。

appconfig 部分的全部目的是帮助您在调用以下命令时获取已配置的 .net 提供程序的列表:

公共 GetProvidersList() { DataTable table= DbProviderFactories.GetFactoryClasses(); }

于 2011-05-04T20:25:28.693 回答
0

以后再回答

使用上面的配置获取它。我发现这似乎要求提供程序程序集位于正在运行的程序可以找到它的地方。

    /// <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;
    }
于 2016-12-29T17:02:06.180 回答