locked
Initialize a DBSet collection inside a Razor Component RRS feed

  • 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