1

I am new to ASP.NET Core / C#, and trying to learn. I have read tutorials, watched videos, and even bought a book. I wanted to inject my AppDBcontext to my models..

Here is my Startup class

    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();
    }

and my 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; }
}

I tried something like from my models

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();
    }

edit: added partial code for Bill Class and to methods that use _ctx, also update the constructor by removing static and private->public;

I get an error:

System.NullReferenceException: Object reference not set to an instance of an object.

because it seemed like the assignment of db to _ctx never happened..

I'm not sure which is best, but I use my AppDbContext before like this. Should I continue using it like this?

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

From the internet examples, DI worked fine from controllers.. but I don't want to do SQL stuff from controllers.. is there a better way to abstract SQL / data access in dotnet? Or I am doing it wrong?

solution1: so after reading, tryouts, solutions from @Bharat, and experiments. I solved my problem, in simplest way.. hopefully I am right. I added this in my BillController that use Bill.LoadBill() I can access my sql well now.

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

Thank you!

9
  • Change private access modifier on your constructor method to public Bill (...), I would also question why you have it static in private static AppDbContext _ctx;? Commented Feb 22, 2017 at 4:15
  • thanks for the reply, why i have private static AppDbContext is because my editor vs code keep complaining to me An object reference is required for the non-static field, method, or property 'Bill._ctx' Commented Feb 22, 2017 at 7:34
  • Where you are registering Bill class ? as i don't see any where. Because this will give you a instance of AppDbContext() Commented Feb 22, 2017 at 7:43
  • @Bharat sorry I'm new to Dotnet, what do you mean by registering Bill class? I'm sure I've added it to AppDbContext.cs Commented Feb 22, 2017 at 7:46
  • 1
    you must calling Bill.LoadBill() methods from some where to get data from sql. so there you need to do like Bill yourInstanceName = new Bill(new AppDbContext()) this will provide context to your _ctx using constructure. Commented Feb 22, 2017 at 10:06

2 Answers 2

2

The biggest mistake i see is this one:

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

Plea to archictural change

I think the idea is nice to provide models with methods to access instances of them directly but please don't do this, even if it is to fill extra properties or lists.

This way you are splitting our DataAccess code (sql) throughout your application. Instead its better to group all your dataAcces in controllers or even better in a sepearate project and as Services.

The advantages of a service is that:

  • you can resolve it through the IOC container
  • No problems of weird model constructors (now every model needs AppDbContext even if it doesnt need it)
  • You can easily replace a service with another if it implements the same interface. (and you use this interface ofcourse)
  • Static methods are not overrideable while virtual instance methods are. (this will lead to problems when your project grows bigger)

It would result in the following code:

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);
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

thanks for the point, I'll take that as an advice. as a try experiment, this is what I can pull out. maybe give me a few more weeks, I will make it right :) and maybe create more test app and simple web api services for our production app..
I will try what I can do with that example, it look so clean. I'll do it now
@Hokutosei It might be a bit psuedo code since i didnt use an IDE so you might need to cleanup until you get it working, but this concept should always work. The downside is you will be needing +1 class per model, for the service and +1 if you want to abstract it: the interface. (So you will end up with more code)
thank you for the point, I'm not sure whether should I use this since I would require to create a new files.. but it is a good point I think to make it as a service
1

System.NullReferenceException

The reason why you are getting a System.NullReferenceException: Object reference not set to an instance of an object. is because your constructor method is private:

private Bill(AppDbContext db)
{
    _ctx = db;
}

Solution: Your constructor should be public.

Otherwise it is not accessible outside of it's own class. You can test this by placing a breakpoint on _ctx = db and you will see that it is not getting called.

To further your reading and understanding, I would recommend you read this Microsoft Documentation: Dependency Injection in ASP.NET Core

1 Comment

this seemed to be not working.. I still get _ctx == null

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.