I'm new at this, can someone show me how this works and what goes in "mycode" in order to call the repository?
public class MemberService : ServiceBase<IMemberModel>, IMemberService
{
public MemberViewModel GetSingle(Expression<Func<MemberViewModel, bool>> whereCondition)
{
mycode
}
}
public abstract class RepositoryBase<T> : IRepository<T>
{
public T GetSingle(Expression<Func<T, bool>> whereCondition)
{
return ObjectSet.Where(whereCondition).FirstOrDefault();
}
}
Thanks!
What you need is probably a lambda based on a Member, not a MemberViewModel. Your method should probably be something like:
public MemberViewModel GetSingle( Expression<Func<Member,bool>> whereCondition )
{
var member = this.MemberRepository.GetSingle( whereCondition );
if (member != null)
{
return new MemberViewModel( member );
// or however you map from member to its view model
}
return null;
}
Related
How to set up where linq extension on object? DbSet in my case. Here is my code:
this.workflowStateSet
.Setup(m => m.Where(It.IsAny<Expression<Func<Model.WorkflowState, int, bool>>>()))
.Returns(new List<Model.WorkflowState>().AsQueryable());
However, it gives me exception not very familiar exception:
System.NotSupportedException: Expression references a method that
does not belong to the mocked object: m => m.Where<WorkflowState>
I will be grateful for any hint.
This extension method will help mock the DbSet
public static class MockDbSetExtensions {
public static Mock<DbSet<T>> AsDbSetMock<T>(this IEnumerable<T> list) where T : class {
IQueryable<T> queryableList = list.AsQueryable();
Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>();
dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider);
dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression);
dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());
return dbSetMock;
}
}
And you can use it like this.
//Arrange
var data = new List<Model.WorkflowState>();
//you would populate your list as needed.
//convert it to a mock DbSet that uses the list as its datasource
var workflowStateSet = data.AsDbSetMock();
var dbSet = workflowStateSet.Object;
//Act
var items = dbSet.Where("Your expression here");
//Assert
//....
Use the repository pattern to add a layer of abstraction to the data retrieval. This abstraction can then be mocked.
If, for example, you were trying to retrieve all of the workflows with a stateId equal to 1, then rather than calling something like this
var result = DbSet.WorkflowState.Where(w => w.stateId == 1);
move this code into another class and then create an interface of the method signature.
public interface IWorkflowStateSetRepository{
IQueryable<Model.WorkflowState> GetAllWorkflows(int state);
}
implementation
public class WorkflowStateSetRepository : IWorkflowStateSetRepository{
public IQueryable<Model.WorkflowState> GetAllWorkflows(int state){
return DbSet.WorkflowState .Where(w => w.stateId == state);
}
}
In the calling code get an instance of IWorkflowStateSetRepository (probably from your IoC container) and call the GetAllWorkflows() method instead. This will give you the same results as before but you can now mock the interface in your tests and setup calls to that methods.
this.MockedIWorkflowStateSetRepository.Setup(m => m.GetAllWorkflows(It.IsAny<int>()))
.Returns(new List<Model.WorkflowState>().AsQueryable());
This code is more maintainable and (with appropriately named variables and methods) also conveys the intent a lot better.
The repository pattern is discussed in greater detail here;
http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Are you trying to Mock up a real DbSet instance ? Cause this won't work, as the error message try to explain you. To mock a type, it must either be an interface or have virtual members (abstract members are also virtual).
You can try to mock up IDbSet or to create a custom DbSet class, for instance something like the following class
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
public class DbSetMock<T> : DbSet<T>, IDbSet<T>
where T : class
{
private readonly ICollection<T> _contentCollection;
public DbSetMock(IList<T> contentCollection = null)
{
_contentCollection = new Collection<T>(contentCollection ?? new List<T>());
AddedEntities = new List<T>();
RemovedEntities = new List<T>();
AttachedEntities = new List<T>();
}
public void OverrideContentCollection(IEnumerable<T> newData)
{
_contentCollection.Clear();
_contentCollection.AddRange(newData);
}
public IList<T> AddedEntities { get; private set; }
public IList<T> AttachedEntities { get; private set; }
public override ObservableCollection<T> Local
{
get
{
throw new NotImplementedException();
}
}
public IList<T> RemovedEntities { get; private set; }
public Type ElementType
{
get
{
return typeof(T);
}
}
public Expression Expression
{
get
{
return _contentCollection.AsQueryable().Expression;
}
}
public IQueryProvider Provider
{
get
{
return _contentCollection.AsQueryable().Provider;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _contentCollection.GetEnumerator();
}
public override T Add(T entity)
{
AddedEntities.Add(entity);
_contentCollection.Add(entity);
return entity;
}
public override T Attach(T entity)
{
AttachedEntities.Add(entity);
var matchingEntity = _contentCollection.SingleOrDefault(x => x.Id == entity.Id);
if (matchingEntity != null)
{
_contentCollection.Remove(matchingEntity);
}
_contentCollection.Add(entity);
return entity;
}
public override TDerivedEntity Create<TDerivedEntity>()
{
throw new NotImplementedException();
}
public override T Create()
{
throw new NotImplementedException();
}
public override T Find(params object[] keyValues)
{
throw new NotImplementedException();
}
public override T Remove(T entity)
{
RemovedEntities.Add(entity);
_contentCollection.Remove(entity);
return entity;
}
}
You can use constructor parameter to setup content that will be retrieved by the db set.
Hope this helps.
I have the following:
public class P1 {
public P2 P2 { get; set; }
public P4 P4 { get; set; }
}
public class P2 {
public P3 P3 { get; set; }
}
public class P3 { }
public class P4 { }
I need a Mapper for P1 as follows:
Mapper mapper = new Mapper<P1>();
But I would like to use it as follows:
Mapper<P1> mapper = Mapper.For<P1>()
.Add(p1 => p1.P2).And(p2 => p2.P3)
.Add(p1 => p1.P4);
So I have:
public class Mapper {
public static Mapper<T> For<T>() {
return new Mapper<T>();
}
}
public class Mapper<T> {
private List<LambdaExpression> _expressions = new List<LambdaExpression>();
public Mapper<T> Add<K>(Expression<Func<T, K>> expression) {
// Add expression to expressions
return this;
}
public Mapper<T> And<K>(Expression<Func<T, K>> expression) {
// Add expression to expressions
return this;
}
}
My problem is how to deal with Child Properties.
Note that my Mapper code only allows to do this:
Mapper<P1> mapper = Mapper.For<P1>()
.Add(p1 => p1.P2).And(p1 => p1.P2.P3);
And not this:
Mapper<P1> mapper = Mapper.For<P1>()
.Add(p1 => p1.P2).And(p2 => p2.P3);
I think the methods should return Mapper but in fact I want to create a Mapper ...
K is only a way to define the expression when calling a method.
Does anyone knows how to do this?
UPDATE
Answer with ContinuationMapper:
public class Mapper<T> {
protected List<LambdaExpression> Paths { get; set; } = new List<LambdaExpression>();
public ContinuationMapper<T, TCont> Add<TCont>(Expression<Func<T, TCont>> path) {
Paths.Add(path);
return new ContinuationMapper<T, TCont>(Paths);
}
}
public class ContinuationMapper<TBase, TCurrent> : Mapper<TBase> {
public ContinuationMapper(List<LambdaExpression> paths) {
Paths = paths;
}
public ContinuationMapper<TBase, TNext> And<TNext>(Expression<Func<TCurrent, TNext>> path) {
base.Paths.Add(path);
return new ContinuationMapper<TBase, TNext>(base.Paths);
}
}
I see the following problems with your current approach:
It shouldn't be possible to do an And before an Add.
At the end of an And (or an Add), you have two types of type information: The base type for a new Add, and the continuation type for a new And. You cannot "store" that type of information at compile-time in a generic type with only one generic parameter.
Luckily, the type system can help here, if we split your one class in two:
public class Mapper<T>
{
public ContinuationMapper<T, TCont> Add<TCont>(Expression<Func<T, TCont>> expression) {
// ...
return new ContinuationMapper<T, TCont>(...);
}
}
public class ContinuationMapper<TBase, TCurrent> : Mapper<TBase>
{
public ContinuationMapper<TBase, TNext> And<TNext>(Expression<Func<TCurrent, TNext>> expression) {
// ...
return new ContinuationMapper<TBase, TNext>(...);
}
}
Obviously, you will have to pass the state of your expression list to the new classes, which is left as an exercise to the reader. As an added bonus, you can even make your classes immutable, if you want.
In essence you don't want to bind the method types to the class type so you need to use 2 new generic types. sort of like the following.
public Mapper<T> Add<V,K>(Expression<Func<V, K>> expression) {
// Add expression to expressions
return this;
}
public Mapper<T> And<V,K>(Expression<Func<V, K>> expression) {
// Add expression to expressions
return this;
}
Example:
Mapper<P1> mapper = Mapper.For<P1>()
.Add((P1 p1) => p1.P2).And((P2 p2) => p2.P3)
.Add((P1 p1) => p1.P4);
If you want the Add to be tied to the T and the and to be tied to the last property you'll need to provide back a wrapper class like
Mapper<T,V>{
public Mapper<T> Add<V>(Expression<Func<T, V>> expression) {
// Add expression to expressions
return Mapper.Wrap<T,V>(this);
}
public Mapper<T> And<V>(Expression<Func<V, K>> expression) {
// Add expression to expressions
return this;
}
}
I have the class PlayersCollection and I want to interface it in IWorldCollection.
The probleme is about writing the declaration in the interface which cause me this error :
Assets/Scripts/Arcane/api/Collections/ItemsCollection.cs(17,22): error CS0425:
The constraints for type parameter `T' of method
`Arcane.api.ItemsCollection.Get<T>(int)
must match the constraints for type parameter `T' of
interface method `Arcane.api.IWorldCollection.Get<T>(int)'.
Consider using an explicit interface implementation instead
Here is my class and my interface. How to write a generic method implementation with a class constraint ?
public class PlayersCollection : IWorldCollection
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public T Get<T>(int id) where T: PlayerModel
{
var t = typeof(T);
if (!Collection.ContainsKey(t)) return null;
var dict = Collection[t] as Dictionary<int, T>;
if (!dict.ContainsKey(id)) return null;
return (T)dict[id];
}
}
}
public interface IWorldCollection
{
T Get<T>(int id) where T : class;// Work when I change "class" to "PlayerModel".
}
Big thanks for your help :)
It seems to me that the following will meet the requirements, by pushing the generic type parameter up to the class/interface level:
public class PlayersCollection<T> : IWorldCollection<T> where T : PlayerModel
{
public Dictionary<Type, T> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, T>();
}
public T Get(int id)
{
var t = typeof(T);
if (!Collection.ContainsKey(t)) return null;
var dict = Collection[t] as Dictionary<int, T>;
if (!dict.ContainsKey(id)) return null;
return (T)dict[id];
}
}
public interface IWorldCollection<T> where T : class
{
T Get(int id);
}
If I have missed something in the requirements, please let me know.
I'm not sure why you need this interface, but maybe this will help:
public class PlayersCollection<T> : IWorldCollection<T> where T:PlayerModel
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public T Get(int id)
{
...
}
}
public interface IWorldCollection<T> where T:class
{
T Get(int id);
}
Try this:
public class PlayersCollection : IWorldCollection<PlayerModel>
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public PlayerModel Get<PlayerModel>(int id)
{
///
}
}
}
public interface IWorldCollection<T>
{
T Get<T>(int id);
}
In your case, in the class implementing your interface, you adding more condition for your class:
where T : class in the interface
where T: PlayerModel in the class
If, for some reason, you need to add a constraint into your interface, you need to add an interface or base class, which will be placed to the interface declaration, and you'll have to derive from it in your PlayerModel class, like this:
public abstract class Model
{
}
public class PlayerModel : Model
{
}
public interface IWorldCollection<T> where T : Model
{
T Get<T>(int id);
}
public class PlayersCollection : IWorldCollection<PlayerModel>
{
///
public PlayerModel Get<PlayerModel>(int id)
{
///
}
}
}
I have a two tier object hierarchy like so:
public class BaseStat
{
}
public class IndividualDefensiveStat: BaseStat
{
} //and many other stats that inherit from BaseStat
I then have a Generic Repository like so:
public class StatRepository<TDerived> where TDerived: BaseStat
{
//ctr here
public TDerived FindById(int? id)
{
if(!id.HasValue)
throw new Exception("id has to have a value");
var result = _context.Set<TDerived>().Find(id);
return result;
}
public void Insert(TDerived item)
{
_context.Set<TDerived>().Add(item);
_context.SaveChanges();
}
}
Finally I have a service class that accepts a data context, and does CRUD operations based on the derived type passed in:
public class StatDataService
{
private IStatContext _context;
public StatDataService(IStatContext context)
{
_context = context;
}
public void InsertData<TEntity>(TEntity item) where TEntity : BaseStat
{
var repo = getRepository<TEntity>();
repo.Insert(item);
}
public TEntity GetById<TEntity>(int? id) where TEntity : BaseStat
{
return getRepository<TEntity>().FindById(id);
}
private StatRepository<TEntity> getRepository<TEntity>() where TEntity: BaseStat
{
return new StatRepository<TEntity>(_context);
}
}
I would like to have a list of BaseStat that houses items that derive from it. However, if I do a foreach loop and pass each item into the the Service's Insert method, TEntity/TDerived in this case is BaseStat.
How can I have TEntity/TDerived actually be the DerivedType that I am trying to target?
I have a repository interface as below:
public interface IDataContext<TId> : IDisposable
{
IQueryable<T> Repository<T>() where T : class, IEntity<TId>;
T FindById<T>(TId id) where T : class, IEntity<TId>;
void Insert<T>(T item) where T : class, IEntity<TId>;
void Delete<T>(T item) where T : class, IEntity<TId>;
void Commit();
}
Note that Repository<T> returns an IQueryable<T>.
I have a class that can wrap a LinqToSQL data context, with the Repository<T> method as below:
public IQueryable<T> Repository<T>() where T : class, IEntity<int>
{
ITable table = _context.GetTable(GetEntityType<T>());
return table.Cast<T>();
}
This works fine, I can do something like
new Repository(new SQLDataContext())
.Repository<MyEntity>().Where(e => SqlMethods.Like(e.Id, "123%");
Now I've started thinking about caching but I have a problem.
I've created a class that wraps and implements an IDataContext<TId> that will cache results from calls to Repository<T> in memory. Something like the below:
public IQueryable<T> Repository<T>() where T : class, IEntity<TId>
{
// Actual caching logic here.....
return _CachedEntities[typeof(T)].OfType<T>().AsQueryable<T>();
}
The issue I have is that now the IQueryable<T> I return is in-memory, not translated to SQL, so I get an exception about using SqlMethods.Like.
TL;DR: So, how can I create my caching repository wrapper in such a way that the calling classes don't need to worry about whether the IDataContext<T> it's dealing with is an in-memory repository (i.e. the caching one) or a normal LinqToSQL repository?
It's possible, you need to write custom IQueryProvider and IQueryable<T>:
public static class MySqlMethods
{
public static bool Like(string matchExpression, string pattern)
{
//Your implementation
return true;
}
}
public class ChangeMethodsVisitor : ExpressionVisitor
{
//This method will change SqlMethods to MySqlMethods.
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof(SqlMethods))
{
//Getting method from MySqlMethods class.
var method = typeof(MySqlMethods).GetMethod(node.Method.Name,
node.Method.GetParameters()
.Select(info => info.ParameterType)
.ToArray());
return Expression.Call(method, node.Arguments);
}
return base.VisitMethodCall(node);
}
}
public class MyQueryProvider : IQueryProvider
{
private static readonly ExpressionVisitor ExpressionVisitor = new ChangeMethodsVisitor();
private readonly IQueryProvider _queryProvider;
public MyQueryProvider(IQueryProvider queryProvider)
{
_queryProvider = queryProvider;
}
public IQueryable CreateQuery(Expression expression)
{
expression = ExpressionVisitor.Visit(expression);
var queryable = _queryProvider.CreateQuery(expression);
//Wrap queryable to MyQuery class.
var makeGenericType = typeof(MyQuery<>).MakeGenericType(queryable.ElementType);
return (IQueryable)makeGenericType.GetConstructor(new[] { typeof(IQueryable<>).MakeGenericType(queryable.ElementType) })
.Invoke(new object[] { queryable });
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
expression = ExpressionVisitor.Visit(expression);
//Wrap queryable to MyQuery class.
var queryable = _queryProvider.CreateQuery<TElement>(expression);
return new MyQuery<TElement>(queryable);
}
public object Execute(Expression expression)
{
expression = ExpressionVisitor.Visit(expression);
return _queryProvider.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
expression = ExpressionVisitor.Visit(expression);
return _queryProvider.Execute<TResult>(expression);
}
}
public class MyQuery<T> : IOrderedQueryable<T>
{
private readonly IQueryable<T> _queryable;
public MyQuery(IQueryable<T> queryable)
{
_queryable = queryable;
Provider = new MyQueryProvider(_queryable.Provider);
}
public MyQuery(IEnumerable<T> enumerable)
: this(enumerable.AsQueryable())
{
}
public IEnumerator<T> GetEnumerator()
{
return _queryable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Expression Expression
{
get { return _queryable.Expression; }
}
public Type ElementType
{
get { return _queryable.ElementType; }
}
public IQueryProvider Provider { get; private set; }
}
And then you can use it:
var list = new List<string>(){"test", "test1"};
var myQuery = new MyQuery<string>(list);
var queryable = myQuery.Where(s => SqlMethods.Like(s, "123%")).ToArray();