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);
}
Related
I currently have a method that allows me to generate an IEnumerable object for my dropdown list in my web app forms.
Example of current code :
//Name with Id
StateListDp = _db.States.ToDropdownList(c => c.Name, c => Convert.ToString(c.Id, CultureInfo.InvariantCulture));
//Description with Id
StatusesListDp = _db.Statuses.ToDropdownList(c => c.Description, c => Convert.ToString(c.Id, CultureInfo.InvariantCulture));
I've just implemented Repository Design Pattern.
I'm able to convert it over to this now :
StateListDp = unitOfWork.State.GetDropDownList().ToList();
StatusesListDp = unitOfWork.Status.GetDropDownList().ToList();
I've created the following support class (I've excluded the unitOfWork for now )
public class StatusRepository : Repository<Statuses>, IStatusRepository
{
private readonly TenDDbContext context;
public StatusRepository(TenDDbContext context): base(context)
{
this.context = context;
}
public IEnumerable<Statuses> GetAllActive()
{
return Find(x => x.IsActive == true);
}
public IEnumerable<SelectListItem> GetDropDownList()
{
return GetAllActive()
.ToDropdownList(c => c.Description, c => Convert.ToString(c.Id, CultureInfo.InvariantCulture));
}
}
public class StateRepository : Repository<States>, IStateRepository
{
private readonly TenDDbContext context;
public StateRepository(TenDDbContext context): base(context)
{
this.context = context;
}
public IEnumerable<States> FindAllActive(Expression<Func<States, bool>> predicate)
{
return Find(predicate).Where(x => x.IsActive == true);
}
public IEnumerable<States> GetAllActive()
{
return Find(x => x.IsActive == true);
}
public IEnumerable<SelectListItem> GetDropDownList()
{
return GetAllActive()
.ToDropdownList(c => c.Name, c => Convert.ToString(c.Id, CultureInfo.InvariantCulture));
}
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly TenDDbContext context;
public Repository(TenDDbContext context)
{
this.context = context;
}
public TEntity Add(TEntity entity)
{
return context.Set<TEntity>().Add(entity).Entity;
}
//public bool save(TEntity entity)
//{
// var test= Add(entity);
// test.
//}
public void AddRange(IEnumerable<TEntity> entities)
{
context.Set<TEntity>().AddRange(entities);
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return context.Set<TEntity>().Where(predicate);
}
public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return context.Set<TEntity>().SingleOrDefault(predicate);
}
public TEntity Get(int id)
{
return context.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return context.Set<TEntity>().ToList();
}
public TEntity Update(TEntity entity)
{
//context.Attach(entity).State = EntityState.Modified;
// context.Attach(entity);
//return context.Entry(entity).State = EntityState.Modified;
return context.Update(entity)
.Entity;
}
public void Remove(TEntity entity)
{
context.Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
context.Set<TEntity>().RemoveRange(entities);
}
}
So my issue is that I have 10 or so tables that I will need to retrieve this very similar data from.
I'm wondering there is a way to make the GetDropDownList a generic method?
So that I can limit the amount of repeat code ...
I'm even willing to make it two methods
GetDropDownNameList and GetDropDownDescriptionList
adding dropdownextension method
public static IEnumerable<SelectListItem> ToDropdownList<T>(this IEnumerable<T> items,
Func<T, string> text, Func<T, string> value = null, Func<T, Boolean> selected = null)
{
var listData = items.Select(p => new SelectListItem
{
Text = text.Invoke(p),
Value = (value == null ? text.Invoke(p) : value.Invoke(p)),
Selected = selected != null && selected.Invoke(p)
});
var defaultRow = new SelectListItem() { Value = "0", Text = "Please Select One", Selected = V };
var newList = listData.Prepend(defaultRow);
//return new SelectList(newList, "Value", "Text");
return newList;
}
passing the selection of the props for description and key should do the trick. If you want to make it even more generic to be inside the IRepository, replace GetAllActive() with a filter
public IEnumerable<SelectListItem> GetDropDownList(Expression<Func<TEntity, string>> predicateDescription, Expression<Func<TEntity, string>> predicateKey)
{
return GetAllActive().ToDropdownList(predicateDescription, predicateKey);
}
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'm trying to create an abstract generic manager which would give events for added/removed and provide abstract method for creating and finalizing an item.
Below is the code I've come up with, however in TestClass constructor passing TestManager for a ManagerBase argument to base produces compiler error for invalid cast even though TestManager is certainly a ManagerBase.
It is possible to make the Manager field an "object" and cast in at run time, but that'd be pretty ugly.
Is it possible to make it work, or perhaps I'm in the wrong direction?
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ManagerTest
{
class Program
{
static void Main(string[] args)
{
var Manager = new TestManager();
Manager.ItemAdded += (manager, item) => Debug.WriteLine("Item added: " + item.Id);
Manager.ItemRemoved += (manager, item) => Debug.WriteLine("Item removed: " + item.Id);
var entity1 = Manager.Create("entity1");
var entity2 = Manager.Create("entity2");
Manager.Remove("entity1");
var entity3 = Manager.Create("entity3");
Manager.Remove("entity3");
Manager.Remove("entity4");
}
}
class TestClass : EntityBase, IDisposable
{
public TestClass(string id, TestManager manager) : base(id, manager) { Debug.WriteLine(Id + " - ctor"); }
public void Dispose() { Debug.WriteLine(Id + " - disposed"); }
}
class TestManager : ManagerBase<TestClass>
{
protected override TestClass CreateInternal(string key) { return new TestClass(key, this); }
protected override void FinalizeRemove(TestClass item) { item.Dispose(); }
}
abstract class EntityBase
{
public string Id { get; private set; }
public ManagerBase<EntityBase> Manager { get; private set; }
public EntityBase(string id, ManagerBase<EntityBase> manager)
{
this.Id = id;
this.Manager = manager;
}
}
abstract class ManagerBase<T> where T : EntityBase
{
public event Action<ManagerBase<T>, T> ItemAdded;
public event Action<ManagerBase<T>, T> ItemRemoved;
private readonly Dictionary<string, T> Storage = new Dictionary<string, T>();
protected abstract T CreateInternal(string key);
protected abstract void FinalizeRemove(T item);
public T Create(string key)
{
T newItem = null;
lock (Storage)
{
if (!Storage.ContainsKey(key))
{
newItem = CreateInternal(key);
Storage.Add(key, newItem);
ItemAdded.SafeInvoke(this, newItem);
}
}
return newItem;
}
public T Get(string key)
{
lock (Storage)
return Storage.ContainsKey(key) ? Storage[key] : null;
}
public bool Contains(string key)
{
lock (Storage)
return Storage.ContainsKey(key);
}
public bool Remove(string key)
{
bool returnValue = false;
lock (Storage)
if (Storage.ContainsKey(key))
{
var item = Storage[key];
returnValue = Storage.Remove(key);
ItemRemoved.SafeInvoke(this, item);
FinalizeRemove(item);
}
return returnValue;
}
}
static class Extensions
{
public static void SafeInvoke<T1, T2>(this Action<T1, T2> action, T1 arg1, T2 arg2)
{
var actionCopy = action;
if (actionCopy != null)
actionCopy(arg1, arg2);
}
}
}
The corrected code
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ManagerTest
{
class Program
{
static void Main(string[] args)
{
var Manager = new TestManager();
Manager.ItemAdded += (manager, item) => Debug.WriteLine("Item added: " + item.Id);
Manager.ItemRemoved += (manager, item) => Debug.WriteLine("Item removed: " + item.Id);
var entity1 = Manager.Create("entity1");
var entity2 = Manager.Create("entity2");
Manager.Remove("entity1");
var entity3 = Manager.Create("entity3");
Manager.Remove("entity3");
Manager.Remove("entity4");
}
}
class TestClass : EntityBase, IDisposable
{
public TestClass(string id, TestManager manager) : base(id, manager) { Debug.WriteLine(Id + " - ctor"); }
public void Dispose() { Debug.WriteLine(Id + " - disposed"); }
}
class TestManager : ManagerBase<TestClass>, IManagerBase<TestClass>
{
protected override TestClass CreateInternal(string key) { return new TestClass(key, this); }
protected override void FinalizeRemove(TestClass item) { item.Dispose(); }
}
abstract class EntityBase
{
public string Id { get; private set; }
public IManagerBase<EntityBase> Manager { get; private set; }
public EntityBase(string id, IManagerBase<EntityBase> manager)
{
this.Id = id;
this.Manager = manager;
}
}
interface IManagerBase<out T>
where T : EntityBase
{
event Action<IManagerBase<T>, T> ItemAdded;
event Action<IManagerBase<T>, T> ItemRemoved;
T Create(string key);
T Get(string key);
bool Contains(string key);
bool Remove(string key);
}
abstract class ManagerBase<T> : IManagerBase<T> where T : EntityBase
{
public event Action<IManagerBase<T>, T> ItemAdded;
public event Action<IManagerBase<T>, T> ItemRemoved;
private readonly Dictionary<string, T> Storage = new Dictionary<string, T>();
protected abstract T CreateInternal(string key);
protected abstract void FinalizeRemove(T item);
public T Create(string key)
{
T newItem = null;
lock (Storage)
{
if (!Storage.ContainsKey(key))
{
Storage.Add(key, CreateInternal(key));
ItemAdded.SafeInvoke(this, newItem);
}
}
return newItem;
}
public T Get(string key)
{
lock (Storage)
return Storage.ContainsKey(key) ? Storage[key] : null;
}
public bool Contains(string key)
{
lock (Storage)
return Storage.ContainsKey(key);
}
public bool Remove(string key)
{
bool returnValue = false;
lock (Storage)
if (Storage.ContainsKey(key))
{
var item = Storage[key];
returnValue = Storage.Remove(key);
ItemRemoved.SafeInvoke(this, item);
FinalizeRemove(item);
}
return returnValue;
}
}
static class Extensions
{
public static void SafeInvoke<T1, T2>(this Action<T1, T2> action, T1 arg1, T2 arg2)
{
var actionCopy = action;
if (actionCopy != null)
actionCopy(arg1, arg2);
}
}
}
The problem is with the invariance of the T parameter in ManagerBase. You can't assign ManagerBase<Derived> to ManagerBase<Base>. You would be able to do that if the parameter was covariant, but this can be done inly in interfaces, not in classes. So maybe try to make the managerbase an interface? Like IManager<out T>
This article explains covariance/contravariance stuff quite well: http://tomasp.net/blog/variance-explained.aspx/
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