Как подписаться на сервисы внедрения зависимостей - PullRequest
0 голосов
/ 07 августа 2020

Я использую Entity Framework Core для создания сервисов crud в моем asp. net основном приложении. В моих сервисах crud есть события и обработчики, такие как OnCreated, OnUpdated.

Эти события должны иметь возможность вызывать и другие сервисы crud.

Как этого можно достичь?

Вот как я внедряю свои службы CRUD:

services.AddTransient<IRoleManager, RoleManager>();
        services.AddTransient<IOfficeManager, OfficeManager>();
        services.AddTransient<IProjectManager, ProjectManager>();

Вот пример моей CRUD-службы:

public class ProjectManager : IProjectManager
{
    protected AppDbContext Db;

    public ProjectManager(AppDbContext db)
    {
        Db = db;
    }

    public IQueryable<Project> GetProjects()
    {
        return Db.Projects;
    }

    public Project CreateProject (Project project)
    {
        Project Project = Db.Projects.Add(project).Entity;
        Db.SaveChanges();
        OnProjectCreated(Project);
        return Project;
    }

    public Project UpdateProject (Project project)
    {
        Project Project = Db.Projects.Update(project).Entity;
        Db.SaveChanges();
        OnProjectUpdated(Project);
        return Project;
    }

    public Project DeleteProject (Project project)
    {
        Db.Projects.Remove(project);
        Db.SaveChanges();
        OnProjectDeleted(project);
        return project;
    }

    public event EventHandler<ProjectManagerEventArgs> ProjectCreated;
    public event EventHandler<ProjectManagerEventArgs> ProjectUpdated;
    public event EventHandler<ProjectManagerEventArgs> ProjectDeleted;

    protected virtual void OnProjectCreated(Project project)
    {
        ProjectCreated?.Invoke(this, new ProjectManagerEventArgs(project));
    }

    protected virtual void OnProjectUpdated(Project project)
    {
        ProjectUpdated?.Invoke(this, new ProjectManagerEventArgs(project));
    }

    protected virtual void OnProjectDeleted(Project project)
    {
        ProjectDeleted?.Invoke(this, new ProjectManagerEventArgs(project));
    }
}

Я могу подписаться таким образом ...

services.AddTransient(sp =>
    {
        AppDbContext dependency = sp.GetService<AppDbContext>();
        ProjectManager target = new ProjectManager(dependency);
        target.ProjectCreated += new ProjectManagerListener().OnProjectCreated;
        return (IProjectManager)target;
    });

, но этот способ кажется очень небрежным и не позволяет моему обработчику / слушателю получить доступ к другим службам CRUD.

Как я могу использовать события с DI?

КОД ДЛЯ СЛУШАТЕЛЯ

public class ProjectManagerListener
{
    private readonly IClaimManager ClaimManager;
    private readonly IRoleManager RoleManager;

    public ProjectManagerListener(IClaimManager claimManager, IRoleManager roleManager)
    {
        ClaimManager = claimManager;
        RoleManager = roleManager;
    }

    public void OnProjectCreated(object source, ProjectManagerEventArgs ProjectEventArgs)
    {
        foreach (Role role in RoleManager.GetRoles())
        {
            ClaimManager.CreateClaim(new ProjectRoleClaim() { ProjectId = ProjectEventArgs.Project.Id, RoleId = role.Id });
        }
    }
}

КОД ДЛЯ ВПРЫСКА СЛУШАТЕЛЯ

services.AddTransient(sp =>
        {
            AppDbContext dependency = sp.GetService<AppDbContext>();
            IClaimManager ClaimManager = sp.GetService<IClaimManager>();
            IRoleManager RoleManager = sp.GetService<IRoleManager>();
            ProjectManager target = new ProjectManager(dependency);
            target.ProjectCreated += new ProjectManagerListener(ClaimManager, RoleManager).OnProjectCreated;
            return (IProjectManager)target;
        });

Ответы [ 2 ]

0 голосов
/ 08 августа 2020

Я решил это, используя SQL Триггеры:

используя EntityFrameworkCore.Triggers из Nuget

В моих моделях (пример роли):

static Role()
    {
        Triggers<Role>.Inserted += e =>
        {
            e.Context.Add(new RoleClaim() { RoleId = e.Entity.Id });
            IEnumerable<Office> offices = e.Context.Set<Office>();

            foreach(Office office in offices)
            {
                e.Context.Add(new OfficeRoleClaim() { Office = office, Role = e.Entity });
            }

            IEnumerable<Project> projects = e.Context.Set<Project>();

            foreach (Project project in projects)
            {
                e.Context.Add(new ProjectRoleClaim() { Project = project, Role = e.Entity });
            }

            e.Context.SaveChanges();
        };
    }

И в DbContext:

public override int SaveChanges()
    {
        return this.SaveChangesWithTriggers(base.SaveChanges, acceptAllChangesOnSuccess: true);
    }
0 голосов
/ 07 августа 2020

Один из способов сделать эту работу - создать службы для публикации / прослушивания событий вместо использования событий C#. Затем вы можете использовать DI с этими услугами. В его абсолютно грубой форме вам потребуются следующие элементы:

События:

public class ProjectCreatedEvent
{
    public int ProjectId { get; }

    public ProjectCreatedEvent(int projectId)
    {
        ProjectId = projectId;
    }
}

Вероятно, это будут неизменяемые POCO.

Слушатели:

public interface IListener
{
}

public interface IListener<T> : IListener
{
    void HandleMessage(T message);
}

public class ProjectCreatedEventListener : IListener<ProjectCreatedEvent>
{
    private readonly IClaimManager _claimManager;

    public ProjectCreatedEventListener(IClaimManager claimManager)
    {
        _claimManager = claimManager;
    }

    public void HandleMessage(ProjectCreatedEvent message)
    {
        _claimManager.CreateClaim(message.ProjectId);
    }
}

Поскольку они будут зарегистрированы в контейнере DI, они могут легко внедрить зависимости.

Диспетчер:

public interface IDispatcher
{
    void Publish<T>(T message);
}

public class Dispatcher : IDispatcher
{
    private readonly IEnumerable<IListener> _listeners;

    public Dispatcher(IEnumerable<IListener> listeners)
    {
        _listeners = listeners;
    }

    public void Publish<T>(T message)
    {
        foreach (var listener in _listeners.OfType<IListener<T>>())
        {
            listener.HandleMessage(message);
        }
    }
}

Это просто вводит всех слушателей и затем предоставляет способ опубликовать sh сообщение для его слушателей.

И вы затем публикуете sh такие события, как:

public class ProjectManager : IProjectManager
{
    private readonly IDispatcher _dispatcher;

    public ProjectManager(IDispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    }

    public void CreateProject(string name)
    {
        // Do the CRUD...
        Console.WriteLine($"Creating project '{name}'");
        _dispatcher.Publish(new ProjectCreatedEvent(43));
    }
}

И вот рабочий пример всего этого подключено к DI:

public static void Main(string[] args) 
{
    var serviceProvider = new ServiceCollection()
        .AddTransient<IProjectManager, ProjectManager>()
        .AddTransient<IClaimManager, ClaimManager>()
        .AddTransient<IListener<ProjectCreatedEvent>, ProjectCreatedEventListener>()
        .AddTransient<IListener>(sp => sp.GetService<IListener<ProjectCreatedEvent>>())
        .AddTransient<IDispatcher, Dispatcher>()
        .BuildServiceProvider();

    var service = serviceProvider.GetService<IProjectManager>();

    service.CreateProject("My project.");
}

Что дает результат:

введите описание изображения здесь

...