如何为API端点方法动态选择DbContext

问题描述 投票:0回答:1

我开发了使用帮助器类的API,以获取每个端点函数的数据库上下文。现在,我正在尝试为每个端点编写单元测试,并且想在单元测试项目中使用内存数据库。

我遇到的问题是,为了调用API函数,我必须在API控制器类中添加一个构造函数。这将允许我将内存数据库的dbContext传递给控制器​​函数以供使用。但是,由于添加了构造器,因此在尝试命中端点时出现以下错误:

"exceptionMessage": "Unable to resolve service for type 'AppointmentAPI.Appt_Models.ApptSystemContext' while attempting to activate 'AppointmentAPI.Controllers.apptController'."

controller.cs

 public class apptController : Controller
    {
        private readonly ApptSystemContext DbContext;

        public apptController(ApptSystemContext dbContext)
        {
            if (dbContext == null)
            {
                // Use helper to get dbContext. Should be used when hitting endpoint
                DbContext = ConnectionHelper.getApptConnection();
            }
            else
            {
                // dbContext should be in-memory db. This context should only be used for unit tests
                DbContext = dbContext;
            }
        }


        #region assingAppt
        /*
         * assignAppt()
         *
         * Assigns newly created appointment to slot
         * based on slotId
         *
         */
        [Authorize]
        [HttpPost]
        [Route("/appt/assignAppt")]
        public string assignAppt([FromBody] dynamic apptData)
        {
            int id = apptData.SlotId;
            string json = apptData.ApptJson;
            DateTime timeStamp = DateTime.Now;

            using (DbContext)
            {
               var slot = DbContext.AppointmentSlots.Single(s => s.SlotId == id);

                // make sure there isn't already an appointment booked in appt slot
                if (slot.Timestamp == null)
                {
                    slot.ApptJson = json;
                    slot.Timestamp = timeStamp;

                    DbContext.SaveChanges();
                    return "Task Executed\n";
                }
                else
                {
                    return "There is already an appointment booked for this slot.\n" +
                           "If this slot needs changing try updating it instead of assigning it.";
                }
            }
        }
   }
c# .net entity-framework api database-connection
1个回答
0
投票

异常表明您尚未在Startup.cs中注册DBContext(如上所述)。我还建议您将私有只读属性的名称更改为DbContext以外的其他名称(这是类名称,可能会引起混淆)使用类似这样的内容:

private readonly ApptSystemContext _context;

此外,您的方法应该更改。

首先,在注册DBContext时将设置连接字符串。只是让依赖注入为您解决这个问题。您的控制器应如下所示:

        public apptController(ApptSystemContext dbContext)
        {
            _context = dbContext;
        }

如果您在启动中注册,则dbContext不会为null。

接下来,单元测试是一个棘手的概念,但是一旦您编写了单元测试,您就会开始对它有所了解。

[您已经说过要使用SQL In Memory db进行单元测试,这是一种很好的方法(请注意,SQL In Mem存在一些限制,就像没有FK约束一样)。接下来,假设您要测试Controller,因此,由于必须传递DBContext才能实例化Controller,因此可以创建一个配置为使用内存数据库的新DBContext实例。

例如

public void ApptControllerTest()
{
   //create new dbcontext
   DbContextOptions<ApptSystemContext> options;
        var builder = new DbContextOptionsBuilder<ApptSystemContext>();
        builder.UseInMemoryDatabase();
        options = builder.Options;

    var context = new ApptSystemContext(options);

   //instantiate your controller
   var controller = new appController(context);

   //call your method that you want to test
   var retVal = controller.assignAppt(args go here);
}

将方法的主体更改为此:

public string assignAppt([FromBody] dynamic apptData)
        {
            int id = apptData.SlotId;
            string json = apptData.ApptJson;
            DateTime timeStamp = DateTime.Now;

            using (_context)
            {
               var slot = _context.AppointmentSlots.Single(s => s.SlotId == id);

                // make sure there isn't already an appointment booked in appt slot
                if (slot.Timestamp == null)
                {
                    slot.ApptJson = json;
                    slot.Timestamp = timeStamp;

                    _context.SaveChanges();
                    return "Task Executed\n";
                }
                else
                {
                    return "There is already an appointment booked for this slot.\n" +
                           "If this slot needs changing try updating it instead of assigning it.";
                }
            }
        }

另一个建议,除非绝对被迫这样做,否则不要将动态对象用作请求的主体。使用动态对象可以传递任何内容,并且您将无法确定请求是否可以接受。

© www.soinside.com 2019 - 2024. All rights reserved.