如何实现在另一个控制器中调用另一个动作的控制器动作?

时间:2019-01-21 22:59:08

标签: c# asp.net-web-api asp.net-core repository-pattern

我正在使用ASP.NET核心创建Web API,并对如何将其发布到审核/日志表感到困惑。

有两个表,一个事件表和一个事件日志表。如果用户创建一个新事件,则它将在事件和事件日志表中都添加一个条目。如果用户编辑事件的名称/描述,它还将在事件日志表中创建一个新条目。请注意,事件日志表的结构与事件表大部分相同。

事件表

+------------------+
|   Event Table    |
+------------------+
| Id               |
| EventName        |
| EventDescription |
| CreatorId        |
+------------------+

事件日志表

+------------------+
| Event Log Table  |
+------------------+
| Id               |
| EventId          |
| EventName        |
| EventDescription |
| EditorId         |
| TimeEdited       |
+------------------+

当前,我的Web API中针对两个表中的每个表都有两个单独的控制器,即事件控制器和事件日志控制器

当前发生的情况:当有事件的发布/投放时,它将创建事件日志控制器的实例,并调用post方法创建事件日志,并传入新的创建/更新的事件日志对象。

这里有一些简化的代码...

在EventController.cs中。

public IActionResult PostEvent([FromBody] Event event)
{
    _context.Event.Add(event);
    _context.Event.SaveChanges();

    EventLogController logController = new EventLogController(context);
    logController.PostEventLog(event);

    return Ok();
}

在EventLogController.cs中...

public IActionResult PostEventLog([FromBody] Event event)
{
    var eventLog = _mapper.Map<EventLog>(event);
    eventLog.TimeEdited = DateTime.Now;

    _context.EventLog.Add(eventLog);
    _context.EventLog.SaveChanges();

    return Ok();
}

虽然目前效果很好,但我非常担心这是否是最佳实践(创建一个仅调用一个方法的新控制器似乎很糟糕)。可能需要注意的是,我不想向每个控制器发送两个单独的API调用。我希望由于添加/更新条目而创建事件日志。

据我所知,我可以采取其他一些方法...

  1. 创建存储库/服务?用于将EventLog添加到数据库并将其注入EventController。虽然这样做确实可以避免创建控制器,但听说在ASP.NET Core中创建存储库是不必要的抽象。

  2. 在EventController的PostEvent操作中添加事件日志条目,而无需创建控制器或存储库/服务。但是,这意味着该操作方法将处理两个请求。

  3. 继续使用当前方法,只是注入EventLog控制器而不创建新实例。

至于我认为我做不到的事情...

我认为我无法在数据库本身内创建触发器,因为创建事件条目所包含的信息不足以创建事件日志(因为数据库不会意识到编辑器)。

我也不认为我可以使用RedirectToAction,因为它是一个后发请求,需要在体内发送一些东西。

所以我的问题是:在相关实体的每次创建/更新上创建日志/审核条目的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

我希望创建一个单独的类,您实际上在“其他方法1”中提到过。然后您可以从多个地方调用它。

通常,如您所知,许多方法都是可行的。经验法则应该始终是:“控制器只能控制,它本身不能完成工作。”这意味着控制器应该只知道调用什么才能完成工作。如果遵循此原则,则可以将实际代码放在单独的类中,然后可以根据需要从一个或多个控制器的多个位置调用它。

答案 1 :(得分:0)

一个单独的班级将成为您的朋友。当然,如果实现非常简单并且应用程序不是很大,那么创建最简单的解决方案就没有错。无论如何,调用另一个控制器将耦合您的代码。

我做了一个简单的例子,通常我喜欢类似的情况。

EventService.cs

public class EventService
{
        private IEventLogger logger;

        public EventService(IEventLogger logger)
        {
            this.logger = logger;
        }

        internal void AddNew(Event @event)
        {

            // Here is your code to persist event.
            // For example, context.SaveChanges(@event)
            // or whatever you want. 

            // When event is saved, I have called object that
            // implements IEventLogger interface. It is just a simple
            // interface to introduce Log(Event @event) -method.
            // 
            // When this service is created, you can pass logger
            // implementation. You can also use different way to pass
            // right implementation if constructor isn't work for you.
            if(logger != null) { 
                this.logger.Log(@event);
            }
        }
}

EventController

# In your controller you can use service like this.
# Of course, service is not needed to create inside the action
# but I just wanted to make a simple example.

public IActionResult PostEvent([FromBody] Event @event)
{
    IEventLogger logger = new EventLogger();
    EventService eventService = new EventService(logger);

    eventService.AddNew(@event); 

    return Ok();
} 

为什么要使用服务?

它不应是知道实现细节的控制器。如前所述,控制器只是控制。

在这里,我介绍了一项负责处理持久事件的服务。您可以使用context初始化服务。我将示例保持简单,不再赘述,但您可能会明白。

当调用方法AddNew(event)时,服务将了解您的业务规则。对于您而言,我认为“日志记录”是您需要的规则之一。您希望保留更改日志,以便在使用服务时始终可以使用记录器。

IEventLogger是一个简单的界面。实现它时,可以在其中使用相同的上下文,然后在实现内部处理日志。

if(logger != null)不是强制性的,但通常是一种不错的选择,可以在不需要时选择不使用日志记录。

如果需要,您始终可以将EventLogController分隔开来,这些返回的记录数据可以从API返回使用。您的EventLogController具有方法PostEventLog。现在,如果需要,您可以在控制器操作中使用相同的记录器来保留日志事件。