首页 文章

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

提问于
浏览
1

我是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-> public更新构造函数;

我收到一个错误:

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

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

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

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

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

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

private Bill _bill;
    public BillController(AppDbContext db)
    {
        _bill = new Bill(db);
    }

谢谢!

2 回答

  • 1

    System.NullReferenceException

    你得到 System.NullReferenceException: Object reference not set to an instance of an object. 的原因是你的构造函数方法是 private

    私人账单(AppDbContext db)

    Solution: 您的构造函数应为 public .

    否则它不能在它自己的课程之外访问 . 你可以通过在 _ctx = db 上放置一个断点来测试它,你会发现它没有被调用 .

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

  • 1

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

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

    请求改变

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

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

    服务的优点是:

    • 您可以通过IOC容器解决它

    • 没有奇怪的模型构造函数的问题(现在每个模型都需要AppDbContext,即使它不需要它)

    • 如果服务实现了相同的接口,您可以轻松地将服务替换为另一个服务 . (你使用这个界面的课程)

    • 虚拟实例方法不可覆盖静态方法 . (当项目变大时会导致问题)

    这将导致以下代码:

    public class EntityFrameworkBillService : IBillService
    {
        public AppDbContext _ctx;
        public Bill(AppDbContext db)
        {
            _ctx = db;
        }
    
        public async Task<List<Bill>> GetBills(BillGetParams billParams)
        {
             var bill = _ctx... //trying to acces an instance property in static context, does not work. and shouldn't even compile.
        }
    }
    //startup.cs:
    services.AddTransient<IBillService,EntityFrameworkBillService>()
    
    //Usage 
    public MyController(IBillService billService) //constructor
    {
        var dollahBills = billService.GetBills();
    }
    
     //And if you really wish this method to be there, and be lazy (but be warned: its an anti pattern)
    public class Bill
    {
        public static async Task<List<Bill>> GetBills(BillGetParams billParams)
        {
             //Make sure your ServiceProvider is available as static on Program in startup.cs.
             return Program.ServiceProvider.Resolve<IBillService>().GetBills(billParams);
        }
    }
    

相关问题