我没有使用DI,只想从我的控制器中调用DbContext。我正在努力弄清楚'选项'应该是吗?
ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Gig> Gigs { get; set; }
public DbSet<Genre> Genres { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
GigsController.cs
public class GigsController : Controller
{
private ApplicationDbContext _context;
public GigsController()
{
_context = new ApplicationDbContext();
}
public IActionResult Create()
{
var viewModel = new GigFormViewModel
{
Genres = _context.Genres.ToList()
};
return View(viewModel);
}
}
问题出现在我的GigsController构造函数中:
_context = new ApplicationDbContext();
我错了,因为我需要将一些东西传递给ApplicationDbContext。没有任何论据符合所要求的形式参数&#39;选项&#39; &#39; ApplicationDbContext.ApplicationDbContext(DbContextOptions)&#39;
我尝试在从base()派生的ApplicationDbContext中创建一个默认构造函数,但是它也没有工作。
在我的startup.cs中,我已经配置了ApplicationDbContext
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
答案 0 :(得分:39)
如果您真的想要手动创建上下文,那么您可以configure这样:
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(Configuration.GetConnectionStringSecureValue("DefaultConnection"));
_context = new ApplicationDbContext(optionsBuilder.Options);
(DbContextOptionsBuilder<ApplicationDbContext>
类是options
中services.AddDbContext<ApplicationDbContext>(options =>
参数的类型。
但是在控制器中,您无法访问Configuration
对象,因此您必须将其作为Startup.cs
中的静态字段公开或使用其他技巧,这都是不好的做法。
获得ApplicationDbContext
的最佳方法是通过DI:
public GigsController(ApplicationDbContext context)
{
_context = context;
}
DI容器将负责实例化{strong>并处理 ApplicationDbContext
。请注意,您已在Startup.cs
中正确配置了所有内容:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
那是配置DI,为什么不使用它?
关于DbContext
的默认构造函数的另外一个注意事项:在EF6中,它是这样完成的:public ApplicationDbContext(): base("DefaultConnection") {}
。然后,基础对象将使用System.Configuration.ConfigurationManager
静态类从DefaultConnection
获取名为web.config
的连接字符串。新的Asp.net Core和EF Core旨在尽可能多地解耦,因此它不应该依赖于任何配置系统。相反,您只需传递一个DbContextOptions
对象 - 创建该对象并对其进行配置是一个单独的问题。
答案 1 :(得分:1)
这就是我要做的:
public class GigsController : Controller
{
private readonly IConfiguration _configuration;
private string _connectionString;
DbContextOptionsBuilder<ApplicationDbContext> _optionsBuilder;
public GigsController (IConfiguration configuration)
{
_configuration = configuration;
_optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
_connectionString = _configuration.GetConnectionString("DefaultConnection");
_optionsBuilder.UseSqlServer(_connectionString);
}
public IActionResult Index()
{
using(ApplicationDbContext _context = new ApplicationDbContext(_optionsBuilder.Options))
{
// .....Do something here
}
}
}
最近,我正在将一个非常大的数据集迁移到数据库(大约1000万个)中,一个上下文实例将很快耗尽我的所有内存。因此,我必须创建一个新的Context实例,并在一定阈值后处置旧的Context实例以释放内存。
这不是一个很好的解决方案,但对我有用。
答案 2 :(得分:1)
我个人不明白为什么您不想使用DI,而只是通过在控制器的构造函数中指定(ApplicationDbContext db)
来代表您创建它,实际上您是使用代码在DI中注册它的无论如何:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
如果您绝对想显式调用new ApplicationDbContext(...)
,请记住在控制器中调用Configuration是一个坏主意,但是您需要配置才能获取连接详细信息,因为您必须提供DbContextOptions作为对上下文的争论。我建议完全删除services.AddDbContext
,因为从不打算解决它。内置的DI似乎没有完成“工厂”注册的干净方法。我使用Autofac和Simple Injector,它们在lambda表达式中提供了非常干净的方式来实现这些功能:
containerBuilder.Register(c =>
{
var optionsBuilder = new DbContextOptionsBuilder<EntityDbContext>()
.UseSqlServer(Configuration.GetConnectionStringSecureValue("DefaultConnection"));
return optionsBuilder.Options;
});
然后您只需执行以下操作:
public GigsController(DbContextOptionsBuilder<EntityDbContext> dbContextOptions)
{
_context = new ApplicationDbContext(dbContextOptions);
}
因此,如果要集成Autofac,那是一种方法。
我刚刚设法弄清楚了所有这些注射剂和配置,并有了一个不错的干净解决方案,它将解决您的问题,包括读取配置。想法是您从appsettings.json中读取配置,并将其分配给配置类上的连接字符串属性:
public interface IDatabaseConfig
{
string ConnectionString { get; set; }
}
public class DatabaseConfig : IDatabaseConfig
{
public DatabaseConfig()
{
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
ConnectionString = configuration.GetSection("Database").GetValue<string>("ConnectionString");
}
public string ConnectionString { get; set; }
}
然后您在ConfigureServices中注册接口:
services.AddTransient<IDatabaseConfig, DatabaseConfig>();
并将接口用作控制器构造函数参数,然后可以创建选项:
public GigsController(IDatabaseConfig dbConfig)
{
var dbContextOptions = new DbContextOptions<ApplicationDbContext>().UseSqlServer(dbContextOptions.ConnectionString);
_context = new ApplicationDbContext(dbContextOptions);
}
我不喜欢直接在该类上创建配置构建器。 ASP.NET Core已经提供了一个,所有这些都应该在Startup类中完成。最佳做法是使用以下方法从appsettings中反序列化DatabaseConfig:
var databaseConfig = configuration.GetSection("Database").Get<DatabaseConfig>();
但是我看不到一种方法来注册该实例或将其推迟到DI工厂样式注册,所以这不是一个选择。
真的,您最好像原来一样使用serices.AddDbContext<ApplicationDbContext>(...)
并将其作为构造函数参数注入控制器中,这样就可以解决问题。
我个人解决整个情况的方式使您可以自由配置所需的选项,甚至可以将SqlServer连接切换为内存中的数据库,以运行集成测试,而您无法访问实际的数据库。 CI构建的一部分,如下所示...
我有一个DatabaseConfig对象图:
public class Config
{
public DatabaseConfig Database { get; set; }
}
public interface IDatabaseConfig
{
InMemoryConfig InMemory { get; set; }
string ConnectionString { get; set; }
}
public class DatabaseConfig : IDatabaseConfig
{
public InMemoryConfig InMemory { get; set; }
public string ConnectionString { get; set; }
}
public class InMemoryConfig
{
public bool Enabled { get; set; }
public string Name { get; set; }
}
与此结构保持一致,并从appsettings.json中反序列化:
"Database": {
"InMemory": {
"Enabled": true,
"Name": "Snoogans"
},
"ConnectionString": "Server=(localdb)\\MSSQLLocalDB;Database=SomeDb;Trusted_Connection=True;"
}
一个开箱即用的选项是这样做
var databaseConfig = configuration.GetSection("Database").Get<DatabaseConfig>();
但是我使用的是Autofac,还有一个名为Divergic.Configuration.Autofac的可爱的nuget包,它允许您使用ConfigureContainer
方法执行此操作:
builder.RegisterModule<ConfigurationModule<JsonResolver<Config>>>();
如果“配置”图上的属性实现了接口,则将向Autofac进行注册,并将设置反序列化到服务实例上。本身就足以将IDatabaseConfig作为构造函数参数注入到任何控制器上,然后可以自己对其进行更新,但这实际上最好在一个地方完成,否则您必须在每次使用它的地方都重复DbContextOptionsBuilder逻辑。
因此,我在ConfigurationModule注册后进行了工厂注册,该注册使用appsettings.json中的选项创建了我的数据库上下文:
containerBuilder.Register(c =>
{
var optionsBuilder = new DbContextOptionsBuilder<EntityDbContext>();
optionsBuilder = databaseConfig.InMemory.Enabled
? optionsBuilder.UseInMemoryDatabase(databaseConfig.InMemory.Name)
: optionsBuilder.UseSqlServer(databaseConfig.ConnectionString);
return optionsBuilder.Options;
});
这是一个干净的解决方案,职责不会泄漏到不应执行的区域。您的控制器不应该负责数据库ORM的创建。应该只给它一个预先创建的使用,否则以后很难更改。考虑一下是否有500个控制器在所有情况下都可以手动创建它们,而不是传递一个在一个地方完成创建代码的预先创建的实例。如果在我的数据库上下文实现IWriteEntities和IReadEntities时再进一步,那么它就更加抽象了,您可以切换到另一个完整的DbContext子类,并将重做简化为注册数据库上下文的一行。
答案 3 :(得分:0)
public class PaymentDetailContext:DbContext //inherit this class from DB context from entity framework core DB Context
{
public PaymentDetailContext(DbContextOptions<PaymentDetailContext> options):base(options)
//CONSTRUCTOR-------------PARAMETER----------TYPE----------------NAME---PARENT CONTSTRUCTOR
{
}
public DbSet<PaymentDetail> PaymentDetails { get; set; }
}