Home > Blockchain >  Entity framework syntax confusion with DBset
Entity framework syntax confusion with DBset

Time:09-22

I'm currently following a C# tutorial, and it has this ApplicationDbContext class file:

public class ApplicationDbContext: DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) {}
public DbSet<Category> Category { get; set; }   
}

Where Category is defined as:

public class Category
{
    [Key]
    public int ID { get; set; }

    [Required]
    public string Name { get; set; }

    public int DisplayOrder { get; set; }
}

Which is then later referenced like so:

    private readonly ApplicationDbContext _db;

    public IEnumerable<Category> Categories { get; set; }

    public IndexModel(ApplicationDbContext db)
    {
        _db = db;
    }

    public void OnGet()
    {
        Categories = _db.Category;
    }

I also have this in the Program.cs file:

  builder.Services.AddDbContext<ApplicationDbContext>(options => 
  options.UseSqlServer(
    builder.Configuration.GetConnectionString("DefaultConnection")
  ));

The final piece of info is that I did a migration to create a database table called "Category". My confusion is around how everything is declared here; the line DbContext file with DbSet doesn't seem to instantiate anything and instead just defines Category as a public variable with type DbSet, but then the IEnumerable seems to treat Category as if it's going through the table, as does calling _db.Category. In this case, looking through the code, the Category class is what gets referenced and not the Category database table. is How does everything tie together here?

CodePudding user response:

The ApplicationDbContext is just the definition of the data context. This effectively defines the tables in your database. Each DbSet<T> represents a table in your data schema.

  • DbContext is the base class of this database schema definition.

Category is a data Model, on it's own, it is just another class that represents something... But when you use it in the DbContext via the DbSet<Cateogry> type it becomes a table definition. When you read this line:

public DbSet<CategoryType> CategoryName { get; set; } 

Interpret it as this: Table called CategoryName that the structure of the CategoryType class.

So far so good,

This next bit is related to a concept called Dependency Injection. the idea is that we can define at a global level a set of instructions that describe how to instantiate objects that we need to use a lot throughout the application without putting that instantiation logic everywhere. It is a way to define this logic once and is inline with the DRY principal.

  • You might think that the contructor of a class is an appropriate place for this type of information and before DI it was a common option, but for compex types this often requires a lot of external domain knowledge that is specific to the current application, which makes it hard to re-use your classes on other applications or contexts. DI is an elegant solution to this that forces you to totally divorce the definition of your types and the implementation.

  • After contructors (and before DI), the next common implementation was to use the Factory Pattern to create objects using domain specific logic. Think of DI is a specific implementation of the Factory Pattern.

So in Porgram.cs, the following code is registering the ApplicationDbContext type for DI, using the AddDbContext factory.

builder.Services.AddDbContext<ApplicationDbContext>(options => 
options.UseSqlServer(
  builder.Configuration.GetConnectionString("DefaultConnection")
));
  • Part of this registration is to specify the connection string, in this case it is telling the underlying factory method to get the connection string from the configuration file.

The magic occurs now in the rest of your DI stack, in this case I think you have a repository called IndexModel, you might find later in the Program.cs there is more DI registration related to that class, but the idea is that when the IndexModel is needed, DI will call the constructor on that class.

public class IndexModel
{
    public IndexModel(ApplicationDbContext db) { ... }
}

As it requires an instance of ApplicationDbContext and DI knows how to create an instance of that type, it uses the AddDbContext factory to create a new instance of ApplicationDbContext and pass it through as an argument to the IndexModel constructor.

CodePudding user response:

the line DbContext file with DbSet doesn't seem to instantiate anything and instead just defines Category as a public variable with type DbSet

Correct

then the IEnumerable seems to treat Category as if it's going through the table

Also correct. the code in Program.cs ties the two together. First, it enables Dependency Injection of _db by calling AddDbContext<ApplicationDbContext>(); and second, it directs _db to retrieve data from the source (usually, database) defined in DefaultConnection. You should have this definition in appsettings.json - for example,

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=servername;Initial Catalog=dbname;User ID=username;password=password;"
  }
}
  • Related