MVC 6 EF7 RC1创建多个数据库上下文

4

我正在尝试找出如何在EF7 RC1中创建第二个DB上下文。 以前,我可以使用带有:base("connectionName")的构造函数,但现在似乎不再是选项,因为它说无法将字符串转换为System.IServiceProvider。

我的第二个上下文代码如下:

public class DecAppContext : DbContext
    {

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

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

在我的config.json文件中,我已经指定了连接:

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

在我的启动配置服务的部分,我添加了两个上下文:
services.AddEntityFramework()
                .AddSqlServer()
                .AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]))
                .AddDbContext<DecAppContext>(options => options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

应用程序DB上下文正常工作,因为我可以创建用户并且登录没有问题。
但是当我尝试通过控制器访问其他上下文时:
private DecAppContext db = new DecAppContext();
var vignette = db.VignetteModels.SingleOrDefault(v => v.CaseId == vid);

我得到了这个错误:
没有配置数据库提供程序。通过在您的DbContext类中覆盖OnConfiguring方法或在设置服务时使用AddDbContext方法来配置数据库提供程序。
非常感谢EF7 RC1中具有多个db上下文并访问它们的任何工作示例。
1个回答

8
首先,我建议您阅读 EntityFramework 在 GitHub 的 wiki 上的 文章。该文章介绍了许多定义 DbContext 的方法,其中引用了 appsettings.json 部分的内容。我个人更喜欢使用 [FromServices] 属性的方式。
代码可能涉及以下内容:
首先,您可以定义以下内容的 appsettings.json
{
  "Data": {
    "ApplicationDbConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ApplicationDb;Trusted_Connection=True;MultipleActiveResultSets=true",
    "DecAppDbConnectionString": "Server=Server=(localdb)\\mssqllocaldb;Database=DecAppDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

这里您需要定义两个连接字符串。

其次,您需要声明 DecAppContext ApplicationDbContext 类,它们的基类是 DbContext 。最简单的形式只需要:

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

没有任何DbSet属性。

第三步。您可以使用Microsoft.Extensions.DependencyInjection来注入数据库上下文。为此,您只需在Startup.cs中包含以下内容即可:

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)
    {
        ...
    }
}

我们需要使用配置"Data:DecAppDbConnectionString""Data:ApplicationDbConnectionString"来创建两个DbContextDecAppContextApplicationDbContext)。

现在我们可以在控制器中直接使用这些上下文。例如:

[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;
        }
    }
}

或者使用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;
        }
    }
}

可以使用属性[FromServices]声明属性public ApplicationDbContext ApplicationDbContext { get; set; },并在ASP.NET中通过ConfigureServices注入的上下文初始化它。同样,每当需要时,可以使用第二个上下文DecAppContext
以上代码示例将在数据库上下文中执行SELECT Id, FirstName From dbo.Users,并以[{"id":123, "firstName":"Oleg"},{"id":456, "firstName":"Xaxum"}]的形式返回JSON数据。由于在ConfigureServices中使用了AddJsonOptions,因此在序列化期间会自动将属性名称从IdFirstName转换为idfirstName更新:我必须参考公告。MVC的下一个版本(RC2)将要求更改上述代码,以使用[FromServices]作为附加参数(例如方法Get()的附加参数),而不是使用公共属性[FromServices] public ApplicationDbContext ApplicationDbContext { get; set; }。您将需要删除属性ApplicationDbContext,并向Get()方法添加附加参数:public async IEnumerable<object> Get([FromServices] ApplicationDbContext applicationDbContext) {...}。这种更改可以轻松完成。请参见此处以及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
    }

如果我正在使用相同的数据库,我需要有两个不同的连接字符串吗?还是可以使用与我的config.json中当前使用的相同的连接字符串? - Xaxum
@Xaxum:可以使用相同的数据库或不同的数据库,选择自由。使用两个不同上下文的主要目标是相应表格的位置可以不同。您只能在一个上下文实体之间定义引用关系。因此,我认为使用两个连接字符串提供了更好的选择。您无需更改代码即可将表移动到不同的数据库中。我再次强调:这是您的选择,您可以使用一个或两个连接字符串。 - Oleg
@Xaxum:我想强调的是,使用public DbSet<VignetteModels> VignetteModels { get; set; }或其他DbSet不必要的,但你当然可以使用它。我个人使用存储过程或动态选择生成从Web Api方法返回的数据。ASP.NET 5没有SqlCommand,但Entity Framework 7支持原始SQL查询(通过DbSet.FromSql)和.CreateCommand(),允许使用纯SQL语句或存储过程。我在代码中演示了如何使用它。/api/users返回在"SELECT Id, FirstName FROM dbo.Users"中定义的属性。 - Oleg
@Xaxum:你可以在这里和这里阅读有关原始SQL查询的用法。 - Oleg
Oleg,感谢您提供的所有额外细节。您关于只能在一个实体中定义关系的评论很有帮助。在我进行了更改之后,当我使用<DBSet>时,由于我正在使用对另一个上下文的引用,所以出现了另一个关于键已定义的错误。这为我节省了大量时间。非常感谢。 - Xaxum
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接