MVC 6 EF7 RC1 creando múltiples dbcontexts

asp.net-core-mvc entity-framework-core

Pregunta

Estoy tratando de averiguar cómo crear un segundo contexto DB en EF7 RC1. En el pasado, podía usar un constructor con: base ("nombre de conexión") pero eso ya no parece ser una opción, ya que dice que no se puede convertir una cadena a System.IServiceProvider.

Mi segundo código de contexto es el siguiente:

public class DecAppContext : DbContext
    {

        public DecAppContext()
          //  :base("DefaultConnection")
        {

        }
        public DbSet<VignetteModels> VignetteModels { get; set; }
        public DbSet<VignetteResult> Result { get; set; }
    }
}

En mi config.json tengo la conexión especificada:

"Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-xxxxx...;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
  }

En mi sección de servicios de configuración de mi inicio tengo ambos contextos agregados:

services.AddEntityFramework()
                .AddSqlServer()
                .AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]))
                .AddDbContext<DecAppContext>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

El contexto de applicationDB funciona bien ya que puedo crear un usuario e iniciar sesión sin problemas

Sin embargo, cuando intento acceder al otro contexto como en mi controlador a través de:

private DecAppContext db = new DecAppContext();
var vignette = db.VignetteModels.SingleOrDefault(v => v.CaseId == vid);

Me sale el error

No hay proveedores de bases de datos configurados. Configure un proveedor de base de datos anulando OnConfiguring en su clase DbContext o en el método AddDbContext al configurar los servicios.

Cualquier ejemplo de trabajo en EF7 RC1 con múltiples contextos db y acceder a ellos sería muy apreciado.

Respuesta aceptada

En primer lugar, le recomendaría el artículo de la wiki de EntityFramework en GitHub. El artículo describe muchas formas de definir DbContext , que hace referencia a una sección de appsettings.json . Personalmente prefiero la forma con el uso del atributo [FromServices] .

El código podría ser sobre lo siguiente:

En primer lugar, definió appsettings.json con el siguiente contenido

{
  "Data": {
    "ApplicationDbConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ApplicationDb;Trusted_Connection=True;MultipleActiveResultSets=true",
    "DecAppDbConnectionString": "Server=Server=(localdb)\\mssqllocaldb;Database=DecAppDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

donde se definen dos cadenas de conexión.

En segundos, declara las clases DecAppContext y ApplicationDbContext que tienen DbContext como la clase base. La forma más sencilla será simplemente

public class ApplicationDbContext : DbContext
{
}
public class DecAppContext : DbContext
{
}

sin ninguna propiedad DbSet .

Tercer paso. Utiliza Microsoft.Extensions.DependencyInjection para inyectar los contextos de base de datos. Para hacer esto solo necesitas incluir en Startup.cs algo como

public class Startup
{
    // property for holding configuration
    public IConfigurationRoot Configuration { get; set; }

    public Startup(IHostingEnvironment env)
    {
        // Set up configuration sources.
        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddEnvironmentVariables();
        // save the configuration in Configuration property
        Configuration = builder.Build();
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddMvc()
            .AddJsonOptions(options => {
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });
        services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<ApplicationDbContext>(options => {
                options.UseSqlServer(Configuration["Data:ApplicationDbConnectionString"]);
            })
            .AddDbContext<DecAppContext>(options => {
                options.UseSqlServer(Configuration["Data:DecAppDbConnectionString"]);
            });
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        ...
    }
}

Se crean dos DbContext ( DecAppContext y ApplicationDbContext ) utilizando la configuración "Data:DecAppDbConnectionString" y "Data:ApplicationDbConnectionString" .

Ahora solo podemos usar el contexto en el controlador. Por ejemplo

[Route("api/[controller]")]
public class UsersController : Controller
{
    [FromServices]
    public ApplicationDbContext ApplicationDbContext { get; set; }

    [FromServices]
    public DecAppContext DecAppContext { get; set; }

    [HttpGet]
    public IEnumerable<object> Get() {
        var returnObject = new List<dynamic>();

        using (var cmd = ApplicationDbContext.Database.GetDbConnection().CreateCommand()) {
            cmd.CommandText = "SELECT Id, FirstName FROM dbo.Users";
            if (cmd.Connection.State != ConnectionState.Open)
                cmd.Connection.Open();

            var retObject = new List<dynamic>();
            using (var dataReader = cmd.ExecuteReader())
            {
                while (dataReader.Read())
                {
                    var dataRow = new ExpandoObject() as IDictionary<string, object>;
                    for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++)
                        dataRow.Add(
                            dataReader.GetName(iFiled),
                            dataReader.IsDBNull(iFiled) ? null : dataReader[iFiled] // use null instead of {}
                        );

                    retObject.Add((ExpandoObject)dataRow);
                }
            }
            return retObject;
        }
    }
}

o lo mismo usando async / await:

[Route("api/[controller]")]
public class UsersController : Controller
{
    [FromServices]
    public ApplicationDbContext ApplicationDbContext { get; set; }

    [FromServices]
    public DecAppContext DecAppContext { get; set; }

    [HttpGet]
    public async IEnumerable<object> Get() {
        var returnObject = new List<dynamic>();

        using (var cmd = ApplicationDbContext.Database.GetDbConnection().CreateCommand()) {
            cmd.CommandText = "SELECT Id, FirstName FROM dbo.Users";
            if (cmd.Connection.State != ConnectionState.Open)
                cmd.Connection.Open();

            var retObject = new List<dynamic>();
            using (var dataReader = await cmd.ExecuteReaderAsync())
            {
                while (await dataReader.ReadAsync())
                {
                    var dataRow = new ExpandoObject() as IDictionary<string, object>;
                    for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++)
                        dataRow.Add(dataReader.GetName(iFiled), dataReader[iFiled]);

                    retObject.Add((ExpandoObject)dataRow);
                }
            }
            return retObject;
        }
    }
}

Uno puede simplemente declarar la propiedad public ApplicationDbContext ApplicationDbContext { get; set; } con el atributo [FromServices] y ASP.NET lo inicializan desde el contexto inyectado en ConfigureServices . De la misma manera, uno puede usar el segundo contexto DecAppContext cuando lo necesite.

El ejemplo del código anterior ejecutará SELECT Id, FirstName From dbo.Users en el contexto de la base de datos y devolverá los datos JSON en la forma [{"id":123, "firstName":"Oleg"},{"id":456, "firstName":"Xaxum"}] . La conversión de nombres de propiedades de Id y FirstName a id y firstName se realizará automáticamente durante la serialización debido al uso de AddJsonOptions en ConfigureServices .

ACTUALIZACIÓN: Tengo que hacer referencia al anuncio . La próxima versión de MVC (RC2) requerirá cambiar el código anterior para usar [FromServices] como parámetro adicional (del método Get() por ejemplo) en lugar de usar la propiedad pública [FromServices] public ApplicationDbContext ApplicationDbContext { get; set; } . Uno tendrá que eliminar la propiedad ApplicationDbContext y agregar un parámetro adicional al método Get() : public async IEnumerable<object> Get([FromServices] ApplicationDbContext applicationDbContext) {...} . Tales cambios pueden ser fáciles de hacer. Vea aquí y un ejemplo de los cambios en el ejemplo de demostración de MVC:

[Route("api/[controller]")]
public class UsersController : Controller
{
    [HttpGet]
    public async IEnumerable<object> Get(
                     [FromServices] ApplicationDbContext applicationDbContext,
                     [FromServices] DecAppContext decAppContext)
    {
        var returnObject = new List<dynamic>();

        // ...  the same code as before, but using applicationDbContext 
        // and decAppContext parameters instead of ApplicationDbContext
        // and DecAppContext properties
    }


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué