I currently have this class make up for my Generic Repository
public abstract class RepositoryBase<T> where T : class
{
private readonly ISession session;
public RepositoryBase(ISession session)
{
this.session = session;
this.session.FlushMode = FlushMode.Auto;
}
public void Start()
{
this.session.BeginTransaction();
}
public bool Add(T entity)
{
this.session.Save(entity);
return true;
}
public bool AddAll(IEnumerable<T> items)
{
foreach (T item in items)
{
this.session.Save(item);
}
return true;
}
public bool Update(T entity)
{
this.session.Flush();
this.session.Update(entity);
return true;
}
public bool Delete(T entity)
{
this.session.Delete(entity);
return true;
}
public bool DeleteAll(IEnumerable<T> entities)
{
foreach (T entity in entities)
{
this.Delete(entity);
}
return true;
}
public T GetById(int id)
{
return this.session.Get<T>(id);
}
public T GetById(string id)
{
return this.session.Get<T>(id);
}
public T GetById(long id)
{
return this.session.Get<T>(id);
}
public T GetById(Guid id)
{
return this.session.Get<T>(id);
}
public IQueryable<T> GetAll()
{
return this.session.Query<T>();
}
public T Get(Expression<System.Func<T, bool>> expression)
{
return GetMany(expression).SingleOrDefault();
}
public IQueryable<T> GetMany(Expression<System.Func<T, bool>> expression)
{
return GetAll().Where(expression).AsQueryable();
}
}
When I call the GetById method which takes a string parameter I am met with an exception error that states GetById is expecting type Guid not string. How do I design this method to accept a string parameter?
You can design your class adding another generic parameter for the type of id:
public abstract class Repository<T, TId>
{
public T Get(TId id)
{
}
}
take a look at this https://github.com/sharparchitecture/sharp-architecture/wiki
Related
I know there are a few similar question out there regarding this issue with structure map, but my issue seems to not be resolvable with the solutions they needed. I must state I have used this same setup many times, but cannot see why this code is causing issues.
Here are all of the classes I think that are needed for help with troubleshooting my code.
ControllerConvention.cs
public class ControllerConvention : IRegistrationConvention
{
public void ScanTypes(TypeSet types, Registry registry)
{
foreach (Type type in types.AllTypes())
{
if (type.CanBeCastTo(typeof(Controller)) && !type.IsAbstract)
{
registry.For(type).LifecycleIs(new UniquePerRequestLifecycle());
}
}
}
}
IoC.cs
public static class IoC
{
public static IContainer Container { get; set; }
static IoC()
{
Container = new Container();
}
}
StandardRegistry.cs
public class StandardRegistry : Registry
{
public StandardRegistry()
{
Scan(scan =>
{
scan.TheCallingAssembly();
scan.Assembly("PotSmart.Service");
scan.Assembly("PotSmart.Data");
scan.WithDefaultConventions();
});
}
}
StructureMapDependencyResolver.cs
public class StructureMapDependencyResolver : IDependencyResolver
{
private readonly Func<IContainer> _factory;
public StructureMapDependencyResolver(Func<IContainer> factory)
{
_factory = factory;
}
public object GetService(Type serviceType)
{
if (serviceType == null)
{
return null;
}
var factory = _factory();
return serviceType.IsAbstract || serviceType.IsInterface
? factory.TryGetInstance(serviceType)
: factory.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _factory().GetAllInstances(serviceType).Cast<object>();
}
}
Global.asax
public IContainer Container
{
get
{
return (IContainer)HttpContext.Current.Items["_Container"];
}
set
{
HttpContext.Current.Items["_Container"] = value;
}
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
DependencyResolver.SetResolver(new StructureMapDependencyResolver(() => Container ?? IoC.Container));
IoC.Container.Configure(cfg =>
{
cfg.AddRegistry(new StandardRegistry());
cfg.AddRegistry(new ControllerRegistry());
});
}
Here are parts of my data and entities classes:
RepositoryBase.cs
public class RepositoryBase
{
private readonly PotSmartEntityModel1 _dataContext;
protected IDbFactory DbFactory { get; private set; }
protected PotSmartEntityModel1 DbContext
{
get
{
return _dataContext ?? DbFactory.Init();
}
}
protected RepositoryBase(IDbFactory dbFactory)
{
DbFactory = dbFactory;
var adapter = (IObjectContextAdapter)this;
var objectContext = adapter.ObjectContext;
// objectContext.CommandTimeout = 120; // value in seconds
}
}
public abstract class RepositoryBase<T> where T : class
{
private readonly PotSmartEntityModel1 _dataContext;
private readonly IDbSet<T> _dbSet;
protected IDbFactory DbFactory { get; private set; }
protected PotSmartEntityModel1 DbContext
{
get
{
return _dataContext ?? DbFactory.Init();
}
}
protected RepositoryBase(IDbFactory dbFactory)
{
DbFactory = dbFactory;
_dbSet = DbContext.Set<T>();
}
public virtual void Add(T entity)
{
_dbSet.Add(entity);
}
public virtual void Update(T entity)
{
_dbSet.Attach(entity);
DbContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = _dbSet.Where<T>(where);
foreach (T obj in objects)
{
_dbSet.Remove(obj);
}
}
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual T GetById(string id)
{
return _dbSet.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return _dbSet.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).SingleOrDefault();
}
public virtual IQueryable<T> Query()
{
return _dbSet;
}
public virtual IQueryable<T> Query(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where);
}
public virtual ObjectQuery<U> CreateQuery<U>(string query, ObjectParameter[] parameters)
{
return CastAsObjectContext().CreateQuery<U>(query, parameters);
}
public virtual ObjectQuery<U> CreateQuery<U>(string query)
{
return CreateQuery<U>(query, new ObjectParameter[0] { });
}
public virtual ObjectQuery<DbDataRecord> CreateQuery(string query, ObjectParameter[] parameters)
{
return CreateQuery<DbDataRecord>(query, parameters);
}
public virtual ObjectQuery<DbDataRecord> CreateQuery(string query)
{
return CreateQuery<DbDataRecord>(query);
}
private ObjectContext CastAsObjectContext()
{
var oContext = (DbContext as IObjectContextAdapter).ObjectContext;
return oContext;
}
}
DBFactory.cs
class DbFactory : Disposable, IDbFactory
{
private PotSmartEntityModel1 _dbContext;
public PotSmartEntityModel1 Init()
{
return _dbContext ?? (_dbContext = new PotSmartEntityModel1());
}
protected override void DisposeCore()
{
if (_dbContext != null)
{
_dbContext.Dispose();
}
}
}
IDBFactory.cs
public interface IDbFactory : IDisposable
{
PotSmartEntityModel1 Init();
}
IRepository.cs
public interface IRepository
{
}
public interface IRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(int id);
T GetById(string id);
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
IQueryable<T> Query();
IQueryable<T> Query(Expression<Func<T, bool>> where);
ObjectQuery<U> CreateQuery<U>(string query, ObjectParameter[] parameters);
ObjectQuery<U> CreateQuery<U>(string query);
ObjectQuery<DbDataRecord> CreateQuery(string query, ObjectParameter[] parameters);
ObjectQuery<DbDataRecord> CreateQuery(string query);
}
Now, sorry for so much code but that way I get all the answer out there that some in the community may ask. The issue is on my controller, I use no default constructor and do the dependency injection like the following code show:
MarkerController.cs
public class MarkerController : Controller
{
private readonly IMarkerService _markerService;
public MarkerController(IMarkerService markerService)
{
_markerService = markerService;
}
// GET: Markers
public ActionResult Index()
{
return View();
}
}
When I run the code I keep getting the following error, and have tried anything I could think of.
StructureMap.StructureMapConfigurationException: 'No default Instance is registered and cannot be automatically determined for type 'PotSmart.Data.Interfaces.IDbFactory'
I know StructureMap does not always have the clearest error messages, but I feel this meant my Entity model was not being initialized at run time for some reason.It fails during "factory.GetInstance(serviceType)". Has anyone had this error, or does anyone see what I am obviously overlooking in the code sets I have above that would cause this issue? Thanks a million as always in advance.
I need to manipulate some data from a database via two different ways, Entity Framework and web service.
To simplify, lets say there are only two tables called A and B.
I am stuck on the design of this. Should I simply have two classes that derive from an interface that exposes the functions I want like so:
public interface IRepository
{
bool AddA(A a);
bool RemoveA(A a);
IEnumerable<A> GetAllA();
bool AddB(B b);
bool RemoveB(B b);
IEnumerable<B> GetAllB();
}
public class EfRepository : IRepository
{
//actual code here
}
public class ServiceRepository : IRepository
{
//actual code here
}
or should I try an approach which is more generic in nature:
public interface IRepository<T>
{
bool Add(T t);
bool Remove(T t);
IEnumerable<T> GetAll();
bool Update(T t);
}
public class EfARepository: IRepository<A>
{
//actual code here
}
public class EfBRepository : IRepository<B>
{
//actual code here
}
public class ServiceARepository: IRepository<A>
{
//actual code here
}
public class ServiceBRepository : IRepository<B>
{
//actual code here
}
The second approach seems cumbersome and repetitive since I am not really following the generic repository pattern because I'm not sure if its doable or worth the effort since Entity Framework already acts like a repository. Or would something like this be more sensible:
public class ARepository<Ef> : IRepository<A>
{
//omitted
}
//or this
public class EfRepository<A> : IRepository<A>
{
//omitted
}
But then again I can't wrap my head on injecting the context (Ef or service) into the classes and a repository of EF or vice versa doesn't really make much sense.
Please enlighten me and comment on the aforementioned designs and suggest a better approach or design for this scenario. Some examples in relation to this would be great!
I use the following approach in several projects. The example is simplified a little bit.
/// <summary>
/// THE base class for all entities.
/// </summary>
/// <typeparam name="TKey">The type of the key for the entity.</typeparam>
public abstract class Entity<TKey>
{
private TKey _id;
public Entity() : this(default(TKey))
{
}
public Entity(TKey id)
{
_id = id;
}
public Entity(Entity<TKey> source) : this(default(TKey))
{
if (source != null)
{
this._id = source._id;
}
}
public TKey Id
{
get { return _id; }
set { _id = value; }
}
public bool IsTransient()
{
return Id.Equals(default(TKey));
}
}
public interface IRepository : IDisposable
{
bool Exists();
void OpenConnection(); // helper
void CreateIfNotExists(); // helper
IQueryable<T> GetAll<T, TKey>() where T : Entity<TKey>;
IQueryable<T> GetAllIncluding<T, TKey>(params Expression<Func<T, object>>[] includeProperties) where T : Entity<TKey>;
IQueryable<T> SearchFor<T, TKey>(Expression<Func<T, bool>> predicate) where T : Entity<TKey>;
T GetById<T, TKey>(TKey id) where T : Entity<TKey>;
void Add<T, TKey>(T entity) where T : Entity<TKey>;
void Update<T, TKey>(T entity) where T : Entity<TKey>;
void Delete<T, TKey>(T entity) where T : Entity<TKey>;
void Delete<T, TKey>(TKey id) where T : Entity<TKey>;
void Save();
void Delete();
}
Then you derive all your entities from the Entity<TKey> base class and implement the repository.
public class MyRepository : IRepository
{
private DbContext _context;
public EFRepository(DbContext context)
{
if (context == null)
throw new ArgumentNullException("context");
_context = context;
}
public bool Exists()
{
return _context.Database.Exists();
}
...
public IQueryable<T> GetAll<T, TKey>() where T : Entity<TKey>
{
return _context.Set<T>();
}
public IQueryable<T> GetAllIncluding<T, TKey>(params Expression<Func<T, object>>[] includeProperties) where T : Entity<TKey>
{
IQueryable<T> query = _context.Set<T>();
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query;
}
public IQueryable<T> SearchFor<T, TKey>(Expression<Func<T, bool>> predicate) where T : Entity<TKey>
{
return _context.Set<T>().Where(predicate);
}
public T GetById<T, TKey>(TKey id) where T : Entity<TKey>
{
// use the static Equals method to accept null values
return _context.Set<T>().FirstOrDefault(x => object.Equals(id, x.Id));
}
public void Add<T, TKey>(T entity) where T : Entity<TKey>
{
if (entity != null)
{
Context.Set<T>().Add(entity); // new entity
}
}
public void Update<T, TKey>(T entity) where T : Entity<TKey>
{
if (entity != null)
{
if (object.Equals(entity.Id, default(TKey)))
Context.Set<T>().Add(entity); // new entity
else
Context.Entry<T>(entity).State = EntityState.Modified;
}
}
public void Save()
{
Context.SaveChanges();
}
...
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (_context != null)
_context.Dispose();
}
catch (Exception ex)
{
Debug.WriteLine("MyRepository.Dispose exception:" + ex);
}
}
}
}
public class MyUser : Entity<int>
{
public MyUser()
{
Name = null;
}
public MyUser(string user)
{
Name = user;
}
public MyUser(MyUser source) : base(source)
{
if (source != null)
{
Name = Helpers.SafeCopy(source.Name);
}
}
public string Name { get; set; }
}
I am trying to implement a repository pattern, however I don't understand how its possible when entities have id's of different types. Currently I have to fallback to using object, while I would really like to use the specific type of the id of the entity the repository is holding.
interface IEntity
{
object GetId();
}
class Foo : IEntity
{
private string id;
public Foo(string id)
{
this.id = id;
}
public object GetId()
{
return id;
}
}
class Bar : IEntity
{
private int id;
public Bar(int id)
{
this.id = id;
}
public object GetId()
{
return id;
}
}
class Repository<T> where T : IEntity
{
private Dictionary<object, T> entities = new Dictionary<object, T>();
public IEnumerable<T> List => entities.Values.AsEnumerable();
public void Add(T entity)
{
entities.Add(entity.GetId(), entity);
}
public T Get(object id)
{
return entities[id];
}
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo("0");
var bar = new Bar(0);
var fooRepo = new Repository<Foo>();
fooRepo.Add(foo);
fooRepo.Get(foo.GetId());
var barRepo = new Repository<Bar>();
barRepo.Add(bar);
barRepo.Get(bar.GetId());
}
}
I also tried something like:
class Repository<Id, Value> where Value : IEntity
{
private Dictionary<Id, Value> entities = new Dictionary<Id, Value>();
public IEnumerable<Value> List => entities.Values.AsEnumerable();
public void Add(Value entity) // But got stuck here, I don't want to pass in Id as separate parameter, I want it auto magically from the interface.
{
entities.Add(entity.GetId(), entity);
}
public Value Get(Id id)
{
return entities[id];
}
}
You can add a generic type of the key to IEntity. E.g.,
interface IEntity<TId>
{
TId GetId();
}
class Foo : IEntity<string>
{
private string id;
public Foo(string id)
{
this.id = id;
}
public string GetId()
{
return id;
}
}
class Repository<TEntity, TId> where TEntity : IEntity<TId>
{
private Dictionary<TId, TEntity> entities = new Dictionary<TId, TEntity>();
public void Add(TEntity entity)
{
entities.Add(entity.GetId(), entity);
}
public TEntity Get(TId id)
{
return entities[id];
}
}
Joel's solution could be extended to allow for variance. The same way generic collections implement both IEnumerable and IEnumerable<T>.
Entity
interface IEntity
{
object GetId();
}
interface IEntity<TId> : IEntity
{
new TId GetId();
}
abstract class EntityBase<TId> : IEntity<TId>
{
protected TId id;
protected EntityBase(TId id)
{
this.id = id;
}
public TId GetId() => id;
object IEntity.GetId() => GetId();
}
Repository
abstract class Repository
{
protected Dictionary<object, IEntity> entities;
protected Repository()
{
entities = new Dictionary<object, IEntity>();
}
public virtual void Add(IEntity entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
entities.Add(entity.GetId(), entity);
}
public virtual IEntity Get(object id)
{
if (id == null) throw new ArgumentNullException(nameof(id));
return entities[id];
}
}
abstract class Repository<TId, TEntity> : Repository
where TEntity : class, IEntity<TId>
{
protected Repository() : base() { }
public override void Add(IEntity entity)
{
Add((TEntity)entity);
}
public override IEntity Get(object id)
{
return Get((TId)id);
}
public void Add(TEntity entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
entities.Add(entity.GetId(), entity);
}
public TEntity Get(TId id)
{
if (id == null) throw new ArgumentNullException(nameof(id));
return (TEntity)entities[id];
}
}
Use Case
class Foo : EntityBase<string>
{
public Foo(string id) : base(id) { }
}
class Bar : EntityBase<int>
{
public Bar(int id) : base(id) { }
}
class FooRepository : Repository<string, Foo>
{
public FooRepository() { }
}
class BarRepository : Repository<int, Bar>
{
public BarRepository() { }
}
Test
[TestMethod]
public void IEntitySupport()
{
// use IEntity and object
IEntity bar = new Bar(1);
Repository barRepository = new BarRepository();
barRepository.Add(bar);
var bar2 = barRepository.Get((object)1);
Assert.AreSame(bar, bar2);
}
[TestMethod]
public void TEntitySupport()
{
// use TEntity and TId
var foo = new Foo("a");
var fooRepository = new FooRepository();
fooRepository.Add(foo);
var foo2 = fooRepository.Get("a");
Assert.AreSame(foo, foo2);
}
Here i implemented generic repository pattern for do the CRUD operation at the same time , i would like to do the search option as well, anyone can you tell me query to do generic search options, my code is below
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(object Id);
T Insert(T obj);
void Delete(object Id);
T Update(T obj);
void Save();
long Count();
}
public class Repository<T> : IRepository<T> where T : class
{
private PegasusPlusEntities context;
private DbSet<T> dbSet;
public Repository()
{
context = new PegasusPlusEntities();
dbSet = context.Set<T>();
}
public IEnumerable<T> GetAll()
{
return dbSet.ToList();
}
public T GetById(object id)
{
return dbSet.Find(id);
}
public T Insert(T obj)
{
dbSet.Add(obj);
Save();
return obj;
}
public void Delete(object id)
{
T entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public void Delete(T entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public T Update(T obj)
{
dbSet.Attach(obj);
context.Entry(obj).State = EntityState.Modified;
Save();
return obj;
}
public long Count()
{
return dbSet.Count();
}
public void Save()
{
try
{
context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
}
}
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (context != null)
{
context.Dispose();
context = null;
}
}
}
}
thanks in advance...
There are a few ways to do this but here is a simple example
Add a method to the current interface to allow for generic search
IEnumerable<T> Search(Expression<Func<T,bool>> predicate);
A simple implementation can look like this
public IEnumerable<T> Search(Expression<Func<T,bool>> predicate) {
return dbSet.Where(predicate).ToList();
}
An example of it in use would be
var repository = new Repository<Person>();
var searchResults = repository.Search(p => p.FirstName == "John" && p.LastName == "Doe");
I am mocking a DbContext for unit testing, and when you save changes in your database the instances you added pull the new id assigned by the database identity column, is there any way to mock this behavior?, I really have no clue where to start.
var acc = new Account {Name = "A New Account"};
_db.Accounts.Add(acc);
_db.SaveChanges();
Assert.IsTrue(acc.Id > 0);
Where
public class TestDbContext : IEntities
{
public DbSet<Instance> Accounts { get; set; } = new MockDbSet<Accounts>();
}
And
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace ControliApiTests.Data
{
public class MockDbSet<T> : DbSet<T>, IQueryable, IEnumerable<T> where T : class
{
readonly ObservableCollection<T> _data;
readonly IQueryable _queryable;
public MockDbSet()
{
_data = new ObservableCollection<T>();
_queryable = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from MockDbSet<T> and override Find");
}
public Task<T> FindAsync(CancellationToken cancellationToken, params object[] keyValues)
{
throw new NotImplementedException();
}
public override T Add(T item)
{
_data.Add(item);
return item;
}
public override IEnumerable<T> AddRange(IEnumerable<T> entities)
{
var addRange = entities as T[] ?? entities.ToArray();
foreach (var entity in addRange)
{
_data.Add(entity);
}
return addRange;
}
public override T Remove(T item)
{
_data.Remove(item);
return item;
}
public override T Attach(T item)
{
_data.Add(item);
return item;
}
public override T Create()
{
return Activator.CreateInstance<T>();
}
public override TDerivedEntity Create<TDerivedEntity>()
{
return Activator.CreateInstance<TDerivedEntity>();
}
public override ObservableCollection<T> Local
{
get { return _data; }
}
Type IQueryable.ElementType
{
get { return _queryable.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _queryable.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return new AsyncQueryProviderWrapper<T>(_queryable.Provider); }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
internal class AsyncQueryProviderWrapper<T> : IDbAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal AsyncQueryProviderWrapper(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new AsyncEnumerableQuery<T>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new AsyncEnumerableQuery<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));
}
}
public class AsyncEnumerableQuery<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>
{
public AsyncEnumerableQuery(IEnumerable<T> enumerable) : base(enumerable)
{
}
public AsyncEnumerableQuery(Expression expression) : base(expression)
{
}
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new AsyncEnumeratorWrapper<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
}
public class AsyncEnumeratorWrapper<T> : IDbAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public AsyncEnumeratorWrapper(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; }
}
}
}
If you define
private static int IdentityCounter = 1;
in your mock implementation and increment it by one for each added item, you will get an incrementing value that does not reset as long as the app domain exists.
If your tests allow for multi-threaded adds, use Interlocked.Increment to update the counter.
Note that your current implementation does not demand that an object have an Id property. If all of the classes in the test have such a property, you can define an interface to use rather than allowing anything that is class.
public interface DbEntity
{
int Id { get; set; }
}
public class MockDbSet<T> : DbSet<T>, IQueryable, IEnumerable<T> where T : DbEntity
With that change, your implementation of Add could look like
public override T Add(T item)
{
item.Id = IdentityCounter++; // Or use Interlocked.Increment to support multithreading
_data.Add(item);
return item;
}
If you don't want use a interface can use reflection and extension method for take and evaluate the id
var MockSet = new Mock<DbSet<T>>();
MockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(MockData.AsQueryable().Provider);
MockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(MockData.AsQueryable().Expression);
MockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(MockData.AsQueryable().ElementType);
MockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => MockData.AsQueryable().GetEnumerator());
MockSet.Setup(m => m.Add(It.IsAny<T>())).Callback<T>(MockData.AddPlus); // here change te method 'Add' for the extension method 'AddPlus'
public static void AddPlus<T>(this List<T> miLista, T item)
{
int nuevoId;
int? id;
try
{
id = (int)item.GetPropValue("id");
}
catch
{
id = null;
}
if (id == 0)
{
if (miLista.Count() > 0)
{
var listaInts = miLista.Select(i => (int)i.GetPropValue("id"));
nuevoId = listaInts.Max(x=>x) + 1;
}
else
nuevoId = 1;
item.SetPropValue("id",nuevoId);
}
miLista.Add(item);
}