实体框架核心依赖注入模型

时间:2017-02-22 03:46:17

标签: c# sql entity-framework .net-core

我是ASP.NET Core / C#的新手,并且正在努力学习。我已经阅读过教程,观看视频,甚至还买了一本书。我想把我的AppDBcontext注入我的模特..

这是我的Startup班级

    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        string dbConnection = Configuration["ConnectionStrings:AzureSQL"];

        services.AddDbContext<AppDbContext>(options => {
            options.UseSqlServer(dbConnection);
        });

        services.AddScoped<AppDbContext>();
        services.AddMvc();
    }

和我的AppDbContext.cs

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
    : base(options) { }

    public DbSet<Monthly> Monthly { get; set; }
    public DbSet<Bill> Bill { get; set; }
}

我尝试过类似我的模特

public class Bill
{
    public AppDbContext _ctx;
    public Bill(AppDbContext db)
    {
        _ctx = db;
    }

    public class BillGetParams
    {
        public string MonthlyID { get; set; }
        public string UserID { get; set; }
        public string BillID { get; set; }
    }

    public int ID { get; set; }
    public int? MonthlyID { get; set; }
    public string BillName { get; set; }
    public string Comment { get; set; }
    public int? Amount { get; set; }
    public bool? Payed { get; set; }
    public bool? Recursive { get; set; }
    public string UserID { get; set; }
    public string UUID { get; set; }
    public DateTime? CreatedAt { get; set; }

    public static async Task<List<Bill>> LoadBills(BillGetParams billParams)
    {
        var bills = _ctx.Bill.FromSql($@"
        SELECT * FROM bills WHERE MonthlyId={billParams.MonthlyID}");

        return await bills.ToListAsync();
    }

    public static async Task<Bill> LoadBill(BillGetParams param)
    {
        var bill = _ctx.Bill.FromSql($@"
            SELECT * FROM bills
                WHERE UserID='{param.UserID}' AND ID='{param.BillID}'
        ");

        return await bill.SingleOrDefaultAsync();
    }

编辑:为Bill类和使用_ctx的方法添加了部分代码,还通过删除static和private-&gt; public来更新构造函数;

我收到错误:

  

System.NullReferenceException:未将对象引用设置为对象的实例。

因为db_ctx的分配似乎从未发生过......

我不确定哪个最好,但我之前使用的是AppDbContext。我应该继续这样使用吗?

using(var ctx = new AppDbContext())
{
    var bills = ctx.Bill.FromSql($@"select 1 from bills")
    // do stuff
}

从互联网上的例子来看,DI在控制器上运行良好..但我不想从控制器那里做SQL的东西..有没有更好的方法在dotnet中抽象SQL /数据访问?或者我做错了?

解决方案1:所以,阅读,试用,@Bharat的解决方案和实验。我用最简单的方式解决了我的问题..希望我是对的。我在使用BillController的{​​{1}}中添加了这个,我现在可以很好地访问我的SQL。

Bill.LoadBill()

谢谢!

2 个答案:

答案 0 :(得分:1)

System.NullReferenceException

您获得{ // See http://go.microsoft.com/fwlink/?LinkId=759670 // for the documentation about the jsconfig.json format "compilerOptions": { "target": "es2017" }, "exclude": [ "node_modules", "bower_components", "jspm_packages", "tmp", "temp" ] } 的原因是因为您的构造函数方法是System.NullReferenceException: Object reference not set to an instance of an object.

private

解决方案:您的构造函数应为private Bill(AppDbContext db) { _ctx = db; }

否则它不能在它自己的课程之外访问。您可以通过在public上放置断点来测试这一点,您将看到它没有被调用。

为了进一步阅读和理解,我建议您阅读此Microsoft文档:Dependency Injection in ASP.NET Core

答案 1 :(得分:1)

我看到的最大错误就是这个:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "secondaryEntityManagerFactory",
        transactionManagerRef = "secondaryTransactionManager",
        basePackages = {"com.ubl.*"})
public class SecondaryDBConfig {

    @Autowired
    JpaVendorAdapter jpaVendorAdapter;

    @Value("${datasource.secondary.url}")
    private String databaseURL; 

    @Value("${datasource.secondary.username}")
    private String username;

    @Value("${datasource.secondary.password}")
    private String password;

    @Value("${datasource.secondary.driverClassName}")
    private String driverClassName;

    @Value("${datasource.secondary.dialect}")
    private String dialect;

    public SecondaryDBConfig() {
        System.out.println("Secondary repository");
        System.out.println("driverClassName: *************" +driverClassName);
    }

    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseURL, username, password);
        dataSource.setDriverClassName(driverClassName);
        return dataSource;
    }

    @Bean(name = "secondaryEntityManager")
    public EntityManager entityManager() {
        return entityManagerFactory().createEntityManager();
    }

    @Bean(name = "secondaryEntityManagerFactory")
    public EntityManagerFactory entityManagerFactory() {
        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect", dialect);

        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource());
        emf.setJpaVendorAdapter(jpaVendorAdapter);
        emf.setPackagesToScan("com.ubl.model.*");   // package for entities
        emf.setPersistenceUnitName("secondaryPersistenceUnit");
        emf.setJpaProperties(properties);
        emf.afterPropertiesSet();
        return emf.getObject();
    }

    @Bean(name = "secondaryTransactionManager")
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager(entityManagerFactory());
    }


}

请求改变结构

我认为提供模型的方法可以直接访问它们的实例,但请不要这样做,即使是要填充额外的属性或列表。

这样您就可以在整个应用程序中拆分DataAccess代码(sql)。 相反,最好将所有dataAcces分组到控制器中,甚至更好地在单独的项目和服务中进行分组。

服务的优点是:

  • 您可以通过IOC容器解决它
  • 没有奇怪的模型构造函数的问题(现在每个模型都需要AppDbContext,即使它不需要它)
  • 如果服务实现了相同的接口,您可以轻松地将服务替换为另一个服务。 (并使用此界面)
  • 虚拟实例方法不可覆盖静态方法。 (当项目变大时,这将导致问题)

这将导致以下代码:

public class Bill
{
    public AppDbContext _ctx;
    public Bill(AppDbContext db)
    {
        _ctx = db;
    }

    public static async Task<List<Bill>> LoadBills(BillGetParams billParams)
    {
         var bill = _ctx... //trying to acces an instance property in static context, does not work. and shouldn't even compile.
    }
}