2016-10-04 4 views
3

настоящее время я использую метод расширения для общего фиктивных DbSets в виде списка:Есть ли способ обобщить метод DbSet.Find с помощью Moq?

public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class 
    { 
     var queryable = sourceList.AsQueryable(); 
     var mockDbSet = new Mock<DbSet<T>>(); 
     mockDbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider); 
     mockDbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression); 
     mockDbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType); 
     mockDbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator()); 
     mockDbSet.Setup(x => x.Add(It.IsAny<T>())).Callback<T>(sourceList.Add); 
     mockDbSet.Setup(x => x.Remove(It.IsAny<T>())).Returns<T>(x => { if (sourceList.Remove(x)) return x; else return null; }); 

     return mockDbSet.Object; 
    } 

Однако, я не могу понять способ поиздеваться метод Find, который поиска на основе первичного ключа таблицы. Я мог бы сделать это на определенном уровне для каждой таблицы, потому что я могу проверить базу данных, получить PK, а затем просто высмеять метод Find для этого поля. Но тогда я не могу использовать общий метод.

Я полагаю, что я мог бы также добавить к частичным классам, которые автогенерирует EF, чтобы отметить, какое поле является PK с атрибутом или чем-то еще. Но у нас есть более 100 таблиц, и это затрудняет управление кодом, если вы полагаетесь на людей, чтобы их вручную поддерживать.

Предоставляет ли EF6 какой-либо способ поиска первичного ключа или он знает только динамически после его подключения к базе данных?

+2

вы ищете что-то вроде этого: [link] (http://stackoverflow.com/a/25199983/5048049), иначе вы могли бы лучше объяснить, что вы ищете? – peval27

+1

Это первая альтернатива, которую я описал. Это делает конкретный макет только для набора ActiveLoan, а не для общего макета для любого общего набора. –

ответ

4

Обдумав это некоторое время, я думаю, что нашел «лучшее» решение в настоящее время. У меня просто есть ряд операторов if, которые непосредственно проверяют тип в методе расширения. Затем я применил тип, который мне нужен, чтобы установить поведение find и вернуть его в общий, когда закончите. Это всего лишь псевдо-родовое, но я не могу придумать ничего лучшего.

 if (typeof(T) == typeof(MyFirstSet)) 
     { 
      mockDbSet.Setup(x => x.Find(It.IsAny<object[]>())).Returns<object[]>(x => (sourceList as List<MyFirstSet>).FirstOrDefault(y => y.MyFirstSetKey == (Guid)x[0]) as T); 
     } 
     else if (typeof(T) == typeof(MySecondSet)) 
     { 
      mockDbSet.Setup(x => x.Find(It.IsAny<object[]>())).Returns<object[]>(x => (sourceList as List<MySecondSet>).FirstOrDefault(y => y.MySecondSetKey == (Guid)x[0]) as T); 
     } 
     ...  
+0

Ницца. Поскольку все мои сущности имеют один и тот же первичный ключ «Id», я смог сделать это более общим, заменив «y.MySetKey» на «y.GetType(). GetProperty (« Id »). GetValue (y)». – Jared

1

Насколько я могу сказать, что нет «лучшей практики», ответ на этот вопрос, но вот как я подошел к нему. Я добавил необязательный параметр к методу AsDbSet, который идентифицирует первичный ключ, тогда метод Find может быть легко взломан.

public static DbSet<T> AsDbSet<T>(this List<T> sourceList, Func<T, object> primaryKey = null) where T : class 
{ 
    //all your other stuff still goes here 

    if (primaryKey != null) 
    { 
     mockSet.Setup(set => set.Find(It.IsAny<object[]>())).Returns((object[] input) => sourceList.SingleOrDefault(x => (Guid)primaryKey(x) == (Guid)input.First())); 
    } 

    ... 
} 

Я написал это на предположении одного GUID используется в качестве первичного ключа, как, казалось бы, как вы работаете, но принцип должен быть достаточно легко адаптироваться, если вам нужно больше гибкости для составные ключи и т.д.

0

Я закончил в следующий класс:

public static class DbSetMocking 
{ 
    #region methods 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, ICollection<TEntity> entities, Func<object[], TEntity> find = null) 
     where TEntity : class where TContext : DbContext 
    { 
     return setup.Returns(CreateMockSet(entities, find).Object); 
    } 

    private static Mock<DbSet<T>> CreateMockSet<T>(ICollection<T> data, Func<object[], T> find) 
     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()); 

     mockSet.SetupData(data, find); 

     return mockSet; 
    } 

    #endregion 
} 

который может быть использован:

private static MyRepository SetupRepository(ICollection<Type1> type1s, ICollection<Type2> type2s) 
{ 
    var mockContext = new Mock<MyDbContext>(); 

    mockContext.Setup(x => x.Type1s).ReturnsDbSet(type1s, o => type1s.SingleOrDefault(s => s.Secret == (Guid) o[ 0 ])); 
    mockContext.Setup(x => x.Type2s).ReturnsDbSet(type2s, o => type2s.SingleOrDefault(s => s.Id == (int) o[ 0 ])); 

    return new MyRepository(mockContext.Object); 
}