I'm making a full repository in C#/ASP.NET with the Entity framework but at the moment I'm afraid I'm overlooking something like disposing my ObjectContexts. In the following lines of codes you'll see my full repository (atleast what is needed for you guys to understand my problem) and I hope someone is kind enough to look through it and tell me if I made some mistakes.
This project is very very important for me but I'm new to the repository/EF models.
Global.asax
public class Global : System.Web.HttpApplication
{
private WebObjectContextStorage _storage;
public override void Init()
{
base.Init();
_storage = new WebObjectContextStorage(this);
}
protected void Application_Start(object sender, EventArgs e)
{
}
protected void Session_Start(object sender, EventArgs e)
{
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
ObjectContextInitializer.Instance().InitializeObjectContextOnce(() =>
{
ObjectContextManager.InitStorage(_storage);
});
}
protected void Application_EndRequest(object sender, EventArgs e)
{
}
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
}
protected void Application_Error(object sender, EventArgs e)
{
}
protected void Session_End(object sender, EventArgs e)
{
}
protected void Application_End(object sender, EventArgs e)
{
}
}
ObjectContextManager
public static class ObjectContextManager
{
public static void InitStorage(IObjectContextStorage storage)
{
if (storage == null)
{
throw new ArgumentNullException("storage");
}
if ((Storage != null) && (Storage != storage))
{
throw new ApplicationException("A storage mechanism has already been configured for this application");
}
Storage = storage;
}
/// <summary>
/// The default connection string name used if only one database is being communicated with.
/// </summary>
public static readonly string DefaultConnectionStringName = "TraceConnection";
/// <summary>
/// Used to get the current object context session if you're communicating with a single database.
/// When communicating with multiple databases, invoke <see cref="CurrentFor()" /> instead.
/// </summary>
public static ObjectContext Current
{
get
{
return CurrentFor(DefaultConnectionStringName);
}
}
/// <summary>
/// Used to get the current ObjectContext associated with a key; i.e., the key
/// associated with an object context for a specific database.
///
/// If you're only communicating with one database, you should call <see cref="Current" /> instead,
/// although you're certainly welcome to call this if you have the key available.
/// </summary>
public static ObjectContext CurrentFor(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
if (Storage == null)
{
throw new ApplicationException("An IObjectContextStorage has not been initialized");
}
ObjectContext context = null;
lock (_syncLock)
{
context = Storage.GetObjectContextForKey(key);
if (context == null)
{
context = ObjectContextFactory.GetTraceContext(key);
Storage.SetObjectContextForKey(key, context);
}
}
return context;
}
/// <summary>
/// This method is used by application-specific object context storage implementations
/// and unit tests. Its job is to walk thru existing cached object context(s) and Close() each one.
/// </summary>
public static void CloseAllObjectContexts()
{
foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
{
if (ctx.Connection.State == System.Data.ConnectionState.Open)
ctx.Connection.Close();
}
}
/// <summary>
/// An application-specific implementation of IObjectContextStorage must be setup either thru
/// <see cref="InitStorage" /> or one of the <see cref="Init" /> overloads.
/// </summary>
private static IObjectContextStorage Storage { get; set; }
private static object _syncLock = new object();
}
ObjectContextInitializer
public class ObjectContextInitializer
{
private static readonly object syncLock = new object();
private static ObjectContextInitializer instance;
protected ObjectContextInitializer() { }
private bool isInitialized = false;
public static ObjectContextInitializer Instance()
{
if (instance == null)
{
lock (syncLock)
{
if (instance == null)
{
instance = new ObjectContextInitializer();
}
}
}
return instance;
}
/// <summary>
/// This is the method which should be given the call to intialize the ObjectContext; e.g.,
/// ObjectContextInitializer.Instance().InitializeObjectContextOnce(() => InitializeObjectContext());
/// where InitializeObjectContext() is a method which calls ObjectContextManager.Init()
/// </summary>
/// <param name="initMethod"></param>
public void InitializeObjectContextOnce(Action initMethod)
{
lock (syncLock)
{
if (!isInitialized)
{
initMethod();
isInitialized = true;
}
}
}
}
ObjectContextFactory
public static class ObjectContextFactory
{
/// <summary>
/// Gets the TraceContext
/// </summary>
/// <param name="connectionString">Connection string to use for database queries</param>
/// <returns>The TraceContext</returns>
public static TraceContext GetTraceContext(string configName)
{
string connectionString = ConfigurationManager.ConnectionStrings[configName].ConnectionString;
return new TraceContext(connectionString);
}
}
WebObjectContextStorage
public class WebObjectContextStorage : IObjectContextStorage
{
public WebObjectContextStorage(HttpApplication app)
{
app.EndRequest += (sender, args) =>
{
ObjectContextManager.CloseAllObjectContexts();
HttpContext.Current.Items.Remove(HttpContextObjectContextStorageKey);
};
}
public ObjectContext GetObjectContextForKey(string key)
{
ObjectContextStorage storage = GetObjectContextStorage();
return storage.GetObjectContextForKey(key);
}
public void SetObjectContextForKey(string factoryKey, ObjectContext session)
{
ObjectContextStorage storage = GetObjectContextStorage();
storage.SetObjectContextForKey(factoryKey, session);
}
public IEnumerable<ObjectContext> GetAllObjectContexts()
{
ObjectContextStorage storage = GetObjectContextStorage();
return storage.GetAllObjectContexts();
}
private ObjectContextStorage GetObjectContextStorage()
{
HttpContext context = HttpContext.Current;
ObjectContextStorage storage = context.Items[HttpContextObjectContextStorageKey] as ObjectContextStorage;
if (storage == null)
{
storage = new ObjectContextStorage();
context.Items[HttpContextObjectContextStorageKey] = storage;
}
return storage;
}
private static readonly string HttpContextObjectContextStorageKey = "HttpContextObjectContextStorageKey";
}
ObjectContextStorage
public class ObjectContextStorage : IObjectContextStorage
{
private Dictionary<string, ObjectContext> storage = new Dictionary<string, ObjectContext>();
/// <summary>
/// Initializes a new instance of the <see cref="SimpleObjectContextStorage"/> class.
/// </summary>
public ObjectContextStorage() { }
/// <summary>
/// Returns the object context associated with the specified key or
/// null if the specified key is not found.
/// </summary>
/// <param name="key">The key.</param>
/// <returns></returns>
public ObjectContext GetObjectContextForKey(string key)
{
ObjectContext context;
if (!this.storage.TryGetValue(key, out context))
return null;
return context;
}
/// <summary>
/// Stores the object context into a dictionary using the specified key.
/// If an object context already exists by the specified key,
/// it gets overwritten by the new object context passed in.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="objectContext">The object context.</param>
public void SetObjectContextForKey(string key, ObjectContext objectContext)
{
this.storage.Add(key, objectContext);
}
/// <summary>
/// Returns all the values of the internal dictionary of object contexts.
/// </summary>
/// <returns></returns>
public IEnumerable<ObjectContext> GetAllObjectContexts()
{
return this.storage.Values;
}
}
GenericRepository
public class GenericRepository : IRepository
{
private readonly string _connectionStringName;
private ObjectContext _objectContext;
private readonly PluralizationService _pluralizer = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en"));
private bool _usePlurazation;
/// <summary>
/// Initializes a new instance of the <see cref="GenericRepository<TEntity>"/> class.
/// </summary>
public GenericRepository()
: this(string.Empty, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericRepository<TEntity>"/> class.
/// </summary>
/// <param name="connectionStringName">Name of the connection string.</param>
public GenericRepository(string connectionStringName, bool usePlurazation)
{
this._connectionStringName = connectionStringName;
this._usePlurazation = usePlurazation;
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericRepository"/> class.
/// </summary>
/// <param name="objectContext">The object context.</param>
public GenericRepository(ObjectContext objectContext, bool usePlurazation)
{
if (objectContext == null)
throw new ArgumentNullException("objectContext");
this._objectContext = objectContext;
this._usePlurazation = usePlurazation;
}
public TEntity GetByKey<TEntity>(object keyValue) where TEntity : class
{
EntityKey key = GetEntityKey<TEntity>(keyValue);
object originalItem;
if (ObjectContext.TryGetObjectByKey(key, out originalItem))
{
return (TEntity)originalItem;
}
return default(TEntity);
}
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return ObjectContext.CreateQuery<TEntity>(entityName).OfType<TEntity>();
}
public IQueryable<TEntity> GetQuery<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
return GetQuery<TEntity>().Where(predicate);
}
public IQueryable<TEntity> GetQuery<TEntity>(ISpecification<TEntity> specification) where TEntity : class
{
return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>());
}
public IEnumerable<TEntity> Get<TEntity>(Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
{
if (sortOrder == SortOrder.Ascending)
{
return GetQuery<TEntity>().OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
}
return GetQuery<TEntity>().OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
}
public IEnumerable<TEntity> Get<TEntity>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
{
if (sortOrder == SortOrder.Ascending)
{
return GetQuery<TEntity>().Where(predicate).OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
}
return GetQuery<TEntity>().Where(predicate).OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
}
public IEnumerable<TEntity> Get<TEntity>(ISpecification<TEntity> specification, Expression<Func<TEntity, string>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
{
if (sortOrder == SortOrder.Ascending)
{
return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>()).OrderBy(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
}
return specification.SatisfyingEntitiesFrom(GetQuery<TEntity>()).OrderByDescending(orderBy).Skip(pageIndex).Take(pageSize).AsEnumerable();
}
public TEntity Single<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
{
return GetQuery<TEntity>().SingleOrDefault<TEntity>(criteria);
}
public TEntity Single<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
{
return criteria.SatisfyingEntityFrom(GetQuery<TEntity>());
}
public TEntity First<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
return GetQuery<TEntity>().FirstOrDefault(predicate);
}
public TEntity First<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
{
return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>()).FirstOrDefault();
}
public void Add<TEntity>(TEntity entity) where TEntity : class
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
ObjectContext.AddObject(GetEntityName<TEntity>(), entity);
}
public void Attach<TEntity>(TEntity entity) where TEntity : class
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
ObjectContext.AttachTo(GetEntityName<TEntity>(), entity);
}
public void Delete<TEntity>(TEntity entity) where TEntity : class
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
ObjectContext.DeleteObject(entity);
}
public void Delete<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
{
IEnumerable<TEntity> records = Find<TEntity>(criteria);
foreach (TEntity record in records)
{
Delete<TEntity>(record);
}
}
public void Delete<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
{
IEnumerable<TEntity> records = Find<TEntity>(criteria);
foreach (TEntity record in records)
{
Delete<TEntity>(record);
}
}
public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
{
return GetQuery<TEntity>().AsEnumerable();
}
public void Update<TEntity>(TEntity entity) where TEntity : class
{
var fqen = GetEntityName<TEntity>();
object originalItem;
EntityKey key = ObjectContext.CreateEntityKey(fqen, entity);
if (ObjectContext.TryGetObjectByKey(key, out originalItem))
{
ObjectContext.ApplyCurrentValues(key.EntitySetName, entity);
}
}
public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
{
return GetQuery<TEntity>().Where(criteria);
}
public TEntity FindOne<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
{
return GetQuery<TEntity>().Where(criteria).FirstOrDefault();
}
public TEntity FindOne<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
{
return criteria.SatisfyingEntityFrom(GetQuery<TEntity>());
}
public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
{
return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>());
}
public int Count<TEntity>() where TEntity : class
{
return GetQuery<TEntity>().Count();
}
public int Count<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
{
return GetQuery<TEntity>().Count(criteria);
}
public int Count<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
{
return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>()).Count();
}
public IUnitOfWork UnitOfWork
{
get
{
if (unitOfWork == null)
{
unitOfWork = new UnitOfWork(this.ObjectContext);
}
return unitOfWork;
}
}
private ObjectContext ObjectContext
{
get
{
if (this._objectContext == null)
{
if (string.IsNullOrEmpty(this._connectionStringName))
{
this._objectContext = ObjectContextManager.Current;
}
else
{
this._objectContext = ObjectContextManager.CurrentFor(this._connectionStringName);
}
}
return this._objectContext;
}
}
private EntityKey GetEntityKey<TEntity>(object keyValue) where TEntity : class
{
var entitySetName = GetEntityName<TEntity>();
var objectSet = ObjectContext.CreateObjectSet<TEntity>();
var keyPropertyName = objectSet.EntitySet.ElementType.KeyMembers[0].ToString();
var entityKey = new EntityKey(entitySetName, new[] { new EntityKeyMember(keyPropertyName, keyValue) });
return entityKey;
}
private string GetEntityName<TEntity>() where TEntity : class
{
// WARNING! : Exceptions for inheritance
if (_usePlurazation)
{
return string.Format("{0}.{1}", ObjectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));
}
else
{
return string.Format("{0}.{1}", ObjectContext.DefaultContainerName, typeof(TEntity).Name);
}
}
private IUnitOfWork unitOfWork;
}
I know it will take some time to read through the code, but it would help me miles is somebody looks at it and gives tips on what to do better or where I do not dispose an object.
Also I got a little question: "I would like to put a business layer above this repository, that would keep things like global.asax the same I guess but would need static classes(right?) like a BookProvider which gives me all the data about my book-entities?
Thanks in advance!
The only concrete remark I can give is about disposing the context:
foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
{
if (ctx.Connection.State == System.Data.ConnectionState.Open)
ctx.Connection.Close();
}
ObjectContext implements IDisposable, so the standard way would be in my opinion:
foreach (ObjectContext ctx in Storage.GetAllObjectContexts())
ctx.Dispose();
As far as I know ObjectContext.Dispose() just closes the connection, so it does the same what you are doing. But I'd consider this as an internal implementation detail which might potentially change between EF releases.
Your generic repository is one as there are many of this kind. A few points which came to my mind when looking at the methods:
Since you are exposing IQueryable in public IQueryable<TEntity> GetQuery<TEntity>(...) why do you need most of the other methods like Single, First, Count, etc.? (Why not Any, etc.?) You get all this from your IQueryable.
Your Update method only works for scalar properties. But this is a common problem with generic repositories. There is no easy solution or no solution at all for updating entities in a generic way.
What is your goal you want to reach by using the repository pattern at all? If you have unit testability with an in-memory data store in mind, you cannot expose IQueryable because LINQ to Entities and LINQ to Objects are not the same. To test if your IQueryables work you need integration tests and the real database your application is supposed to work with in production. But if you don't expose IQueryable your repository needs a lot of business specific methods which return the results as POCOs, POCO collections or DTOs of projected/selected properties and hide the internal query specifications so that you can mock these methods with in-memory data to test your business logic. But this is the point where a generic repository is not sufficient anymore. (How are you going to write, for instance, a LINQ Join where more than one Entity/ObjectSet is involved in a repository which has only one entity type as generic parameter?)
If you ask ten people how their repositories are architectured, you'll get ten different answers. And none is really wrong or the best because it depends on the application you are going to build with this repository. I believe that nobody can tell you what your repository is really worth. You will see it in practice when you start writing an application. For some applications it might be over-architectured (which I consider as most dangerous because managing and keeping a meaningless architecture under control is expensive and a waste of time you lose for writing real application content). And for other needs you will likely have to extend the repository. For example:
How do you handle explicit loading or queries on navigation properties of an entity (with CreateSourceQuery or DbEntityEntry.Collection/Reference in EF 4.1)? If your application never needs explicite loading: Fine. If it's necessary you need to extend your repo.
How do you control eager loading? Sometimes perhaps you just want a parent entity. Sometimes you want to Includ the children1 collection and sometimes the child2 reference.
How do you set the entity state of an entity manually? Perhaps you never must. But in the next application it might be very helpful.
How do you detach an entity from the context manually?
How do you control loading behaviour (with or without tracking of entities by the context)?
How do you control manually lazy loading behaviour and creation of change tracking proxies?
How do you create an entity proxy manually? You might need it in some situations when you work with lazy loading or change tracking proxies.
How do you load entities into the context without building a result collection? Yet another repository method, perhaps... or perhaps not. Who knows in advance what your application logic will require.
And so on and so forth...
Related
Following the book "Artificial Intelligence - A Modern Approach" I am trying to make search algorithm implementations (DFS, A*, etc.) that can work with different problems (path from A to B, sliding-block puzzle, etc.) instead of just the one specific problem.
public abstract class Problem
{
protected Problem(object initialState)
{
InitialState = initialState;
}
public object InitialState { get; }
/// <summary>
/// Checks if the state is the goal state
/// </summary>
public abstract bool IsGoalState(object state);
/// <summary>
/// Returns the actions available from the state
/// </summary>
public abstract ISet<Action> Actions(object state);
/// <summary>
/// Returns the state that results after performing the action on the state
/// </summary>
public abstract object ResultState(object state, Action action);
/// <summary>
/// Returns the cost of action to reach from state to reachedState
/// </summary>
/// <param name="state">The state from which the action will be performed</param>
/// <param name="action">One of the actions available in the state</param>
/// <param name="reachedState">The state that results after performing the action</param>
public virtual int StepCost(object state, Action action, object reachedState)
{
return 1;
}
}
_
public class Node
{
public Node(object state)
{
State = state;
PathCost = 0;
}
/// <summary>
/// </summary>
/// <param name="action">The action that was applied to the parent to generate the node</param>
/// <param name="stepCost">The cost from the parent node to this node</param>
public Node(object state, Node parent, Action action, int stepCost)
: this(state)
{
Parent = parent;
Action = action;
if (Parent != null)
PathCost = Parent.PathCost + stepCost;
}
public object State { get; }
public Node Parent { get; }
/// <summary>
/// The action that was applied to the parent to generate the node
/// </summary>
public Action Action { get; }
/// <summary>
/// The cost of the path from the initial statee to the node
/// </summary>
public int PathCost { get; }
public bool IsRootNode => Parent == null;
public IEnumerable<Node> PathFromRoot()
{
var path = new Stack<Node>();
var node = this;
while (!node.IsRootNode)
{
path.Push(node);
node = node.Parent;
}
path.Push(node); // root
return path;
}
}
_
public abstract class Action
{
/// <summary>
/// true if it is a "No Operation" action
/// </summary>
public virtual bool IsNoOp()
{
return false;
}
public string Name => GetType().Name;
public override string ToString()
{
return Name;
}
}
public class NoOp : Action
{
public override bool IsNoOp()
{
return true;
}
}
public abstract class EightPuzzleAction : Action
{
}
/// <summary>
/// Move the blank tile to the left
/// </summary>
public class MoveLeft : EightPuzzleAction
{
}
// the same MoveRight, MoveTop, MoveBottom
In order to do that I can implement methods from this base Problem class and pass that concrete instance to a search algorithm (that accepts Problem).
class EightPuzzleProblem : Problem
{
private readonly int[,] _goalState =
{
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
};
public EightPuzzleProblem(int[,] initialState) : base(initialState)
{ }
public override bool IsGoalState(object state)
{
var puzzleState = (int[,]) state;
......... <check if puzzleState is the same as _goalState>
}
............
}
class DepthFirstSearch
{
public IEnumerable<Action> Search(Problem p)
{
return DfsRecursive(p, new Node(p.InitialState));
}
public IEnumerable<Action> DfsRecursive(Problem p, Node node)
{
if (p.IsGoalState(node.State))
return node.PathFromRoot();
.......<simple DFS implementation>
}
}
// usage example
var dfs = new DepthFirstSearch();
var result = dfs.Search(new EightPuzzleProblem(initialState));
if (!result.Any)
// fail
if (result.Count == 1 && result[0].IsNoOp)
// already at goal
foreach (var act in result)
{
if (act is MoveLeft) .....
}
But I see one inconvenience with my class design: in the concrete class implementation code I need to have a lot of casts from object. Is there any way to avoid that?
If I make the Problem class generic and inherit ConcreteProblem from Problem<Something> then I will not be able to use it via Problem...
Why don't you inject your GoalState too and make GameState also pluggable using the interface like below. In this way you can implement any operation you want to do on two States in the concrete class and Problem class just needs to call those functions. Also you can have different types of states too.
public abstract class Problem<T> where T:IGameState
{
protected Problem(T initialState)
{
InitialState = initialState;
}
public T InitialState { get; set; }
/// <summary>
/// Checks if the state is the goal state
/// </summary>
public abstract bool IsGoalState(T state);
}
public class EightPuzzleProblem<T> : Problem<T> where T : IGameState
{
private readonly T _goalState;
public EightPuzzleProblem(T initialState, T goalState)
: base(initialState)
{
_goalState = goalState;
}
public override bool IsGoalState(T state)
{
return _goalState.IsEqual(state);
}
}
public interface IGameState
{
bool IsEqual(IGameState state);
}
public class EightPuzzleGameState : IGameState
{
private readonly int[,] _goalState =
{
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
};
public bool IsEqual(IGameState state)
{
//Compare state with _goalState
}
}
You could actually make Problem generic with the generic param set to the actual state.
public abstract class Problem<T> where T : IState{
public abstract bool IsGoalState(T state);
}
Within your derived classes you can now simply derive from Problem passing the actual type of IState as generic parameter:
class MyDerived : Problem<MyState> {
public override bool IsGoalState(MyState state) { ... }
}
This question already has answers here:
Generic dependency injection with Unity
(2 answers)
Closed 6 years ago.
I have an interface, ICrudService, and a service that implements the interface, CrudService. I am trying to use Unity for dependency injection purposes, but I can't see to figure out how to register the type.
Here is my ICrudService:
public interface ICrudService<T> where T: DelEntity, new()
{
int Create(T item);
void Save();
T Get(int id);
IEnumerable<T> GetAll();
IEnumerable<T> Where(Expression<Func<T, bool>> func, bool showDeleted = false);
}
Here is my CrudService:
public class CrudService<T> : ICrudService<T> where T : DelEntity, new ()
{
protected IRepo<T> repo;
public CrudService(IRepo<T> repo)
{
this.repo = repo;
}
public IEnumerable<T> GetAll()
{
return repo.GetAll();
}
public T Get(int id)
{
return repo.Get(id);
}
public virtual int Create(T item)
{
var newItem = repo.Insert(item);
repo.Save();
return newItem.Id;
}
public void Save()
{
repo.Save();
}
public virtual void Delete(int id)
{
repo.Delete(repo.Get(id));
repo.Save();
}
public void Restore(int id)
{
repo.Restore(repo.Get(id));
repo.Save();
}
public void BatchDelete(int[] ids)
{
foreach (var id in ids)
{
repo.Get(id).IsDeleted = true;
}
repo.Save();
}
public void BatchRestore(int[] ids)
{
foreach (var id in ids)
{
repo.Get(id).IsDeleted = false;
}
repo.Save();
}
public IEnumerable<T> Where(Expression<Func<T, bool>> predicate, bool showDeleted = false)
{
return repo.Where(predicate, showDeleted);
}
}
And finally, my UnitConfig.cs, which I can't even get to build...
public class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Gets the configured Unity container.
/// </summary>
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
#endregion
/// <summary>Registers the type mappings with the Unity container.</summary>
/// <param name="container">The unity container to configure.</param>
/// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to
/// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your types here
container.RegisterType<ICrudService<T>, CrudService<T>>();
}
}
What am I doing wrong in the UnityConfig.cs?
What you are getting is a C# compiler error stating that T is unknown. This is the way to register generic types in Unity:
container.RegisterType(typeof(ICrudService<>), typeof(CrudService<>));
Following is the implementation of repository pattern using EF6. I want to use the generic repository in the following way thereby removing the need to create repository for every type
_unitOfWork.Repository(entityName).Insert(entityName)
public interface IRepository<T> where T : class
{
//--Search Operations
IEnumerable<T> Get();
T GetByID(object id);
void Insert(T entity);
void Delete(object id);
void Delete(T entityToDelete);
void Update(T entityToUpdate);
}
I have created a base class that all entities will inherit. The intent of this bases class is so that i can refer all entities with this base class.
public class IRepositoryEntity
{
}
Following is the Entity Framework generated code, I have inherited this entity from IRepositoryEntity
public partial class Client_Entity: IRepositoryEntity
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Client_Entity()
{
}
public int Id { get; set; }
}
Following is the generic repository
public class GenericRepository<IRepositoryEntity> : IRepository<IRepositoryEntity> where IRepositoryEntity : class
{
#region Private member variables...
internal ConsularDB_March2016Entities Context;
internal DbSet<IRepositoryEntity> DbSet;
#endregion
#region Public Constructor...
/// <summary>
/// Public Constructor,initializes privately declared local variables.
/// </summary>
/// <param name="context"></param>
public GenericRepository(ConsularDB_March2016Entities context)
{
this.Context = context;
this.DbSet = context.Set<IRepositoryEntity>();
}
///// <summary>
///// Public Constructor,initializes privately declared local variables.
///// </summary>
///// <param name="context"></param>
//public GenericRepository()
//{
// this.Context = context;
// this.DbSet = context.Set<IRepositoryEntity>();
//}
#endregion
#region Public member methods...
/// <summary>
/// generic Get method for Entities
/// </summary>
/// <returns></returns>
public IEnumerable<IRepositoryEntity> Get()
{
IQueryable<IRepositoryEntity> query = DbSet;
return query.ToList();
}
}
Following is the IUnitOfWork interface and its implementation
public interface IUnitOfWork
{
#region Properties
GenericRepository<Client_Entity> ClientRepository { get; }
#endregion
#region Public methods
/// <summary>
/// Save method.
/// </summary>
void Save();
GenericRepository<IRepositoryEntity> Repository(IRepositoryEntity entity);
#endregion
}
/// <summary>
/// Unit of Work class responsible for DB transactions
/// </summary>
public class UnitOfWork : IDisposable, IUnitOfWork
{
#region Private member variables...
private March2016Entities _context = null;
private GenericRepository<Client_Entity> _clientRepository;
#endregion
public UnitOfWork()
{
_context = new March2016Entities();
}
#region Public member methods...
/// <summary>
/// Save method.
/// </summary>
public void Save()
{
try
{
_context.Configuration.ValidateOnSaveEnabled = false;
_context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
var context = ((IObjectContextAdapter)_context).ObjectContext;
var refreshobjects = _context.ChangeTracker.Entries().Select(c => c.Entity).ToList();
context.Refresh(System.Data.Entity.Core.Objects.RefreshMode.StoreWins, refreshobjects);
_context.SaveChanges();
}
catch (DbEntityValidationException e)
{
var outputLines = new List<string>();
foreach (var eve in e.EntityValidationErrors)
{
outputLines.Add(string.Format("{0}: Entity of type \"{1}\" in state \"{2}\" has the following validation errors:", DateTime.Now, eve.Entry.Entity.GetType().Name, eve.Entry.State));
foreach (var ve in eve.ValidationErrors)
{
outputLines.Add(string.Format("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage));
}
}
System.IO.File.AppendAllLines(#"C:\errors.txt", outputLines);
throw e;
}
}
#endregion
#region Implementing IDiosposable...
#region private dispose variable declaration...
private bool disposed = false;
#endregion
/// <summary>
/// Protected Virtual Dispose method
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
Debug.WriteLine("UnitOfWork is being disposed");
_context.Dispose();
}
}
this.disposed = true;
}
/// <summary>
/// Dispose method
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region GenericMembers
Dictionary<string, GenericRepository<IRepositoryEntity>> repostories = new Dictionary<string, GenericRepository<IRepositoryEntity>>();
/// <summary>
/// Generic Repository method which checks the repository is available if not,
/// it sets it up.
/// </summary>
/// <param name="entity">Entity</param>
/// <returns>Repository to use.</returns>
public GenericRepository<IRepositoryEntity> Repository(IRepositoryEntity entity)
{
string index = entity.GetType().ToString();
if (!repostories.ContainsKey(index))
{
//Reflections to create the repoositiory if it is not needed.
Type type1 = typeof(GenericRepository<IRepositoryEntity>);
Type[] typeArgs = { entity.GetType() };
Type constructed = type1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed, this._context);
//if (o is GenericRepository<IRepositoryEntity>)
//{
object genericRepo = (object)o;
var rep = (GenericRepository<IRepositoryEntity>)genericRepo;
//rep.Context = this._context;
repostories.Add(index, rep);
//}
}
return this.repostories[index];
}
#endregion
}
I am trying to create a list of GenericRepository dynamically in the following function
public GenericRepository<IRepositoryEntity> Repository(IRepositoryEntity entity)
while creating the instance, it is throwing the following exception
An exception of type 'System.InvalidOperationException' occurred in mscorlib.dll but was not handled in user code
Additional information: GenericRepository`1[IRepositoryEntity] is not a GenericTypeDefinition. MakeGenericType may only be called on a type for which Type.IsGenericTypeDefinition is true.
I understand the exception, however not sure on how can I change the implementation so that i am able to achieve the desired functionality. Also how can I add IRepositoryEntity to EF model because it says it is not part of the model.
public void ValidateGenericEntityInsert(IRepositoryEntity entityName)
{
IRepositoryEntity entity = (IRepositoryEntity)Activator.CreateInstance(entityName);
_unitOfWork.Repository(entityName).Insert(entityName);
// int generatedId = GetPropertyFromEntity(entity, en => en.Id);
int generatedId = (int)GetPropertyFromEntity(entityName, "Id");
Assert.That(generatedId, Is.GreaterThan(0));
}
I am working on a project where I plan on using Redis as persistent data storage, however the task at hand, I am working on a generic Object cache. and as a huge fan of LINQ I have started designing a cache which does support this.
public ConcurrentBag<Object> Cache = new ConcurrentBag<object>();
public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
var tmp = new List<T>();
foreach (var i in Cache)
{
try
{
T obj = i as T;
if (obj != null)
tmp.Add(obj);
}
catch { }
}
return tmp.Where(predicate).ToList();
}
I am afraid that when the object cache grows large it will become inefficient. (I estimate 500k-1m objects)
I was hoping that it would be possible to use something like this
public ConcurrentBag<Object> Cache = new ConcurrentBag<object>();
public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
return Cache.Where<T>(predicate).ToList();
}
Hopefully I am not all off-track here? Any suggestions are welcome :)
Hash your genric type and save list of specific type..
Something like:
Dictionary<Type,List<T>>
Then get value by type key and query as you wanted
Since you estimate a lot of items in the cache and the operations on the cache will be type specific, you could use multiple bags wrapped into a dictionary. That would speed up finding the subset of the cache of type of interest and would be ideal if the cache contained many minimal subsets of different types.
readonly IDictionary<Type, ConcurrentBag<object>> _cache = new ConcurrentDictionary<Type, ConcurrentBag<object>>();
public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
// Check if items of type {T} exist in the cache.
ConcurrentBag<object> bag;
if (_cache.TryGetValue(typeof (T), out bag))
{
// Cast, apply predicate and return.
return bag.Cast<T>().Where(predicate).ToList();
}
// Return an empty list.
return new List<T>();
}
Of course now you also need to handle adding items properly to the cache to ensure that different types will be put into their corresponding bags.
Big thanks to both Discosultan and user1190916 Whom both pointed me in the right direction in what I needed to get a CRUD cached-object repository with full LINQ support using Redis for persistent storage (Client ServiceStack.Redis), this is what I have managed to conjure up thus far.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Collections.Concurrent;
using ServiceStack.Redis;
namespace RedisTestRepo
{
class Program
{
//public static DataRepository Db;
static void Main(string[] args)
{
Repo r = new Repo();
// We do not touch sequence, by running example we can see that sequence will give Users new unique Id.
// Empty data store.
Console.WriteLine("Our User Data store should be empty.");
Console.WriteLine("Users In \"Database\" : {0}\n", r.All<User>().Count);
// Add imaginary users.
Console.WriteLine("Adding 100 imaginairy users.");
for (int i = 0; i < 99; i++)
r.Create<User>(new User { Id = r.Next<User>(), Name = "Joachim Nordvik" });
// We should have 100 users in data store.
Console.WriteLine("Users In \"Database\" : {0}\n", r.All<User>().Count);
// Lets print 10 users from data store.
Console.WriteLine("Order by Id, Take (10) and print users.");
foreach (var u in r.All<User>().OrderBy(z => z.Id).Take(10))
{
Console.WriteLine("ID:{0}, Name: {1}", u.Id, u.Name);
// Lets update an entity.
u.Name = "My new Name";
r.Update<User>(x=>x.Id == u.Id, u);
}
// Lets print 20 users from data store, we already edited 10 users.
Console.WriteLine("\nOrder by Id, Take (20) and print users, we previously edited the users that we printed lets see if it worked.");
foreach (var u in r.All<User>().OrderBy(z => z.Id).Take(20))
{
Console.WriteLine("ID:{0}, Name: {1}", u.Id, u.Name);
}
// Clean up data store.
Console.WriteLine("\nCleaning up Data Store.\n");
foreach (var u in r.All<User>())
r.Delete<User>(u);
// Confirm that we no longer have any users.
Console.WriteLine("Confirm that we no longer have User entities in Data Store.");
Console.WriteLine("Users In \"Database\" : {0}\n\n", r.All<User>().Count);
Console.WriteLine("Hit return to exit!");
Console.Read();
}
}
public class Repo
{
private static readonly PooledRedisClientManager m = new PooledRedisClientManager();
public Repo()
{
// Spool Redis Database into our object cache.
LoadIntoCache<User>();
}
readonly IDictionary<Type, List<object>> _cache = new ConcurrentDictionary<Type, List<object>>();
/// <summary>
/// Load {T} into object cache from Data Store.
/// </summary>
/// <typeparam name="T">class</typeparam>
private void LoadIntoCache<T>() where T : class
{
_cache[typeof(T)] = GetAll<T>().Cast<object>().ToList();
}
/// <summary>
/// Add single {T} into cache and Data Store.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="entity">class object</param>
public void Create<T>(T entity) where T : class
{
List<object> list;
if (!_cache.TryGetValue(typeof(T), out list))
{
list = new List<object>();
}
list.Add(entity);
_cache[typeof(T)] = list;
Store<T>(entity);
}
/// <summary>
/// Delete single {T} from cache and Data Store.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="entity">class object</param>
public void Delete<T>(T entity) where T : class
{
List<object> list;
if (_cache.TryGetValue(typeof(T), out list))
{
list.Remove(entity);
_cache[typeof(T)] = list;
RedisDelete<T>(entity);
}
}
/// <summary>
/// Tries to update or Add entity to object cache and Data Store.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="predicate">linq expression</param>
/// <param name="entity">entity</param>
public void Update<T>(Func<T, bool> predicate, T entity) where T : class
{
List<object> list;
if (_cache.TryGetValue(typeof(T), out list))
{
// Look for old entity.
var e = list.Cast<T>().Where(predicate).FirstOrDefault();
if(e != null)
{
list.Remove(e);
}
// Regardless if object existed or not we add it to our Cache / Data Store.
list.Add(entity);
_cache[typeof(T)] = list;
Store<T>(entity);
}
}
/// <summary>
/// Find List<T>(predicate) in cache.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="predicate">linq statement</param>
/// <returns></returns>
public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
List<object> list;
if (_cache.TryGetValue(typeof(T), out list))
{
return list.Cast<T>().Where(predicate).ToList();
}
return new List<T>();
}
/// <summary>
/// Find All {T}
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>List<T></returns>
public List<T> All<T>() where T : class
{
return GetAll<T>().ToList();
}
/// <summary>
/// Find Single {T} in object cache.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="predicate">linq statement</param>
/// <returns></returns>
public T Read<T>(Func<T, bool> predicate) where T : class
{
List<object> list;
if (_cache.TryGetValue(typeof(T), out list))
{
return list.Cast<T>().Where(predicate).FirstOrDefault();
}
return null;
}
public long Next<T>() where T : class
{
long id = 1;
using (var ctx = m.GetClient())
{
try
{
id = ctx.As<T>().GetNextSequence();
}
catch(Exception ex)
{
// Add exception handler.
}
}
return id;
}
private void RedisDelete<T>(T entity) where T : class
{
using (var ctx = m.GetClient())
ctx.As<T>().Delete(entity);
}
private T Find<T>(long id) where T : class
{
using (var ctx = m.GetClient())
return ctx.As<T>().GetById(id);
}
private IList<T> GetAll<T>() where T : class
{
using(var ctx = m.GetClient())
{
try
{
return ctx.As<T>().GetAll();
}
catch
{
return new List<T>();
}
}
}
private void Store<T>(T entity) where T : class
{
using (var ctx = m.GetClient())
ctx.Store<T>(entity);
}
}
public class User
{
public long Id { get; set; }
public string Name { get; set; }
}
}
I have the following interfaces/class:
public interface IUnitOfWork : IDisposable
{
event EventHandler<EventArgs> Saved;
DbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
void Commit();
}
And an implementation of a repository:
public class CachedSqlRepository<T, TKey, TContext> : ICacheRepository<T, TKey, TContext>
where T : class
where TContext : DbContext, IDisposable, new()
{
//A list of the Navigation Properties to include
private readonly Expression<Func<T, object>>[] _NavigationProperties;
public CachedSqlRepository(params Expression<Func<T, object>>[] navigationProperties)
{
_NavigationProperties = navigationProperties;
using (TContext dbContext = new TContext()) //Fetch the List of Entities
{
RefreshCache(dbContext);
}
}
/// <summary>
/// The Collection of Items in the database
/// Note this is a Cache, but should replicate whats in the DB
/// </summary>
public IList<T> Items { get; private set; }
public bool Any(Func<T, bool> predicate)
{
return Items.Any(predicate);
}
public void RefreshCache(DbContext context)
{
switch (_NavigationProperties.Length)
{
case 0:
Items = context.Set<T>().ToList();
break;
case 1:
Items = context.Set<T>().Include(_NavigationProperties[0]).ToList();
break;
//more here
}
}
/// <summary>
/// Refresh the internal cache
/// </summary>
public void RefreshCache()
{
using (TContext dbContext = new TContext())
{
RefreshCache(dbContext);
}
}
public IEnumerable<T> FilterBy(Func<T, bool> predicate)
{
return Items.Where(predicate);
}
public T Add(T entity)
{
T newEntity;
using (TContext dbContext = new TContext())
{
newEntity = dbContext.Set<T>().Add(entity);
if (dbContext.SaveChanges() == 1) //1 change was made
Items.Add(newEntity);
}
return newEntity;
}
public void Delete(TKey id)
{
using (TContext dbContext = new TContext())
{
var attachedEntry = dbContext.Set<T>().Find(id);
if (attachedEntry == null) return; //it doesnt exist anyway!
dbContext.Set<T>().Remove(attachedEntry);
dbContext.SaveChanges();
RefreshCache(dbContext);
}
}
public void Update(T entity, TKey id)
{
if (entity == null) throw new ArgumentException("Cannot update a null entity.");
using (TContext dbContext = new TContext())
{
var entry = dbContext.Entry(entity);
if (entry.State != EntityState.Detached) return;
T attachedEntity = dbContext.Set<T>().Find(id);
if (attachedEntity != null)
{
var attachedEntry = dbContext.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // This should attach entity
}
dbContext.SaveChanges();
RefreshCache(dbContext);
}
}
#region Transaction Methods
public IUnitOfWork StartTransaction()
{
return new EFUnitOfWork(new TContext());
}
public T TransactionAdd(T entity, IUnitOfWork context)
{
context.Saved += OnSave;
return context.Set<T>().Add(entity);
}
public void TransactionDelete(TKey id, IUnitOfWork context)
{
var attachedEntry = context.Set<T>().Find(id);
if (attachedEntry == null) return; //it doesnt exist anyway
context.Saved += OnSave;
context.Set<T>().Remove(attachedEntry);
}
public void TransactionUpdate(T entity, TKey id, IUnitOfWork context)
{
if (entity == null) throw new ArgumentException("Cannot update a null entity.");
var entry = context.Entry(entity);
if (entry.State != EntityState.Detached) return;
T attachedEntity = context.Set<T>().Find(id);
if (attachedEntity != null)
{
var attachedEntry = context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // This should attach entity
}
context.Saved += OnSave;
}
private void OnSave(object sender, EventArgs e)
{
RefreshCache();
}
#endregion
}
It is adapted from various patterns on the net. I don't expect this to be useful for tables with hundreds of thousands of rows, but for lookup tables etc - I'm not always hitting the DB.
It works, but some things aren't super clean, for example where I refresh the cache - sometimes I have to pull all the data again (currently a work in progress).
Is this sound design? Or am I reinventing the wheel here?
An interesting question +1. In my view, context content caching is one that is best done properly or left well alone. And use DB caching.
Why:
Parallel WPs all have a cache
Each WP may have threads, the context is not thread safe
Should each thread have a cache?
Is your cache session persistent?
No: you reload each request
Yes: you use global caching on ASP.NET, EnterpriseLibary cache or similar?
Are you managing the cache correctly?
How do you deal with concurrency and changes
Have you considered best practice around Context lifetime? Some experts suggest short lifetime only
Is DB located on LAN near WebServer?
Have you compared response times when using DB buffer access?
Having looked into this topic in various environments, not just EF/.NET/SQL Server, I have come to the conclusion that unless the DB server has become or is tending to be the CPU bottleneck and can't be easily scaled, it is a very reasonable approach to supply memory to DB and let it cache 100sMB
before building or trying to cache entries.
I would rather throw GBs or RAM at SQL Server before coding myself in app knots on WebServer.
When every microsecond counts, or your DB is separated on a network span with latency/throughput issues and your data is non volatile and needs no cache expiry/concurrency management. Then go ahead and implement caching.
Consider memory use, cache construction time and memory persistency model carefully.
Look at some tools made for caching for ideas and as potential solutions. e.g. Enterprise Caching Block.
Good Luck.