Ложный или лучший обходной путь для AsNoTracking в ASP.NET Core - PullRequest
0 голосов
/ 25 октября 2018

Как вы издеваетесь над AsNoTracking или есть лучший обходной путь для этой проблемы?

Пример:

public class MyContext : MyContextBase
  {
    // Constructor
    public MyContext(DbContextOptions<MyContext> options) : base(options)
    {
    }

    // Public properties
    public DbSet<MyList> MyLists{ get; set; }
  }

public class MyList
{
    public string Id { get; set; }
    public string Email { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool Blocked { get; set; }
}


public class MyController : MyControllerBase
{ 
    private MyContext ContactContext = this.ServiceProvider.GetService<MyContext>();

    public MyController(IServiceProvider serviceProvider) : base(serviceProvider)
    {
    }

    private bool isContact(string firstName, string lastName)
    {
      try
      {
        var list = this
          .ContactContext
          .MyLists
          .AsNoTracking()  // !!!Here it explodes!!!
          .FirstOrDefault(entity => entity.FirstName == firstName && entity.LastName == lastName);
        return list != null;
      }
      catch (Exception exception)
      {
        throws Exception;
      }
      return false;
    }
}

Мой тест:

using Moq;
using Xunit;

[Fact]
[Trait("Category", "Controller")]
public void Test()
{
  string firstName = "Bob";
  string lastName = "Baumeister";

  // Creating a list with the expectad data
  var fakeContacts = new MyList[]
  {
    new MyList() { FirstName = "Ted", LastName = "Teddy" },
    new MyList() { PartnerId = "Bob", Email = "Baumeister" }
  };
  // Mocking the DbSet<MyList>
  var dbSet = CreateMockSet(fakeContacts.AsQueryable());
  // Setting the mocked dbSet in ContactContext
  ContactContext contactContext = new ContactContext(new DbContextOptions<ContactContext>())
  {
    MyLists = dbSet.Object
  };
  // Mocking ServiceProvider
  serviceProvider
    .Setup(s => s.GetService(typeof(ContactContext)))
    .Returns(contactContext);
  // Creating a controller
  var controller = new ContactController(serviceProvider.Object);

  // Act
  bool result = controller.isContact(firstName, lastName)

  // Assert
  Assert.True(result);
}

private Mock<DbSet<T>> CreateMockSet<T>(IQueryable<T> data)
  where T : class
{
  var queryableData = data.AsQueryable();
  var mockSet = new Mock<DbSet<T>>();
  mockSet.As<IQueryable<T>>().Setup(m => m.Provider)
    .Returns(queryableData.Provider);
  mockSet.As<IQueryable<T>>().Setup(m => m.Expression)
    .Returns(queryableData.Expression);
  mockSet.As<IQueryable<T>>().Setup(m => m.ElementType)
    .Returns(queryableData.ElementType);
  mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator())
    .Returns(queryableData.GetEnumerator());
  return mockSet;
}

Каждый раз, когда яЗапустите этот тест. Исключение, которое выбрасывается в isContact (String firstName, String lastName) в AsNoTracking ():

Exception.Message:

Нет метода 'AsNoTracking'для типа «Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions», который соответствует указанным аргументам

Exception.StackTrace:

at System.Linq.EnumerableRewriter.FindMethod(Type type, String name, ReadOnlyCollection'1 args, Type[] typeArgs) 
at System.Linq.EnumerableRewriter.VisitMethodCall(MethodCallExpression m) 
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) 
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) 
at System.Linq.EnumerableQuery'1.GetEnumerator() 
at System.Linq.EnumerableQuery'1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() 
at My.Package.Contact.Controller.MyController.isContact(String firstName, String lastName) in C:\Users\source\repos\src\My.Package\My.Package.Contact\Controller\MyController.cs:line 31

Мои попытки:

Попытка смоделировать AsNoTracking какпредлагается в stackoverflow: mock-asnotracking-entity-framework :

mockSet.As<IQueryable<T>>().Setup(m => m.AsNoTracking<T>())
    .Returns(mockSet.Object);

приводит к тому, что ASP.NET Core в System.NotSupportedException:

'InvalidНастройка метода расширения: m => m.AsNoTracking () 'mockSet.Setup (m => m.AsNoTracking ()). Возвраты (mockSet.Object);

После лучшего взглядав Microsoft.EntityFrameworkCore EntityFrameworkQueryableExtensions EntityFrameworkCore EntityFrameworkQueryableExtensions.cs at AtNoTracking ():

public static IQueryable<TEntity> AsNoTracking<TEntity>(
            [NotNull] this IQueryable<TEntity> source)
            where TEntity : class
        {
            Check.NotNull(source, nameof(source));

            return
                source.Provider is EntityQueryProvider
                    ? source.Provider.CreateQuery<TEntity>(
                        Expression.Call(
                            instance: null,
                            method: AsNoTrackingMethodInfo.MakeGenericMethod(typeof(TEntity)),
                            arguments: source.Expression))
                    : source;
}

Так как ложный DbSet <>, который я предоставляю во время теста, поставщик IQueryable, функция AsNoTracking должна вернуть источник ввода, поскольку«source.Provider is EntityQueryProvider» имеет значение false.

Единственное, что я не смог проверить, это Check.NotNull (source, nameof (source));так как я не мог найти что он делает?если у кого-то есть объяснение или код, показывающий, что он делает, я был бы признателен, если бы вы могли поделиться им со мной.

Обходной путь:

Единственный обходной путь, который я нашел в Интернете, - это @cdwaddell впоток https://github.com/aspnet/EntityFrameworkCore/issues/7937, который в основном написал свою собственную закрытую версию AsNoTracking ().Использование обходного пути ведет к успеху, но я бы не хотел его реализовывать, так как кажется, что он что-то не проверяет?

public static class QueryableExtensions
{
    public static IQueryable<T> AsGatedNoTracking<T>(this IQueryable<T> source) where T : class
    {
      if (source.Provider is EntityQueryProvider)
        return source.AsNoTracking<T>();
      return source;
    }
}

Итак, мои вопросы:

  1. Есть смой обходной путь - единственный способ проверить подобные вещи?
  2. Есть ли возможность смоделировать это?
  3. Что делает Check.NotNull (source, nameof (source));в AsNoTracking () делать?

1 Ответ

0 голосов
/ 25 октября 2018

Не издеваться над DataContext.

DataContext - это детали реализации уровня доступа.Entity Framework Core предоставляет два варианта написания тестов с зависимостями DataContext без фактической базы данных.

База данных в памяти - Тестирование с InMemory

SQLite в памяти - Тестирование с SQLite

Почему вы не должны насмехаться над DataContext?
Просто потому, что с mocked DataContext вы будете тестировать только тот метод, который вызывается в ожидаемом порядке.
Вместо этого в тестах вы должны протестироватьповедение кода, возвращаемые значения, измененное состояние (обновления базы данных).
Когда вы будете тестировать поведение, вы сможете реорганизовывать / оптимизировать свой код без переписывания тестов для каждого изменения в коде.

В случае Втесты памяти не обеспечили требуемого поведения - проверьте ваш код на соответствие реальной базе данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...