Answered by:
Initialize a DBSet collection inside a Razor Component

Question
-
User2090306728 posted
How can I initialize a DBSet collection variable?
<div> @categories = GetCategories @foreach (ProductModel_Category_Tier1 category in categories) { <table> <tr> <td>@category.Name</td> </tr> </table> } </div> @code { public List<ProductModel_Category_Tier1> categories { get; set; } List<ProductModel_Category_Tier1> GetCategories() { using (var db = new ProductCategoriesContext()) { return db.Categories_Tier1.ToList(); } } }
"categories" is not being set.
Can anyone tell me what I'm doing wrong here?
This works, but I also want to do a count on the number of items in the collection, so don't see another call to GetCategories as necessary:
@foreach (ProductModel_Category_Tier1 category in GetCategories())
Any help would be greatly appreciated.
Monday, August 3, 2020 9:48 AM
Answers
-
User-474980206 posted
you just make the variable a property. but you code it not well designed. a component can render multiple times, and you are doing a database call on each render. you are also nt using async, so you are locking the render thread.
<div> @foreach (ProductModel_Category_Tier1 category in categories) { <table> <tr> <td>@category.Name</td> </tr> </table> } </div> @code { public List<ProductModel_Category_Tier1> categories { get; set; } async Task<List<ProductModel_Category_Tier1>> GetCategoriesAsync() { using (var db = new ProductCategoriesContext()) { return await db.Categories_Tier1.ToListAsync(); } } protected override async Task OnInitializedAsync() { categories = await GetCategoriesAsync(); } }
note: because you are doing database queries in the component, your code will only work sever side.
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Monday, August 3, 2020 2:45 PM -
User475983607 posted
I'll assume this is a Blazor server application. The OnInitializeAsync runs twice. Once when the page loads and again when the SignalR connection is made. https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-3.1
The recommended programming pattern is using state to make the call when after the connection is made. https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-3.1#stateful-reconnection-after-prerendering
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Tuesday, August 4, 2020 11:58 AM
All replies
-
User-474980206 posted
you just make the variable a property. but you code it not well designed. a component can render multiple times, and you are doing a database call on each render. you are also nt using async, so you are locking the render thread.
<div> @foreach (ProductModel_Category_Tier1 category in categories) { <table> <tr> <td>@category.Name</td> </tr> </table> } </div> @code { public List<ProductModel_Category_Tier1> categories { get; set; } async Task<List<ProductModel_Category_Tier1>> GetCategoriesAsync() { using (var db = new ProductCategoriesContext()) { return await db.Categories_Tier1.ToListAsync(); } } protected override async Task OnInitializedAsync() { categories = await GetCategoriesAsync(); } }
note: because you are doing database queries in the component, your code will only work sever side.
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Monday, August 3, 2020 2:45 PM -
User2090306728 posted
Getting an exception at the foreach loop:
System.NullReferenceException: 'Object reference not set to an instance of an object.' UI.Web.Components.Products.Categories.Product_Category_Tier1.categories.get returned null.
Is my DBContext built correctly? The datacontext was getting retrieved when I was doing it incorrectly...
The ProductCategoriesContext class is in a Class Library of another project in the solution.
public class ProductCategoriesContext : DbContext { //public ProductCategoriesContext(DbContextOptions<ProductCategoriesContext> options) : base(options) { } #region CATEGORIES public DbSet<ProductModel_Category_Tier1> Categories_Tier1 { get; set; } public virtual DbSet<ProductModel_Category_Tier2> Categories_Tier2 { get; set; } public virtual DbSet<ProductModel_Category_Tier3> Categories_Tier3 { get; set; } public virtual DbSet<ProductModel_Category_Type> Category_Types { get; set; } #endregion protected override void OnConfiguring(DbContextOptionsBuilder options) { options.UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=TestDB;Integrated Security=True;"); } } }
Couldn't get the connection string from my UI project so had to explicity declare it. Figured I'd get it sorted out after I got everything working.
Monday, August 3, 2020 3:33 PM -
User-474980206 posted
your coding error. OnConfiguring will not be called because you did not pass the config builder to the base constructor.
while I think its poor design to call EF from blazor components, you should inject the dbcontext. see:
https://www.blogofpi.com/crud-using-blazor-and-entity-framework-core/
Monday, August 3, 2020 3:49 PM -
User2090306728 posted
Really sorry about this Bruce, I wasn't able to pass the config builder to the base constructor as explained here: https://forums.asp.net/t/2169553.aspx?EFCore+Migrations+ConnectionString
Any chance you could give it a look? Just seem to be getting more and more lost.
Monday, August 3, 2020 4:18 PM -
User2090306728 posted
Added a null check to the HTML. Exception was caused by the system taking its time to populate the variable.
So... should a async call be used or should I be using a standard call?
Because in theory, if the async is run, I'm just waiting for something to show regardless.
<div> @if (categories != null) { @foreach (ProductModel_Category_Tier1 category in categories) { <table> <tr> <td>@category.Name</td> <td>@category.SortOrder</td> </tr> </table> } } </div>
@code { public List<ProductModel_Category_Tier1> categories { get; set; } async Task<List<ProductModel_Category_Tier1>> GetCategoriesAsync() { using (var db = new ProductCategoriesContext()) { return await db.Categories_Tier1.ToListAsync(); } } protected override async Task OnInitializedAsync() { categories = await GetCategoriesAsync(); } //protected override void OnInitialized() //{ // base.OnInitialized(); // categories = GetCategories(); //} List<ProductModel_Category_Tier1> GetCategories() { using (var db = new ProductCategoriesContext()) { return db.Categories_Tier1.ToList(); } } }Tuesday, August 4, 2020 11:43 AM -
User475983607 posted
I'll assume this is a Blazor server application. The OnInitializeAsync runs twice. Once when the page loads and again when the SignalR connection is made. https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-3.1
The recommended programming pattern is using state to make the call when after the connection is made. https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-3.1#stateful-reconnection-after-prerendering
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Tuesday, August 4, 2020 11:58 AM -
User2090306728 posted
Thanks @mgebhard for clarifying the reasoning behind the async call and thanks Bruce for the initial solution and pointing me in the right direction.
Tuesday, August 4, 2020 12:08 PM