entity framework - Agregue un DbProviderFactory sin una App.Config
entity-framework sqlite (8)
Estoy usando DbProviderFactories en mi capa de datos (basado en Entity Framework) y estoy usando SQLite para mi base de datos, pero no necesito tener una App.Config para tener el siguiente código:
<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>
En cambio, me gustaría que mi capa de datos lo ponga en forma programática. Alguien sabe una manera de hacer esto?
EDITAR:
La razón para esto es que estoy usando un contenedor de IoC para seleccionar la capa de datos y algunas de mis capas de datos no necesitan los valores de App.Config, o tienen que estar vinculados a la capa de datos.
Actualización para EF 6.0+
Puede agregar un DbProviderFactory
registrando un IDbDependencyResolver
y resolviendo para el tipo DbProviderFactory
. Un ejemplo de esto es a continuación:
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);
}
}
es posible que deba resolver algunos tipos adicionales, dependiendo del tipo de anulación que necesite realizar y de la configuración de su proyecto. Básicamente solo comience con el código anterior y continúe depurando hasta que haya determinado todos los servicios que necesita resolver según sus requisitos específicos.
Puede leer más sobre la resolución de dependencia de EF en los siguientes enlaces:
- http://msdn.microsoft.com/en-us/data/jj680697.aspx
- https://entityframework.codeplex.com/wikipage?title=EF%20Configuration%20and%20Extensibility
- https://entityframework.codeplex.com/wikipage?title=Rebuilding%20EF%20providers%20for%20EF6
Además, puede hacer esta configuración anulando DbConfiguration
como se describe en el primer enlace de arriba.
Elegir la fábrica del proveedor de DB programáticamente casi frustra el propósito. También podría usar las clases específicas de SQLite en lugar de todas esas interfaces, ¿no?
JoshRivers publicó una solución para SQLite. De hecho, esto también se puede usar para otros adaptadores. Pude hacerlo funcionar para MySQL usando su ejemplo. He envuelto esto en algo un poco más genérico. Esto se debe ejecutar una vez que se inicia la aplicación y es para el conector .NET versión 6.6.5.0 (pero imagino que también es bueno para otras versiones).
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);
Lo siguiente probablemente causará manchas solares y derrocará a la civilización occidental. Incluso puede provocar un debate sobre la Programación de Cinta Adhesiva (¡hazlo parar!), Pero funciona (por ahora)
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) { }
Vea aquí para ejemplos: MSDN: Obtención de DbProviderFactory
mira el siguiente fragmento
public DataProviderManager(string ProviderName)
{
var _Provider = DbProviderFactories.GetFactory(ProviderName);
}
necesita pasar el ProviderName que en su caso es "System.Data.SQLite".
no es necesario crear la sección de configuración de la aplicación. Esa sección es creada por SQLite en machine.config después de instalar el proveedor de SQLite.net.
el propósito de la sección de aplicación es ayudarlo a obtener la lista de proveedores .NET configurados cuando llama al siguiente comando:
public GetProvidersList () {DataTable table = DbProviderFactories.GetFactoryClasses (); }
Incluso más tarde
Obténgalo usando la configuración anterior. Descubrí que esto parece requerir que el ensamblado del proveedor esté en algún lugar donde el programa en ejecución pueda encontrarlo.
/// <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.
}
}
Otro método que obtiene el campo Instancia directamente desde el ensamblado. Funciona incluso cuando la DLL está en otro lugar en el sistema.
/// <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;
}
RESPUESTA TARDÍA:
Siempre puede obtener directamente una fábrica como esta:
DbProviderFactory factory = System.Data.SQLite.SQLiteFactory.Instance;
// (note that the rest of the code is still provider-agnostic.)
O use su contenedor IoC para resolver el DbProviderFactory
, por ejemplo:
container.RegisterInstance<DbProviderFactory>(SQLiteFactory.Instance);
Prefiero no usar DbProviderFactories.GetFactory
debido a su limitación de requerir un archivo de configuración (o un hack como en la respuesta de @ JoshRiver ).
Todo DbProviderFactories.GetFactory
hace DbProviderFactories.GetFactory
es que busca el nombre calificado de ensamblador registrado del tipo de fábrica utilizando el nombre del proveedor, y luego obtiene el valor de la propiedad Instance
estática mediante reflexión.
Si no desea utilizar la configuración, uno de los métodos anteriores podría ser más conveniente dependiendo de su caso de uso.