-1
votes

L'injection de dépendances ne fonctionne pas correctement pour mon application ASP.NET Core MVVM Blazor

J'ai du mal à appliquer l'injection de dépendance. Après de nombreuses recherches et en regardant diverses vidéos sur YouTube et des réponses sur le débordement de Stack, mon ITaskRepository continue de renvoyer un null au lieu d'être une instance de mon référentiel. En regardant mon code, il semble que j'ai ajouté toutes les bonnes choses pour faire fonctionner l'injection de dépendances.

Interface de mon référentiel de base

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();
        }

    }
}

Ma classe BaseRepository

MissingMethodException: No parameterless constructor defined for type 'portfolio_backend.Presentation.Pages.Tasks'.

Mon interface ITaskRepository

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();
        }

    }
}

Implémentation de TaskRepository

System.NullReferenceException: 'Object reference not set to an instance of an object.'

Ma classe de démarrage:

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" });
        }

    }
}

J'essaie d'appliquer l'injection de dépendances pour les deux classes suivantes:

Tasks.razor.cs (un code-behind pour un composant Blazor)

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();
        }

    }
}

et le modèle de vue de ce composant

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");
            });
        }
    }
}

_taskRepository renvoie toujours null, et voici le message d'erreur qui apparaît:

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();
        }
    }
}

que puis-je faire pour résoudre ce problème? ou comment puis-je mieux appliquer la DI dans ces circonstances?

MISE À JOUR:

J'ai apporté les modifications suivantes en fonction de l'une des solutions suggérées dans les commentaires:

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);
    }
}

Cela déclenchera l'erreur suivante:

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();
        }
    }
}

Comme l'erreur l'a suggéré, j'ai ajouté un constructeur supplémentaire sans paramètre

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);
    }
}

La modification ci-dessus a créé le même problème avec le taskRepository étant nul.


3 commentaires

oui je viens de l'essayer, et le _taskRepository retournera toujours null


J'ai également ajouté le message d'erreur qui apparaît lorsque je débogue mon code.


Vous n'avez pas modifié les tâches pour utiliser l'injection de constructeur


4 Réponses :


0
votes

_taskRepository doit être une propriété ou un paramètre de constructeur. Vous l'avez en tant que membre de la classe. Ça ne peut pas être injecté comme ça.


4 commentaires

Je ne suis pas trop sûr de comprendre. J'ai ajouté un paramètre par défaut au _taskRepository pour en faire une propriété, mais il a quand même renvoyé une valeur nulle. Je suis encore nouveau dans tout cela. Pourriez-vous donner un exemple de la façon dont cela pourrait être fait correctement?


Pouvez-vous montrer le code que vous avez ajouté? De plus, je ne suis pas sûr que l'injection de propriété soit même prise en charge dans ASP.Net, en particulier par défaut. Essayez d'utiliser l'injection de constructeur.


J'ai ajouté le code, c'est dans ma classe TaskViewModel


cela ne fonctionnait toujours pas, j'ai publié une mise à jour en utilisant votre solution suggérée.



0
votes

Vous devez enregistrer votre dépendance dans ConfigureServices (services IServiceCollection) de votre fichier de démarrage:

services.AddScoped<ITaskRepository, TaskRepository>();

or 

services.AddTransient<ITaskRepository, TaskRepository>();

Vous devez décider de ce qui convient le mieux à votre application.


0 commentaires

0
votes

Suite aux modifications que vous avez apportées à votre question initiale, il semble que vous utilisez des mécanismes d'injection de dépendances sur une classe qui n'est pas enregistrée: les tâches . Comment cette classe est-elle implémentée?

Si vous souhaitez utiliser DI sur une classe spécifique, vous devez l'enregistrer comme vous l'avez fait avec ITaskRepository par exemple.

Ajoutez la ligne suivante à votre méthode ConfigureServices ():

        services.AddScoped<Tasks>();


1 commentaires

Cette classe est en fait un code derrière une page de rasoir. Il n'a pas été mis en œuvre d'une manière particulière, mais je vais certainement l'essayer.



0
votes

Il y avait deux défis principaux dans ce scénario.

Le premier était que je ne concevais pas correctement mon application. Ce que je veux dire par là, c'est que j'avais initialement l'intention d'utiliser l'injection de dépendances sur une instance de mon référentiel pour pouvoir créer une instance de mon TaskViewModel dans mon code derrière comme ça.

using Microsoft.AspNetCore.Components;

Une meilleure façon de le faire qui faisait partie de ma solution était de créer également une interface pour mon TaskViewModel afin que je puisse utiliser l'injection de dépendances dans mon composant Blazor code-behind. Le TaskViewModel lui-même aurait dû avoir une instance de mon référentiel via l'injection de dépendances.

ITaskViewModel:

 [Inject]
 private ITaskViewModel _viewModel { get; set; }

Mon implémentation pour le TaskViewModel

services.AddScoped<ITaskViewModel, TaskViewModel>();

Inscription des composants sur la méthode ConfigureServices du fichier Startup.cs

    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);
        }

    }

Le deuxième problème était que je ne pouvais pas utiliser le constructeur ou l'approche de propriété de membre pour utiliser l'injection de dépendances dans un composant Blazor code-behind. Je ne sais pas si la même chose s'applique au code de page de rasoir derrière, mais la façon dont vous utilisez l'injection de dépendance pour un code de composant Blazor est à l'aide de l'attribut Inject

Tasks.razor.cs

    public interface ITaskViewModel : IBaseViewModel<Task>
    {
        List<Task> AllTasks { get; set; }

        void SeedTasks();
    }

Assurez-vous que le package Nuget suivant est également installé pour que l'attribut Inject fonctionne.

public Tasks(ITaskRepository repository)
{
  _taskRepository = repository;
 _taskViewModel = new TaskViewModel(_taskRepository);
}


0 commentaires