Перебрасывание двух или более вызовов DBContext на одну и ту же сущность не удается - PullRequest
0 голосов
/ 26 марта 2019

У меня есть приложение MVC ASP.Net, использующее Entity Framework v6.0 с таблицей Сотрудника.

Мы используем подход Code First со стандартным методом Create (CRUD), который имеет EF-поиск для существующих сотрудников, а также EF-поиск для сотрудников CreatedBy / ModifiedBy. Попытка создать макеты для обоих (заглушки объектов EF) не удалась.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "EmployeeID,TeamID,EmployeeADID,FirstName,MiddleName,LastName,EmailAddress,IsAdministrator,IsDeleted")] Employee employee)
{
    if (ModelState.IsValid)
    {
        //Only unique employee IDs
        var existingEmployee = db.Employees.FirstOrDefault(o => o.EmployeeADID == employee.EmployeeADID);
        if(existingEmployee != null)
        {
            ViewBag.Error = "Employee ID must be unique, this employee (" + existingEmployee.FullName + ") already exists in the system.";
            ViewBag.TeamID = new SelectList(db.Teams, "TeamID", "Name", employee.TeamID);
            return View(existingEmployee);
        }

        SetAuditFields(employee);
        db.Employees.Add(employee);
        db.SaveChanges();
        return RedirectToAction("Index");
    }    
    ViewBag.TeamID = new SelectList(db.Teams, "TeamID", "Name", employee.TeamID);
    return View(employee);
}

Проблема в вызове SetAuditFields, и мне нужно смоделировать db.Employees.AsNoTracking AsNoTracking для операции редактирования.

private void SetAuditFields(Employee employee, bool onlyModified = false)
{
    char sep = '\\';
    string pID = User.Identity.Name.Split(sep)[1].ToUpper();
    var users = db.Employees.AsNoTracking().Where(c => c.EmployeeADID == pID || c.EmployeeID == employee.EmployeeID).ToList();
    var currentUser = users.FirstOrDefault(u => u.EmployeeADID == pID);
    if (onlyModified)
    {
        //Notice the AsNoTracking, when you set the db.Entry(object).State = EntityState.Modified; this query wont return anything as its in Modified Mode.
        //var originalEmployee = db.Employees.AsNoTracking().FirstOrDefault(o => o.EmployeeID == employee.EmployeeID);
        var originalEmployee = users.FirstOrDefault(o => o.EmployeeID == employee.EmployeeID);
        employee.CreatedByID = originalEmployee.CreatedByID;
        employee.CreatedDate = originalEmployee.CreatedDate;
        employee.ModifiedByID = currentUser.EmployeeID;
        employee.ModifiedDate = DateTime.Now;
    }
    else
    {
        employee.CreatedByID = currentUser.EmployeeID;
        employee.CreatedDate = DateTime.Now;
        employee.ModifiedByID = currentUser.EmployeeID;
        employee.ModifiedDate = DateTime.Now;
    }
}

Так, как мне издеваться db.Employees.AsNoTracking после первоначального издевательства db.Employees?

КОММЕНТАРИИ ЭТИХ ДВУХ ЛИНИЙ в приведенном ниже коде не работают и дают сбой:

"Элемент 'IQueryable.Provider' не реализован для типа 'DbSet 1Proxy' which inherits from 'DbSet 1'

Я также попробовал mockContext.SetupSequence, но мне нужно поменять местами с включенным и выключенным AsNoTracking. Наверняка, должно быть, я что-то упускаю?

[TestMethod()]
public void Create_Submit_Test()
{
    // Arrange
    var employeeDoesntExist = new Employee { EmployeeID = 0, FirstName = "DoesntExist" };
    var employeeAdmin = new Employee { EmployeeID=140, FirstName = "Bern", MiddleName = "", LastName = "O", EmployeeADID = "123", EmailAddress = "Bernard.O@a.com", TeamID = 1, IsDeleted = false, IsAdministrator = true };
    var employeeNew = new Employee { FirstName = "Jez", MiddleName = "", LastName = "T", EmployeeADID = "321", EmailAddress = "Jeremy.Thompson@a.com", TeamID = 1, IsDeleted = false, IsAdministrator = true };

    var mockContext = new Mock<ICTEntities>();
    var employeeEmptyMock = base.GetQueryableMockDbSet(employeeDoesntExist);
    var employeeAdminMock = base.GetQueryableMockDbSet(employeeAdmin);

    //THESE TWO LINES COMMENTED OUT
    //mockContext.Setup(m => m.Employees).Returns(employeeEmptyMock);
    //mockContext.Setup(m => m.Employees.AsNoTracking()).Returns(employeeAdminMock);

    mockContext.SetupSequence(x => x.Employees.AsNoTracking())
    .Returns(employeeEmptyMock)
    .Returns(employeeAdminMock);

    //I dont want to save it to the Database, otherwise next time we run this the object will already exist, so I mock the call
    mockContext.Setup(d => d.SaveChanges()).Returns(1);

    var controller = new EmployeesController(mockContext.Object);
    controller.ControllerContext = base.MockAccess().Object;

    // Act
    RedirectToRouteResult result = controller.Create(employeeNew) as RedirectToRouteResult;

    // Assert
    Assert.IsNotNull(result);
    Assert.AreEqual("Index", result.RouteValues["Action"]); 
}

Вот метод GetQueryableMockDbSet:

protected DbSet<T> GetQueryableMockDbSet<T>(params T[] sourceList) where T : class
{
    var queryable = sourceList.AsQueryable();

    var dbSet = new Mock<DbSet<T>>();
    dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
    dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
    dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
    dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());

    return dbSet.Object;
}

1 Ответ

0 голосов
/ 01 апреля 2019

Я закончил тем, что создал цепочку издевательств, используя счетчик согласно https://stackoverflow.com/a/14368486/495455

int callCounter = 1;
mockContext.Setup(m => m.Employees)
    .Returns(() =>
    {
        if (callCounter == 1)
        {
            callCounter++;
            return employeeToEditMockCU;
        }
        else
        {
            return employeeMockCU;
        }
    });

Насмешка с использованием SetupSequence не работает для меня после первой насмешки. Db.Employee становится нулевым после первого вызова. Поэтому я не использую SetupSequence:

mockContext.SetupSequence(x => x.Employees)
.Returns(employeeToEditMockCU)
.Returns(employeeMockCU);

Кроме того, чтобы обойти AsNoTracking() я закончил тем, что выбрал запись для обновления и сохранил ее без использования EntityState.Modified:

Обновление EF с использованием EntityState.Modified
Как обновить запись с помощью Entity Framework 6?

...