Hello i got some method that generating where statment programmatically how can i move where generation to other class method anyone can help ?
public static List<MME.Objects.TypedLists.InvoiceList> GetList(List<MMPFramework.SearchParameter> parameter)
{
MME.Objects.Invoice Invoice = null;
MME.Objects.Contractor Contractor = null;
MME.Objects.Contract Contract = null;
MME.Objects.TypedLists.InvoiceList invoiceList= null;
var t = MME.DAL.NhSessionHelper.GetCurrentSession().QueryOver<MME.Objects.Invoice>(() => Invoice);
foreach (var searchParameter in parameter)
{
if(searchParameter.Expression == "Like")
{
t.Where(Restrictions.Like(searchParameter.PropertyName, searchParameter.ObjectValueLo));
}
else if (searchParameter.Expression == "Eq")
{
t.Where(Restrictions.Eq(searchParameter.PropertyName, searchParameter.ObjectValueLo));
}
else if (searchParameter.Expression == "Between")
{
t.Where(Restrictions.Between(searchParameter.PropertyName, searchParameter.ObjectValueLo,searchParameter.ObjectValueHi));
}
else if(searchParameter.Expression == "Gt")
{
t.Where(Restrictions.Gt(searchParameter.PropertyName, searchParameter.ObjectValueLo));
}
else if (searchParameter.Expression == "Lt")
{
t.Where(Restrictions.Lt(searchParameter.PropertyName, searchParameter.ObjectValueLo));
}
else
{
//todo more
}
//t.Where(Restrictions.Eq(searchParameter.PropertyName, searchParameter.ObjectValue));
}
t.JoinQueryOver(() => Invoice.Contractor, () => Contractor, JoinType.LeftOuterJoin)
.JoinQueryOver(() => Invoice.Contract, () => Contract, JoinType.LeftOuterJoin)
.Select(Projections.Property(() => Invoice.Id).WithAlias(() => invoiceList.Id),
Projections.Property(() => Invoice.Number).WithAlias(() => invoiceList.InvoiceNumber),
Projections.Property(() => Contractor.Name).WithAlias(() => invoiceList.ContractorName),
Projections.Property(() => Contract.Number).WithAlias(() => invoiceList.ContractNumber)
)
.TransformUsing(Transformers.AliasToBean<MME.Objects.TypedLists.InvoiceList>());
return t.List<MME.Objects.TypedLists.InvoiceList>().ToList();
}
I've tried with this but it seems to not work.... Hope someone was doing something and can help me to handle with it.
public class BaseList
{
public object WhereGenerator(object ob)
{
QueryOver Ded = ob as QueryOver;
return null;
}
}
foreach (var restriction in BaseList.Createrestrictions(parameter))
{
t.Where(restriction);
}
public class BaseList
{
public IEnumerable<AbstractCriterion> Createrestrictions(List<MMPFramework.SearchParameter> parameter)
{
return parameter.Select(ToCritieria);
}
private AbstractCriterion ToCritieria(SearchParameter searchParameter)
{
if(searchParameter.Expression == "Like")
{
return Restrictions.Like(searchParameter.PropertyName, searchParameter.ObjectValueLo);
}
else ...
}
}
Related
Is there anyway I can optimize this code into shorter?
MakeList, TrimList and etc are List type.
and Vehicle are models.
My problem is code is very long. I have 20 property in model.
if (MakeList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => MakeList.Contains(b.Vehicle.Make));
}
if (TrimList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => TrimList.Contains(b.Vehicle.Trim));
}
if (ModelList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => ModelList.Contains(b.Vehicle.Model));
}
if (StockNoList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => StockNoList.Contains(b.Vehicle.StockNo));
}
if (BodyStyleList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => BodyStyleList.Contains(b.Vehicle.Body));
}
if (ExtColorList?.Any() == true)
{
bidVehicles = bidVehicles.Where(b => ExtColorList.Contains(b.Vehicle.Exterior));
}
return bidVehicles;
For LINQ to Objects:
public static class FilterExt
{
public static IEnumerable<TItem> ApplyFilter<TProp, TItem>(this IEnumerable<TItem> list, List<TProp> filter, Func<TItem, TProp> prop)
{
if (filter == null || filter.Count == 0)
{
return list;
}
return list.Where(x => filter.Contains(prop.Invoke(x)));
}
}
...
var filtered = bidVehicles
.ApplyFilter(MakeList, x => x.Vehicle.Make)
.ApplyFilter(TrimList, x => x.Vehicle.Trim).ToList();
If you use EF (means bidVehicles is IQueryable) you have to write expression for each property because you need full predicate Expression<Func<BidVehicle, bool>> not just Func<BidVehicle, TProp>.
I am trying to refactor some code. So the code I am trying to refactor is this
var userApp = string.Join(",", items.Where(x => x.LicenseType == 1).Select(x => x.GroupName));
var groupApp = string.Join(",", items.Where(x => x.LicenseType == 2).Select(x => x.GroupName));
var totalUsedLicense = GetTotalFreeLicense(schoolCode, userApp, groupApp);
foreach (var item in totalUsedLicense)
{
items.FirstOrDefault(x => x.GroupName == item.GroupName).AvailableLicense = items.FirstOrDefault(x => x.GroupName == item.GroupName).TotalLicense - item.Count;
}
Type of items can be List<ApplicationListDto> or List<AdUserApplicationDto> - they both inherit from BaseApplicationDto,
where the common properties are.
Now I want to make a function where I can pass items as a parameter. How should I do this?
I am trying to get rid of the redundant code here
if (isList)
{
if (data.Count <= 0) return;
List<AdUserApplicationDto> userApplicationDto = data;
var items = userApplicationDto;
var userApp = string.Join(",", items.Where(x => x.LicenseType == 1).Select(x => x.GroupName));
var groupApp = string.Join(",", items.Where(x => x.LicenseType == 2).Select(x => x.GroupName));
var totalUsedLicense = GetTotalFreeLicense(schoolCode, userApp, groupApp);
foreach (var item in totalUsedLicense)
{
items.FirstOrDefault(x => x.GroupName == item.GroupName).AvailableLicense = items.FirstOrDefault(x => x.GroupName == item.GroupName).TotalLicense - item.Count;
}
}
else
{
Page<ApplicationListDto> userApplicationDto = data;
if (userApplicationDto.TotalItems <= 0) return;
var items = userApplicationDto.Items;
var userApp = string.Join(",", items.Where(x => x.LicenseType == 1).Select(x => x.GroupName));
var groupApp = string.Join(",", items.Where(x => x.LicenseType == 2).Select(x => x.GroupName));
var totalUsedLicense = GetTotalFreeLicense(schoolCode, userApp, groupApp);
foreach (var item in totalUsedLicense) {
items.FirstOrDefault(x => x.GroupName == item.GroupName).AvailableLicense = items.FirstOrDefault(x => x.GroupName == item.GroupName).TotalLicense - item.Count;
}
}
If you want a method to accept different types of list with a common base type, do this:
public void SomeAction<T>(List<T> list) where T : BaseApplicationDto
{
// ...
}
You use the type of the base class as the type of the list..
public abstract class Common
{
public string CommonString { get; set; }
}
public class B : Common
{
}
public class A : Common
{
}
public class ABConsumer
{
public void DoSomething(List<Common> myList)
{
List<Common> EmptyStrings = myList.Where(x => x.CommonString == string.Empty).ToList();
}
}
You can now access properties of the base class of both classes.
This is simple inheritance.
Edit
It'll require a cast to from any given type: A or B to Common before it can be passed. If casting is an issue another good example using conditional generics can be used as shown in #ikkentims answer.
i have a problem to use the dynamic parameters inside orderby linq expression
SearchExp function
public Expression<Func<EmailAflAwmMessageDM, bool>> SearchXpr(string param, string q)
{
if (param == "to")
return e => e.to_msg.Contains(q);
else if (param == "from")
return e => e.from_msg.Contains(q);
else if (param == "cc")
return e => e.cc_msg.Contains(q);
else if (param == "bcc")
return e => e.bcc_msg.Contains(q);
else if (param == "subject")
return e => e.subject.Contains(q);
else
return e => e.body_text.Contains(q);
}
filterExp function
public Expression<Func<EmailAflAwmMessageDM, bool>> FiltertXpr(string filter, string value)
{
if (filter == "attachments")
return e => e.attachments == value;
else if (filter == "flagged")
return e => e.flagged == value;
else
return e => e.seen == value;
}
IQueryable function
private IQueryable SearchFilter(string param,string q,string filter,
string value,string sort,string dir)
{
var searchXpr = SearchXpr(param, q);
var filterXpr = FiltertXpr(filter, value);
var emailmessage =
db.EmailAflAwmMessage.
Where(filterXpr).Where(searchXpr)
.OrderByDescending(a => a.msg_date).Select(a =>
new
{
a.subject,
a.msg_date,
});
return emailmessage;
}
The above code is working, but i need OrderBy in dynamic way.
as i have 2 parameters sort( mean its the parameter name ) and dir (mean ascending or descending) like i want orderby(parameter name) dir
please help me, i appreciate your valuable time and suggestion, and also suggest me any alternate with simple way. thanks.
I suggest you to read about Expression's tree's, the code bellow is for didatic , but I think that will help you:
public static class ExpressionBuilder
{
private static readonly MethodInfo ToStringMethod = typeof(object).GetMethod("ToString");
private static readonly MethodInfo StringContainsMethod = typeof(string).GetMethod("Contains");
public static Func<T, object> Selector<T>(string prop)
{
var type = typeof(T);
var param = Expression.Parameter(type);
return Expression.Lambda<Func<T, object>>(Expression.Property(param, type.GetProperty(prop)), param).Compile();
}
public static Expression<Func<T, bool>> BuildFilterPredicate<T>(string q)
{
var query = Expression.Constant(q);
var type = typeof(T);
var lbdSelector = Expression.Parameter(type);
var predicates = type.GetProperties().SelectMany(p => PredicateContainsBuilder(lbdSelector, p, query)).ToList();
Expression body = predicates[0];
body = predicates.Skip(1).Aggregate(body, Expression.OrElse);
return Expression.Lambda<Func<T, bool>>(body, lbdSelector);
}
private static IEnumerable<MethodCallExpression> PredicateContainsBuilder(Expression lbdSelector, PropertyInfo prop, Expression query)
{
if (prop.PropertyType.IsClass)
return new List<MethodCallExpression> { Expression.Call(Expression.Call(Expression.Property(lbdSelector, prop), ToStringMethod), StringContainsMethod, query) };
var properties = prop.PropertyType.GetProperties();
return properties.Select(p => Expression.Call(Expression.Call(Expression.Property(lbdSelector, p), ToStringMethod), StringContainsMethod, query)).ToList();
}
}
So now you do this in your method:
Note:
I supose that the entity is EmailMessage so i use that to generate the predicate;
It doesn't search in depth;
it will search in all properties and doesn't use the string param to define what property to match;
private IQueryable SearchFilter(string param,string q,string filter,string value,string sort,string dir)
{
var emailMessage = db.EmailAflAwmMessage
.Where(ExpressionBuilder.BuildFilterPredicate<EmailMessage>(q))
.OrderBy(ExpressionBuilder.Selector<EmailMessage>(sort))
.Select(m=> new{m.subject,m.msg_date});
return emailmessage;
}
i have got the easy solution and now its working with me, there are more alternatives but i just to share my answer:
SortXpr function
private IQueryable SortXpr(IQueryable<EmailAflAwmMessageDM> email ,string sort,string dir) {
if (sort.Contains("to"))
{
if (dir.Contains("asc"))
{
return email.OrderBy(e => e.to_msg);
}
else
{
return email.OrderByDescending(e => e.to_msg);
}
}
else if (sort.Contains("from"))
{
if (dir.Contains("asc"))
{
return email.OrderBy(e => e.from_msg);
}
else
{
return email.OrderByDescending(e => e.from_msg);
}
}
else if (sort.Contains("subject"))
{
if (dir.Contains("asc"))
{
return email.OrderBy(e => e.subject);
}
else
{
return email.OrderByDescending(e => e.subject);
}
}
else
{
if (dir.Contains("asc"))
{
return email.OrderBy(e => e.msg_date);
}
else
{
return email.OrderByDescending(e => e.msg_date);
}
}
}
FilterXpr function
private Expression<Func<EmailAflAwmMessageDM, bool>> FiltertXpr(string filter, string value)
{
if (filter == "attachments")
return e => e.attachments == value;
else if (filter == "flagged")
return e => e.flagged == value;
else
return e => e.seen == value;
}
SearchXpr function
private Expression<Func<EmailAflAwmMessageDM, bool>> SearchXpr(string param, string q)
{
if (param == "to")
return e => e.to_msg.Contains(q);
else if (param == "from")
return e => e.from_msg.Contains(q);
else if (param == "cc")
return e => e.cc_msg.Contains(q);
else if (param == "bcc")
return e => e.bcc_msg.Contains(q);
else if (param == "subject")
return e => e.subject.Contains(q);
else
return e => e.body_text.Contains(q);
}
SearchFilterCondition function
private IQueryable SearchFilterCondition(string param,string q
,string filter,string value,string sort,string dir)
{
var searchXpr = SearchXpr(param, q);
var filterXpr = FiltertXpr(filter, value);
IQueryable<EmailAflAwmMessageDM>
EmailAflAwmMessagejc = db.EmailAflAwmMessage.Where(filterXpr).Where(searchXpr);
return SortXpr(EmailAflAwmMessagejc, sort, dir);
}
thanks for the stackoverflow community, i appreciate your valuable time, thanks again.
I want to make async method in redis by StackExchange.Redis follow code :
public bool Insert<T>(T entity) where T : IBaseEntity
{
long entityCounter = _redisClient.StringIncrement(CacheProcessPatterns.MakeItemCounter(entity.GetType().Name));
if (entity.Id == 0)
{
entity.Id = ((int)GetLastId<T>()) + 1;
}
_redisClient.StringSet(CacheProcessPatterns.MakeLastId(entity.GetType().Name), entity.Id);
string itemRedisKey = CacheProcessPatterns.MakeItemById(entity.GetType().Name, entity.Id);
bool setStatus = _redisClient.StringSet(itemRedisKey, JsonSerializer.SerializeToString<T>(entity));
if (setStatus)
{
_redisClient.StringSet(CacheProcessPatterns.MakeIdByKey(entity.GetType().Name, entity.Id), entity.Key.ToString());
_redisClient.StringSet(CacheProcessPatterns.MakeKeyById(entity.GetType().Name, entity.Key.ToString()), entity.Id);
_redisClient.SetAdd(CacheProcessPatterns.MakeItemKeysByType(entity.GetType().Name), entity.Id);
}
else
{
entityCounter = _redisClient.StringDecrement(CacheProcessPatterns.MakeItemCounter(entity.GetType().Name));
}
return setStatus;
}
and the other hands i trying make async but I have a problem on second ContinueWith() Method .
Error : Cannot implicity convert type 'Task' to 'Task'.An
explicit conversation exists(are you missing a cast?).
Follow code :
public Task<bool> Insert<T>(T entity) where T : IBaseEntity
{
return _redisClient.StringIncrementAsync(CacheProcessPatterns.MakeItemCounter(entity.GetType().Name))
.ContinueWith(entityCounter =>
{
if (entity.Id == 0)
{
entity.Id = ((int)GetLastId<T>().Result);
}
}).ContinueWith(_ =>
{
_redisClient.StringSetAsync(CacheProcessPatterns.MakeLastId(entity.GetType().Name), entity.Id).ContinueWith(status =>
{
string itemRedisKey = CacheProcessPatterns.MakeItemById(entity.GetType().Name, entity.Id);
_redisClient.StringSetAsync(itemRedisKey, JsonSerializer.SerializeToString<T>(entity)).ContinueWith( setStatus =>
{
if (setStatus.Result)
{
ITransaction tran = _redisClient.CreateTransaction();
tran.StringSetAsync(CacheProcessPatterns.MakeIdByKey(entity.GetType().Name, entity.Id), entity.Key.ToString());
tran.StringSetAsync(CacheProcessPatterns.MakeKeyById(entity.GetType().Name, entity.Key.ToString()), entity.Id);
tran.SetAddAsync(CacheProcessPatterns.MakeItemKeysByType(entity.GetType().Name), entity.Id);
return tran.ExecuteAsync();
}
else
{
_redisClient.StringDecrementAsync(CacheProcessPatterns.MakeItemCounter(entity.GetType().Name));
}
return setStatus;
});
});
});
}
What is my problem ? and how to fix that ?Thanks ...
I think the problem is that your second ContinueWith returns a Task and not a Task<bool>. Try changing the code as follows:
public Task<bool> Insert<T>(T entity) where T : IBaseEntity
{
return _redisClient.StringIncrementAsync(CacheProcessPatterns.MakeItemCounter(entity.GetType().Name))
.ContinueWith(entityCounter =>
{
if (entity.Id == 0)
{
entity.Id = ((int)GetLastId<T>().Result);
}
})
// Explicitly specify task type to be bool
.ContinueWith<bool>(_ =>
{
_redisClient.StringSetAsync(CacheProcessPatterns.MakeLastId(entity.GetType().Name), entity.Id).ContinueWith(status =>
{
string itemRedisKey = CacheProcessPatterns.MakeItemById(entity.GetType().Name, entity.Id);
_redisClient.StringSetAsync(itemRedisKey, JsonSerializer.SerializeToString<T>(entity)).ContinueWith( setStatus =>
{
if (setStatus.Result)
{
ITransaction tran = _redisClient.CreateTransaction();
tran.StringSetAsync(CacheProcessPatterns.MakeIdByKey(entity.GetType().Name, entity.Id), entity.Key.ToString());
tran.StringSetAsync(CacheProcessPatterns.MakeKeyById(entity.GetType().Name, entity.Key.ToString()), entity.Id);
tran.SetAddAsync(CacheProcessPatterns.MakeItemKeysByType(entity.GetType().Name), entity.Id);
return tran.ExecuteAsync();
}
else
{
_redisClient.StringDecrementAsync(CacheProcessPatterns.MakeItemCounter(entity.GetType().Name));
}
return setStatus;
});
});
return true; // since this is a Task<bool> we need a bool return value
I am trying to apply the new mocking of EF6 to my existing code.
I have a class that Extends DbSet. One of the methods call the base class (BdSet) Create method. Here is the a sample code (not the complete solution or real names):
public class DerivedDbSet<TEntity> : DbSet<TEntity>, IKeyValueDbSet<TEntity>, IOrderedQueryable<TEntity> where TEntity : class
{
public virtual bool Add(string value1, string value2) {
var entity = Create(); // There is no direct implementation of the Create method it is calling the base method
// Do something with the values
this.Add(entity);
return true;
}
}
I am mocking using the Test Doubles sample (here is the peace of code):
var data = new List<DummyEntity> {
new DummyEntity { Value1 = "First", Value2 = "001" },
new DummyEntity { Value1 = "Second", Value2 = "002" }
}.AsQueryable();
var mock = new Mock<DerivedDbSet<DummyEntity>>();
mock.CallBase = true;
mock.As<IQueryable<T>>().Setup(m => m.Provider).Returns(source.Provider);
mock.As<IQueryable<T>>().Setup(m => m.Expression).Returns(source.Expression);
mock.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(source.ElementType);
mock.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
I've set the CallBase property to true to try to force the call to the base class...
But I keep receiving the following error:
System.NotImplementedException: The member 'Create' has not been implemented on type 'DerivedDbSet1Proxy' which inherits from 'DbSet1'. Test doubles for 'DbSet`1' must provide implementations of methods and properties that are used.
I want the call of create to fallback to the default implementation in DbSet.
Can someone help me with that?
After some struggle with the internal functions and async references of mocking a DbSet I came out with a helper class that solved most of my problems and might serve as base to someone implementation.
Here is the code:
public static class MockHelper
{
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider {
private readonly IQueryProvider _inner;
internal TestDbAsyncQueryProvider(IQueryProvider inner) { _inner = inner; }
public IQueryable CreateQuery(Expression expression) { return new TestDbAsyncEnumerable<TEntity>(expression); }
public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { return new TestDbAsyncEnumerable<TElement>(expression); }
public object Execute(Expression expression) { return _inner.Execute(expression); }
public TResult Execute<TResult>(Expression expression) { return _inner.Execute<TResult>(expression); }
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute(expression)); }
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute<TResult>(expression)); }
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T> {
public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { }
public TestDbAsyncEnumerable(Expression expression) : base(expression) { }
public IDbAsyncEnumerator<T> GetAsyncEnumerator() { return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); }
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() { return GetAsyncEnumerator(); }
public IQueryProvider Provider { get { return new TestDbAsyncQueryProvider<T>(this); } }
}
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> {
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner) { _inner = inner; }
public void Dispose() { _inner.Dispose(); }
public Task<bool> MoveNextAsync(CancellationToken cancellationToken) { return Task.FromResult(_inner.MoveNext()); }
public T Current { get { return _inner.Current; } }
object IDbAsyncEnumerator.Current { get { return Current; } }
}
public static Mock<TDbSet> CreateDbSet<TDbSet, TEntity>(IList<TEntity> data, Func<object[], TEntity> find = null)
where TDbSet : class, IDbSet<TEntity>
where TEntity : class, new() {
var source = data.AsQueryable();
var mock = new Mock<TDbSet> { CallBase = true };
mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression);
mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType);
mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider));
mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity());
mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; });
mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; });
if (find != null) mock.As<IDbSet<TEntity>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns(find);
return mock;
}
public static Mock<DbSet<TEntity>> CreateDbSet<TEntity>(IList<TEntity> data, Func<object[], TEntity> find = null)
where TEntity : class, new() {
return CreateDbSet<DbSet<TEntity>, TEntity>(data, find);
}
}
And here is a use sample (based on the names I've given before):
var data = new List<DummyEntity> {
new DummyEntity { Value1 = "First", Value2 = "001" } },
new DummyEntity { Value1 = "Second", Value2 = "002" } }
};
var mockDummyEntities = MockHelper.CreateDbSet<DerivedDbSet<DummyEntities>, DummyEntities>(data, i => data.FirstOrDefault(k => k.Value2 == (string)i[0]));
var mockContext = new Mock<DummyDbContext>();
mockContext.Setup(c => c.DummyEntities).Returns(mockDummyEntities.Object);
Any suggestions on how to improve this solution is very welcome.
Regards
This is a modified version based on Andre that worked for me. Note, that I did not need the async references. Code will add all derived classes (if any)
Usage:
/// <summary>
///
/// </summary>
[TestMethod]
public void SomeTest()
{
//Setup
var mockContext = new Mock<FakeDbContext>();
//SomeClass can be abstract or concrete
mockContext.createFakeDBSet<SomeClass>();
var db = mockContext.Object;
//Setup create(s) if needed on concrete classes
//Mock.Get(db.Set<SomeOtherClass>()).Setup(x => x.Create()).Returns(new SomeOtherClass());
//DO Stuff
var list1 = db.Set<SomeClass>().ToList();
//SomeOtherClass derived from SomeClass
var subList1 = db.Set<SomeOtherClass>().ToList();
CollectionAssert.AreEquivalent(list1.OfType<SomeOtherClass>.ToList(), subList1);
}
Code:
/// <summary>
/// http://stackoverflow.com/questions/21943328/ef6-mocking-derived-dbsets
/// </summary>
public static class MoqSetupExtensions
{
static IEnumerable<Type> domainTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes());
public static Mock<DbSet<T>> createFakeDBSet<T>(this Mock<FakeDbContext> db, List<T> list = null, Func<List<T>, object[], T> find = null, bool createDerivedSets = true) where T : class
{
list = list ?? new List<T>();
var data = list.AsQueryable();
//var mockSet = MockHelper.CreateDbSet(list, find);
var mockSet = new Mock<DbSet<T>>() { CallBase = true };
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(() => { return data.Provider; });
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(() => { return data.Expression; });
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(() => { return data.ElementType; });
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => { return list.GetEnumerator(); });
mockSet.Setup(m => m.Add(It.IsAny<T>())).Returns<T>(i => { list.Add(i); return i; });
mockSet.Setup(m => m.AddRange(It.IsAny<IEnumerable<T>>())).Returns<IEnumerable<T>>((i) => { list.AddRange(i); return i; });
mockSet.Setup(m => m.Remove(It.IsAny<T>())).Returns<T>(i => { list.Remove(i); return i; });
if (find != null) mockSet.As<IDbSet<T>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns<object[]>((i) => { return find(list, i); });
//mockSet.Setup(m => m.Create()).Returns(new T());
db.Setup(x => x.Set<T>()).Returns(mockSet.Object);
//Setup all derived classes
if (createDerivedSets)
{
var type = typeof(T);
var concreteTypes = domainTypes.Where(x => type.IsAssignableFrom(x) && type != x).ToList();
var method = typeof(MoqSetupExtensions).GetMethod("createFakeDBSetSubType");
foreach (var item in concreteTypes)
{
var invokeResult = method.MakeGenericMethod(type, item)
.Invoke(null, new object[] { db, mockSet });
}
}
return mockSet;
}
public static Mock<DbSet<SubType>> createFakeDBSetSubType<BaseType, SubType>(this Mock<FakeDbContext> db, Mock<DbSet<BaseType>> baseSet)
where BaseType : class
where SubType : class, BaseType
{
var dbSet = db.Object.Set<BaseType>();
var mockSet = new Mock<DbSet<SubType>>() { CallBase = true };
mockSet.As<IQueryable<SubType>>().Setup(m => m.Provider).Returns(() => { return dbSet.OfType<SubType>().Provider; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.Expression).Returns(() => { return dbSet.OfType<SubType>().Expression; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.ElementType).Returns(() => { return dbSet.OfType<SubType>().ElementType; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.GetEnumerator()).Returns(() => { return dbSet.OfType<SubType>().GetEnumerator(); });
mockSet.Setup(m => m.Add(It.IsAny<SubType>())).Returns<SubType>(i => { dbSet.Add(i); return i; });
mockSet.Setup(m => m.AddRange(It.IsAny<IEnumerable<SubType>>())).Returns<IEnumerable<SubType>>((i) => { dbSet.AddRange(i); return i; });
mockSet.Setup(m => m.Remove(It.IsAny<SubType>())).Returns<SubType>(i => { dbSet.Remove(i); return i; });
mockSet.As<IDbSet<SubType>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns<object[]>((i) => { return dbSet.Find(i) as SubType; });
baseSet.Setup(m => m.Create<SubType>()).Returns(() => { return mockSet.Object.Create(); });
db.Setup(x => x.Set<SubType>()).Returns(mockSet.Object);
return mockSet;
}
}
Microsoft published a very well explained guide providing an in-memory implementation of DbSet which has a complete implemention for all the methods on DbSet.
Check the article at http://msdn.microsoft.com/en-us/data/dn314431.aspx#doubles