I am building an ASP.NET Core 8.0 Blazor solution. This solution has 3 projects. 1st is an iStar.Framework.dll
project that contains Model Classes
. 2nd is iStar.Framework.ModelIRepos.dll
that contains Interface
repository classes. 3rd is iStar.Framework.Application.dll
which contains repository classes to perform database operations.
iStar.Framework.dll
has a model class named Company
.
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
namespace iStar.Framework.Models
{
public class Company
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[StringLength(150)]
public string Name { get; set; } = string.Empty;
[DataType(DataType.Url)]
public string? NavBarLogoUrl { get; set; }
public bool UseNavBarLogoUrl { get; set; } = true;
/// <summary>
/// A collection of workplaces managed by this company
/// </summary>
public virtual ICollection<WorkStation> Workstations { get; set; } = new HashSet<WorkStation>();
}
}
It also has an interface named IModelRepo
namespace iStar.Framework
{
public interface IModelRepo<T> where T : class
{
/// <summary>
/// GetOne a list of all instances of the <typeparamref name="T"/> satisfying the set of values passed through parameters
/// </summary>
/// <returns>List<typeparamref name="T"/></returns>
Task<List<T>?> GetAllAsync();
/// <summary>
/// GetOne a list of all instances of the <typeparamref name="T"/> satisfying the set of values passed through parameters
/// </summary>
/// <returns>List<typeparamref name="T"/></returns>
Task<List<T>?> GetActiveOnly();
/// <summary>
/// GetOne an instance of <typeparamref name="T"/> with provided primary key value <paramref name="id"/>
/// </summary>
/// <param name="id">Primary Key value</param>
/// <returns></returns>
Task<T>? GetOneAsync(int id);
/// <summary>
/// GetOne a list of all the instances contining the string <paramref name="name"/> in its name value.
/// </summary>
/// <param name="name"></param>
/// <returns>list of <typeparamref name="T"/></returns>
Task<List<T>?> GetAllByNameAsync(string name);
/// <summary>
/// Adds <paramref name="item"/> of type <typeparamref name="T"/> to the end of relevant database table
/// </summary>
/// <param name="item">An instance of <typeparamref name="T"/></param>
/// <returns></returns>
Task AddAsync(T item);
/// <summary>
/// Updates the changes to the database made to the <paramref name="item"/> of type <typeparamref name="T"/>
/// </summary>
/// <param name="model">An already saved instance of <typeparamref name="T"/> with changes</param>
/// <returns></returns>
Task UpdateAsync(T item);
/// <summary>
/// Attempts to perform a dellete operation on database against an instances of <typeparamref name="T"/> with provided primary key value <paramref name="id"/>
/// </summary>
/// <param name="id">Primary key value of the instance of <typeparamref name="T"/> to be delleted</param>
/// <returns></returns>
Task DeleteAsync(int id);
/// <summary>
/// Attempts to dellete the provided instance of <typeparamref name="T"/>
/// </summary>
/// <param name="item">An instance of <typeparamref name="T"/> to be delleted</param>
/// <returns></returns>
Task DeleteAsync(T item);
/// <summary>
/// GetOne a list of all instances of the <typeparamref name="T"/> satisfying the set of values passed through parameters
/// </summary>
/// <param name="all">Returns all instances if set to True</param>
/// <param name="ActiveOnly">Returns only active instances</param>
/// <returns>List<typeparamref name="T"/></returns>
List<T>? Get(bool all = true, bool ActiveOnly = false);
/// <summary>
/// GetOne an instance of <typeparamref name="T"/> with provided primary key value <paramref name="id"/>
/// </summary>
/// <param name="id">Primary Key value</param>
/// <returns></returns>
T? GetOne(int id);
/// <summary>
/// GetOne a list of all the instances contining the string <paramref name="name"/> in its name value.
/// </summary>
/// <param name="name"></param>
/// <returns>list of <typeparamref name="T"/></returns>
List<T>? GetAllByName(string name);
/// <summary>
/// Adds <paramref name="item"/> of type <typeparamref name="T"/> to the end of relevant database table
/// </summary>
/// <param name="item">An instance of <typeparamref name="T"/></param>
/// <returns></returns>
void Add(T item);
/// <summary>
/// Updates the changes to the database made to the <paramref name="item"/> of type <typeparamref name="T"/>
/// </summary>
/// <param name="model">An already saved instance of <typeparamref name="T"/> with changes</param>
/// <returns></returns>
void Updatec(T item);
/// <summary>
/// Attempts to perform a dellete operation on database against an instances of <typeparamref name="T"/> with provided primary key value <paramref name="id"/>
/// </summary>
/// <param name="id">Primary key value of the instance of <typeparamref name="T"/> to be delleted</param>
/// <returns></returns>
void Delete(int id);
/// <summary>
/// Attempts to dellete the provided instance of <typeparamref name="T"/>
/// </summary>
/// <param name="item">An instance of <typeparamref name="T"/> to be delleted</param>
/// <returns></returns>
void Delete(T item);
}
}
iStar.Framework.IModelRepo
has an interface for Company
public interface ICompanyRepo : IModelRepo<Company> { }
iStar.Framework.Application
has repository class for Company
using iStar.Framework.Application.Data;
using iStar.Framework.ModelIRepos;
using iStar.Framework.Models;
using Microsoft.EntityFrameworkCore;
namespace iStar.Framework.Application.Repositories
{
public class CompanyRepo : ICompanyRepo
{
private readonly IDbContextFactory<FrameworkDbContext> _contextFactory;
public CompanyRepo(IDbContextFactory<FrameworkDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public void Add(Company item)
{
throw new NotImplementedException();
}
public async Task AddAsync(Company item)
{
using var context = _contextFactory.CreateDbContext();
context.Companies.Add(item);
await context.SaveChangesAsync();
}
public void Delete(int id)
{
throw new NotImplementedException();
}
public void Delete(Company item)
{
throw new NotImplementedException();
}
public Task DeleteAsync(int id)
{
throw new NotImplementedException();
}
public Task DeleteAsync(Company item)
{
throw new NotImplementedException();
}
public List<Company>? Get(bool all = true, bool ActiveOnly = false)
{
throw new NotImplementedException();
}
public Task<List<Company>?> GetActiveOnly()
{
throw new NotImplementedException();
}
public async Task<List<Company>?> GetAllAsync()
{
using var context = _contextFactory.CreateDbContext();
var lst = await context.Companies.ToListAsync();
return lst;
}
public List<Company>? GetAllByName(string name)
{
throw new NotImplementedException();
}
public Task<List<Company>?> GetAllByNameAsync(string name)
{
throw new NotImplementedException();
}
public Company GetOne(int id)
{
throw new NotImplementedException();
}
public async Task<Company>? GetOneAsync(int id)
{
using var context = _contextFactory.CreateDbContext();
var cpy = await context.Companies
.Where(c => c.Id == id)
.FirstOrDefaultAsync();
if (cpy is not null) return cpy; else return null;
}
public Task UpdateAsync(Company item)
{
throw new NotImplementedException();
}
public void Updatec(Company item)
{
throw new NotImplementedException();
}
}
}
Another Blazor Server app in the same solution uses all above details in this way.
Here is the program.css
class code.
using iStar.Framework.Application.Data;
using iStar.Framework.Application.Repositories;
using iStar.Framework.Application.Repositories.Identity;
using iStar.Framework.ModelIRepos;
using iStar.Framework.Models.Identity;
.
.
var builder = WebApplication.CreateBuilder(args);
.
.
builder.Services.AddScoped<ICompanyRepo, CompanyRepo>();
.
.
builder.Services.AddDbContextFactory<FrameworkDbContext>(options =>
{
//options.UseLazyLoadingProxies();
options.UseSqlServer(connectionString);
});
.
.
Below is CompanyList.razor
code.
@* @page "/companies/list" *@
@page "/cmpnylist"
@using Microsoft.EntityFrameworkCore
@inject CompanyRepo _companyRepo
<div class="container-fluid bg-light py-2">
<button class="btn btn-primary" @onclick="ShowAddCompanyModal">Add Company</button>
</div>
<h3>Company List</h3>
@if (companies == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table table-hover">
<thead>
<tr>
<th @onclick="() => SortCompanies(nameof(Company.Name))">Name</th>
<th @onclick="() => SortCompanies(nameof(Company.NavBarLogoUrl))">NavBar Logo URL</th>
<th @onclick="() => SortCompanies(nameof(Company.UseNavBarLogoUrl))">Use NavBar Logo URL</th>
</tr>
</thead>
<tbody>
@foreach (var company in companies)
{
<tr>
<td>@company.Name</td>
<td>@company.NavBarLogoUrl</td>
<td>@company.UseNavBarLogoUrl</td>
</tr>
}
</tbody>
</table>
}
<div class="container-fluid bg-light py-2">
<button class="btn btn-primary" @onclick="ShowAddCompanyModal">Add Company</button>
</div>
@if (showModal)
{
<div class="modal fade show d-block" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add New Company</h5>
<button type="button" class="btn-close" @onclick="HideAddCompanyModal"></button>
</div>
<div class="modal-body">
<EditForm Model="newCompany" OnValidSubmit="AddCompany">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<InputText id="name" class="form-control" @bind-Value="newCompany.Name" />
</div>
<div class="mb-3">
<label for="navBarLogoUrl" class="form-label">Nav Bar Logo URL</label>
<InputText id="navBarLogoUrl" class="form-control" @bind-Value="newCompany.NavBarLogoUrl" />
</div>
<div class="form-check">
<InputCheckbox id="useNavBarLogoUrl" class="form-check-input" @bind-Value="newCompany.UseNavBarLogoUrl" />
<label for="useNavBarLogoUrl" class="form-check-label">Use Nav Bar Logo URL</label>
</div>
<button type="submit" class="btn btn-success mt-3">Save</button>
</EditForm>
</div>
</div>
</div>
</div>
}
@code {
private List<Company>? companies;
private bool ascending = true;
private string currentSortColumn;
private bool showModal = false;
private Company newCompany = new Company();
protected override async Task OnInitializedAsync()
{
companies = await _companyRepo.GetAllAsync();
}
private void SortCompanies(string columnName)
{
if (currentSortColumn == columnName)
{
ascending = !ascending;
}
else
{
currentSortColumn = columnName;
ascending = true;
}
companies = ascending
? companies!.OrderBy(c => EF.Property<object>(c, columnName)).ToList()
: companies!.OrderByDescending(c => EF.Property<object>(c, columnName)).ToList();
}
private void ShowAddCompanyModal()
{
newCompany = new Company();
showModal = true;
}
private void HideAddCompanyModal()
{
showModal = false;
}
private async Task AddCompany()
{
await _companyRepo.AddAsync(newCompany);
companies = await _companyRepo.GetAllAsync();
showModal = false;
}
}
Below is _imports.razor
code
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using TutorsOnlinePortal
@using TutorsOnlinePortal.Client
@using TutorsOnlinePortal.Components
@using iStar.Framework.Models.Identity
@using iStar.Framework.Application.Repositories
@using iStar.Framework.Models
When I run this app, everything goes fine until I click the clink that loads CompanyList.razor
page. It shows the following error.
InvalidOperationException: Cannot provide a value for property '_companyRepo' on type 'TutorsOnlinePortal.Components.Framework.Company.CompanyList'. There is no registered service of type 'iStar.Framework.Application.Repositories.CompanyRepo'.
I have checked all the paths, refferences etc. I have cleaned and rebuilt solution many times but the error does not go away. Remember that app logs in using Microsoft Identity Core infratscture.
Make sure you have register CompanyRepo
and ICompanyRepo
like below in your Program.cs
file.
...
builder.Services.AddDbContextFactory<FrameworkDbContext>(options =>
{
options.UseSqlServer(connectionString);
});
// Make sure you have this line
builder.Services.AddScoped<ICompanyRepo, CompanyRepo>();
var app = builder.Build();
...
And also find the CompanyList.razor
file, modify this line
@inject CompanyRepo _companyRepo
to
@inject ICompanyRepo _companyRepo