如何订阅依赖注入服务事件

时间:2020-08-06 23:47:00

标签: c# asp.net-core dependency-injection entity-framework-core

我正在使用实体Framework Core在asp.net Core应用程序中创建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 :(得分:0)

一种实现此目的的方法是为事件发布/侦听创建服务,而不是使用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);
        }
    }
}

这只是注入所有侦听器,然后提供一种向其侦听器发布消息的方法。

然后您将发布以下事件:

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

哪个给出结果:

enter image description here

答案 1 :(得分:0)

我使用SQL触发器解决了这个问题

使用Nuget的EntityFrameworkCore.Triggers

在我的模型中(角色示例):

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);
    }
相关问题