#c# #asp.net-core #dependency-injection #blazor #repository-pattern
#c# #asp.net-core #внедрение зависимостей #blazor #репозиторий-шаблон
Вопрос:
У меня возникли проблемы при попытке применить внедрение зависимостей. После долгих исследований и просмотра различных видеороликов на YouTube и ответов на вопросы о переполнении стека мой ITaskRepository продолжает возвращать значение null вместо того, чтобы быть экземпляром моего репозитория. Глядя на мой код, кажется, что я добавил все правильные вещи, чтобы заставить внедрение зависимостей работать.
Мой базовый интерфейс репозитория
using portfolio_backend.Data.Base;
using System.Collections.Generic;
namespace portfolio_backend.Business.Repositories.Base
{
public interface IBaseRepository<TEntity> where TEntity : BaseModel
{
void Add(TEntity model);
void Delete(TEntity model);
bool Exists(int Id);
TEntity Get(int Id);
IEnumerable<TEntity> GetAll();
void Update(int Id, TEntity model);
}
}
Мой класс BaseRepository
using Microsoft.EntityFrameworkCore.Internal;
using portfolio_backend.Data;
using portfolio_backend.Data.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace portfolio_backend.Business.Repositories.Base
{
public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : BaseModel
{
protected PortfolioContext _context;
public BaseRepository(PortfolioContext context)
{
_context = context;
}
public void Add(TEntity model)
{
if (!Exists(model.Id))
{
_context.Set<TEntity>().Add(model);
_context.SaveChanges();
}
}
public void Delete(TEntity model)
{
if (Exists(model.Id))
{
_context.Set<TEntity>().Remove(model);
_context.SaveChanges();
}
}
public bool Exists(int Id)
{
return _context.Set<TEntity>().Any(model => model.Id == Id);
}
public TEntity Get(int Id)
{
return _context.Set<TEntity>().FirstOrDefault(model => model.Id == Id);
}
public IEnumerable<TEntity> GetAll()
{
return _context.Set<TEntity>().ToList();
}
public void Update(int Id, TEntity model)
{
var modelToFind = Get(Id);
_context.Set<TEntity>().Update(modelToFind);
_context.SaveChanges();
}
}
}
Мой интерфейс ITaskRepository
using portfolio_backend.Business.Repositories.Base;
using portfolio_backend.Data;
using System.Collections.Generic;
namespace portfolio_backend.Business.Repositories
{
public interface ITaskRepository : IBaseRepository<Task>
{
IEnumerable<Task> GetTaskByProjects(int ProjectId);
}
}
Реализация TaskRepository
using portfolio_backend.Business.Repositories.Base;
using portfolio_backend.Data;
using System.Collections.Generic;
using System.Linq;
namespace portfolio_backend.Business.Repositories
{
public class TaskRepository : BaseRepository<Task>, ITaskRepository
{
public TaskRepository(PortfolioContext context) : base(context)
{
}
public IEnumerable<Task> GetTaskByProjects(int ProjectId)
{
return _context.Tasks.OrderByDescending(task => task.Project.Id == ProjectId).ToList();
}
}
}
My Startup class:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using portfolio_backend.Business.Repositories.Base;
using portfolio_backend.Business.Repositories;
using portfolio_backend.Data;
using Blazorise;
using Blazorise.Bootstrap;
using Blazorise.Icons.FontAwesome;
namespace portfolio_backend.Presentation
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PortfolioContext>(options =>
options.UseMySQL(Configuration.GetConnectionString("portfolio")));
services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>));
services.AddScoped<ITaskRepository, TaskRepository>();
services.AddBlazorise(options =>{
options.ChangeTextOnKeyPress = true;})
.AddBootstrapProviders()
.AddFontAwesomeIcons();
services.AddRazorPages();
services.AddServerSideBlazor();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.ApplicationServices
.UseBootstrapProviders()
.UseFontAwesomeIcons();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
I am trying to apply dependency injection for the following two classes:
Tasks.razor.cs ( a code-behind for a Blazor component)
using portfolio_backend.Business;
using portfolio_backend.Business.Repositories;
namespace portfolio_backend.Presentation.Pages
{
public partial class Tasks
{
private ITaskRepository _taskRepository;
private TaskViewModel _taskViewModel => new TaskViewModel(_taskRepository);
protected override void OnInitialized()
{
_taskViewModel.SeedTasks();
}
}
}
and the view model for this component
using portfolio_backend.Business.Repositories;
using portfolio_backend.Data;
using System.Collections.Generic;
namespace portfolio_backend.Business
{
public class TaskViewModel
{
private ITaskRepository _taskRepository {get; set;}
private List<Task> _allTasks;
public TaskViewModel(ITaskRepository repository)
{
_taskRepository = repository;
}
public List<Task> AllTasks
{
get => _allTasks;
set => _taskRepository.GetAll();
}
public void SeedTasks()
{
_taskRepository.Add( new Task { Description = "Task 1"} );
_taskRepository.Add(new Task { Description = "Task 2" });
_taskRepository.Add(new Task { Description = "Task 3" });
}
}
}
_taskRepository always returns null, and this is the error message that appears:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
what can I do to solve this? or how can I apply DI in a better way under these circumstances?
UPDATE:
Я внес следующие изменения на основе одного из предложенных решений в комментариях:
using portfolio_backend.Business;
using portfolio_backend.Business.Repositories;
namespace portfolio_backend.Presentation.Pages
{
public partial class Tasks
{
private ITaskRepository _taskRepository;
private TaskViewModel _taskViewModel;
public Tasks(ITaskRepository repository)
{
_taskRepository = repository;
_taskViewModel = new TaskViewModel(_taskRepository);
}
protected override void OnInitialized()
{
_taskViewModel.SeedTasks();
}
}
}
Это вызовет следующую ошибку:
MissingMethodException: No parameterless constructor defined for type 'portfolio_backend.Presentation.Pages.Tasks'.
Как подсказала ошибка, я добавил дополнительный конструктор без параметров
using portfolio_backend.Business;
using portfolio_backend.Business.Repositories;
namespace portfolio_backend.Presentation.Pages
{
public partial class Tasks
{
private ITaskRepository _taskRepository;
private TaskViewModel _taskViewModel;
public Tasks()
{
}
public Tasks(ITaskRepository repository)
{
_taskRepository = repository;
_taskViewModel = new TaskViewModel(_taskRepository);
}
protected override void OnInitialized()
{
_taskViewModel.SeedTasks();
}
}
}
Приведенное выше изменение создало ту же проблему, когда taskRepository был равен null.
Комментарии:
1. да, я только что попробовал, и _taskRepository все равно вернет null
2. Я также добавил сообщение об ошибке, которое появляется при отладке моего кода.
3. Вы не изменили задачи, чтобы использовать внедрение конструктора
Ответ №1:
_taskRepository
должно быть либо свойством, либо параметром конструктора. Оно у вас есть как член класса. Это не может быть введено таким образом.
Комментарии:
1. Я не слишком уверен, что понимаю. Я добавил значение get по умолчанию в _taskRepository, чтобы сделать его свойством, но оно все равно возвращало нулевое значение. Я все еще новичок во всем этом. Не могли бы вы привести пример того, как это можно сделать правильно?
2. Можете ли вы показать код, который вы добавили? Кроме того, я не уверен, что внедрение свойств вообще поддерживается в ASP.Net , особенно по умолчанию. Попробуйте использовать инъекцию конструктора.
3. это все равно не сработало, я опубликовал обновление, используя предложенное вами решение.
Ответ №2:
Вы должны зарегистрировать свою зависимость в ConfigureServices (IServiceCollection services) вашего загрузочного файла :
services.AddScoped<ITaskRepository, TaskRepository>();
or
services.AddTransient<ITaskRepository, TaskRepository>();
Вы должны решить, что лучше подходит для вашего приложения.
Ответ №3:
После изменений, которые вы внесли в свой первоначальный вопрос, кажется, что вы используете механизмы внедрения зависимостей в классе, который не зарегистрирован: Задачи. Как реализован этот класс?
Если вы хотите использовать DI для определенного класса, вы должны зарегистрировать его, как вы сделали, например, с ITaskRepository.
Добавьте следующую строку в свой метод ConfigureServices () :
services.AddScoped<Tasks>();
Комментарии:
1. Этот класс на самом деле является кодом за страницей razor. Это не было реализовано каким-либо конкретным образом, но я обязательно попробую.
Ответ №4:
В этом сценарии было две основные проблемы.
Во-первых, я неправильно спроектировал свое приложение. Я имею в виду, что изначально я намеревался использовать внедрение зависимостей в экземпляре моего репозитория, чтобы иметь возможность создавать экземпляр моей TaskViewModel в моем коде, например, так.
public Tasks(ITaskRepository repository)
{
_taskRepository = repository;
_taskViewModel = new TaskViewModel(_taskRepository);
}
Лучшим способом сделать это, который был частью моего решения, было также создать интерфейс для моей TaskViewModel, чтобы я мог использовать внедрение зависимостей в коде моего компонента Blazor. Сама TaskViewModel должна была иметь экземпляр моего репозитория посредством внедрения зависимостей.
ITaskViewModel:
public interface ITaskViewModel : IBaseViewModel<Task>
{
List<Task> AllTasks { get; set; }
void SeedTasks();
}
Моя реализация для TaskViewModel
public class TaskViewModel : BaseViewModel<Task>, ITaskViewModel
{
private ITaskRepository _taskRepository;
private List<Task> _allTasks;
public TaskViewModel(ITaskRepository repository) : base(repository)
{
_taskRepository = repository;
}
public List<Task> AllTasks
{
get => _allTasks;
set
{
_allTasks = value;
}
}
public void SeedTasks()
{
var task1 = new Task { Description = "Task 1" };
var task2 = new Task { Description = "Task 2" };
var task3 = new Task { Description = "Task 3" };
_taskRepository.Add(task1);
_taskRepository.Add(task2);
_taskRepository.Add(task3);
}
}
Регистрация компонента в методе ConfigureServices файла Startup.cs
services.AddScoped<ITaskViewModel, TaskViewModel>();
Вторая проблема заключалась в том, что я не мог использовать конструктор или подход к свойствам членов для использования внедрения зависимостей в коде компонента Blazor. Не уверен, что то же самое относится и к исходному коду страницы razor, но способ использования dependency inject для кода компонента Blazor заключается в использовании атрибута Inject
Tasks.razor.cs
[Inject]
private ITaskViewModel _viewModel { get; set; }
Убедитесь, что у вас также установлен следующий пакет Nuget для работы атрибута Inject.
using Microsoft.AspNetCore.Components;