What I'm trying to do is to create a class with static methods to manage rights of different user types on some types of resources (which are NHibernate entity objects). Specifically I'd like to check the current principal (in an asp.net MVC project) against an object id, to see if he can view or edit an entity. The signature I have in mind is the following:
PermissionManager.CanView<TEntity>(object id);
By now I've done these steps:
1) an interface like this:
public interface ICanAccessQuery<TAccount, TEntity>
where TAccount : IAccountOwner
{
bool CanView(TAccount user, object entityKey);
bool CanEdit(TAccount user, object entityKey);
}
2) some implementations like this one:
public class TeacherCanAccessCourseReportsQuery : ICanAccessQuery<Teacher, CourseReport>
{
public bool CanView(Teacher user, object entityKey)
{
var predicate = PredicateBuilder.Create<CourseReport>(x => x.Id == (long)entityKey);
var conditions = PredicateBuilder.Create<CourseReport>(x => x.Teacher.Id == user.Id);
conditions = conditions.Or(x => x.Teacher.Tutor.Id == user.Id);
conditions = conditions.Or(x => x.CoachingTeachers.Any(t => t.Id == user.Id));
predicate = predicate.And(conditions);
return RepositoryProvider.Get<CourseReport>().Count(predicate) > 0;
}
public bool CanEdit(Teacher user, object entityKey)
{
// similar implementation
}
}
3) a static Configure() method inside my PermissionManager class, to be called in Global.asax:
public static IDictionary<string, object> _permissions = new Dictionary<string, object>();
public static void Configure()
{
_permissions.Add(typeof(Teacher).Name + typeof(CourseReport).Name, new TeacherCanAccessCourseReportsQuery());
}
4) inside the PermissionManager class:
public static bool CanView<TEntity>(object primaryKey, params string[] enabledRoles)
{
var accounts = RepositoryProvider.Get<Account, AccountRepository>();
var principal = Thread.CurrentPrincipal as MyCustomPrincipal;
if (enabledRoles.Any(r => principal.IsInRole(r)))
return true;
IAccountOwner user = accounts.GetUser(principal.AccountId);
var can = false;
var #switch = new Dictionary<Type, Action> {
{ typeof(Teacher), () => can = CanView<Teacher, TEntity>(user as Teacher, primaryKey) },
{ typeof(TrainingCenter), () => can = CanView<TrainingCenter, TEntity>(user as TrainingCenter, primaryKey) }
};
#switch[user.GetType()]();
return can;
}
private static bool CanView<TAccount, TEntity>(TAccount user, object primaryKey)
where TAccount : IAccountOwner
{
var key = typeof(TAccount).Name + typeof(TEntity).Name;
if (_permissions.ContainsKey(key))
{
return (((ICanAccessQuery<TAccount, TEntity>)_permissions[key]).CanView(user, primaryKey);
}
return false;
}
The same methods would be defined for CanEdit... perfectly identical except the method name to be called.
What I'm asking is: is there a better way to define what I have in mind, in a more OOP-way?
I've implemented a better solution that maybe can be interesting for someone.
This is the interface of a "Can I access?" query:
public interface ICanAccessQuery<TAccount, TEntity>
where TAccount : IAccountOwner
where TEntity : IStoredEntity
{
bool CanView(TAccount user, TEntity entity);
bool CanEdit(TAccount user, TEntity entity);
}
My entities now implement an empty interface IStoredEntity to make a constraint.
And this is an example of implementation:
public class TeacherCanAccessOrdersQuery : ICanAccessQuery<Teacher, Order>
{
public bool CanView(Teacher user, Order entity)
{
var predicate = PredicateBuilder.Create<Order>(x => x.Id == entity.Id && x => x.Account.Id == user.Account.Id);
return RepositoryProvider.Get<Order>().Count(predicate) > 0;
}
public bool CanEdit(Teacher user, Order entity)
{
// similar implementation
}
}
Finally, my new AuthorizationProvider class (changed name from PermissionManager, didn't like it):
public class AuthorizationProvider
{
public enum Abilities
{
View,
Edit
};
private static IDictionary<string, object> _authorizations = new Dictionary<string, object>();
// this method should be called at application bootstrap, such as Global.asax in an asp.net app
public static void Configure()
{
_authorizations.Add(typeof(Teacher).Name + typeof(CourseReport).Name, new TeacherCanAccessCourseReportsQuery());
_authorizations.Add(typeof(Teacher).Name + typeof(Order).Name, new TeacherCanAccessOrdersQuery());
// other rules user type-entity type
}
// Can I view entity with primary key X?
public static bool CanI<TEntity>(Abilities ability, object entityKey)
where TEntity : IStoredEntity
{
TEntity entity = RepositoryProvider.Get<TEntity>().Load(entityKey);
return CanI<TEntity>(ability, entity, AccountRoles.Admin);
}
// Can I view entity (and if I have a specific role, I surely can)?
public static bool CanI<TEntity>(Abilities ability, TEntity entity, params string[] authorizedRoles)
where TEntity : IStoredEntity
{
var principal = Thread.CurrentPrincipal as MyCustomPrincipal;
if (authorizedRoles.Any(r => principal.IsInRole(r)))
return true;
var user = RepositoryProvider.Get<Account, AccountRepository>().GetUser(principal.AccountId);
// my system has only two types of users
if (user is Teacher)
{
return Can<Teacher, TEntity>(user as Teacher, ability, entity);
}
else if (user is TrainingCenter)
{
return Can<TrainingCenter, TEntity>(user as TrainingCenter, ability, entity);
}
return false;
}
/// Can user X (view|edit) entity Y?
/// With some reflection I call the needed method. In this way I can add "abilities" to my ICanAccessQuery
/// interface and its implementations without altering this class.
public static bool Can<TAccount, TEntity>(TAccount user, Abilities ability, TEntity entity)
where TAccount : IAccountOwner
where TEntity : IStoredEntity
{
var key = typeof(TAccount).Name + typeof(TEntity).Name;
if (_authorizations.ContainsKey(key))
{
var query = (ICanAccessQuery<TAccount, TEntity>)_authorizations[key];
string methodName = "Can" + ability.ToString();
var method = typeof(ICanAccessQuery<TAccount, TEntity>).GetMethod(methodName);
return (bool)method.Invoke(query, new object[] { user, entity });
}
return false;
}
}
An example of use in an asp.net mvc controller:
public ActionResult Details(long? id)
{
if (!id.HasValue)
return new EmptyResult();
if (!AuthorizationProvider.CanI<CourseReport>(AuthorizationProvider.Abilities.View, id.Value))
return RedirectToAccessDenied();
// etc.
}
Related
I'm trying to create a unit test for my service with a mocked DbContext. I created an interface IDbContext with the following functions:
public interface IDbContext : IDisposable
{
IDbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
int SaveChanges();
}
My real context implements this interface IDbContext and DbContext.
Now I'm trying to mock the IDbSet<T> in the context, so it returns a List<User> instead.
[TestMethod]
public void TestGetAllUsers()
{
// Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new List<User>
{
new User { ID = 1 }
});
UserService userService = new UserService(mock.Object);
// Act
var allUsers = userService.GetAllUsers();
// Assert
Assert.AreEqual(1, allUsers.Count());
}
I always get this error on .Returns:
The best overloaded method match for
'Moq.Language.IReturns<AuthAPI.Repositories.IDbContext,System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>.Returns(System.Func<System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>)'
has some invalid arguments
I managed to solve it by creating a FakeDbSet<T> class that implements IDbSet<T>
public class FakeDbSet<T> : IDbSet<T> where T : class
{
ObservableCollection<T> _data;
IQueryable _query;
public FakeDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public T Detach(T item)
{
_data.Remove(item);
return item;
}
public T Create()
{
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public ObservableCollection<T> Local
{
get { return _data; }
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
Now my test looks like this:
[TestMethod]
public void TestGetAllUsers()
{
//Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new FakeDbSet<User>
{
new User { ID = 1 }
});
UserService userService = new UserService(mock.Object);
// Act
var allUsers = userService.GetAllUsers();
// Assert
Assert.AreEqual(1, allUsers.Count());
}
In case anyone is still interested, I was having the same problem and found this article very helpful:
Entity Framework Testing with a Mocking Framework (EF6 onwards)
It only applies to Entity Framework 6 or newer, but it covers everything from simple SaveChanges tests to async query testing all using Moq (and a few of manual classes).
Thank you Gaui for your great idea =)
I did add some improvements to your solution and want to share it.
My FakeDbSet also inherents from DbSet to get additional methods
like AddRange()
I replaced the ObservableCollection<T> with List<T> to pass all
the already implemented methods in List<> up to my FakeDbSet
My FakeDbSet:
public class FakeDbSet<T> : DbSet<T>, IDbSet<T> where T : class {
List<T> _data;
public FakeDbSet() {
_data = new List<T>();
}
public override T Find(params object[] keyValues) {
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public override T Add(T item) {
_data.Add(item);
return item;
}
public override T Remove(T item) {
_data.Remove(item);
return item;
}
public override T Attach(T item) {
return null;
}
public T Detach(T item) {
_data.Remove(item);
return item;
}
public override T Create() {
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T {
return Activator.CreateInstance<TDerivedEntity>();
}
public List<T> Local {
get { return _data; }
}
public override IEnumerable<T> AddRange(IEnumerable<T> entities) {
_data.AddRange(entities);
return _data;
}
public override IEnumerable<T> RemoveRange(IEnumerable<T> entities) {
for (int i = entities.Count() - 1; i >= 0; i--) {
T entity = entities.ElementAt(i);
if (_data.Contains(entity)) {
Remove(entity);
}
}
return this;
}
Type IQueryable.ElementType {
get { return _data.AsQueryable().ElementType; }
}
Expression IQueryable.Expression {
get { return _data.AsQueryable().Expression; }
}
IQueryProvider IQueryable.Provider {
get { return _data.AsQueryable().Provider; }
}
IEnumerator IEnumerable.GetEnumerator() {
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator() {
return _data.GetEnumerator();
}
}
It is very easy to modify the dbSet and Mock the EF Context Object:
var userDbSet = new FakeDbSet<User>();
userDbSet.Add(new User());
userDbSet.Add(new User());
var contextMock = new Mock<MySuperCoolDbContext>();
contextMock.Setup(dbContext => dbContext.Users).Returns(userDbSet);
Now it is possible to execute Linq queries, but be a aware that foreign key references may not be created automatically:
var user = contextMock.Object.Users.SingeOrDefault(userItem => userItem.Id == 42);
Because the context object is mocked the Context.SaveChanges() won't do anything and property changes of your entites might not be populated to your dbSet. I solved this by mocking my SetModifed() method to populate the changes.
Based on this MSDN article, I've created my own libraries for mocking DbContext and DbSet:
EntityFrameworkMock - GitHub
EntityFrameworkMockCore - GitHub
Both available on NuGet and GitHub.
The reason I've created these libraries is because I wanted to emulate the SaveChanges behavior, throw a DbUpdateException when inserting models with the same primary key and support multi-column/auto-increment primary keys in the models.
In addition, since both DbSetMock and DbContextMock inherit from Mock<DbSet> and Mock<DbContext>, you can use all features of the Moq framework.
Next to Moq, there also is an NSubstitute implementation.
Usage with the Moq version looks like this:
public class User
{
[Key, Column(Order = 0)]
public Guid Id { get; set; }
public string FullName { get; set; }
}
public class TestDbContext : DbContext
{
public TestDbContext(string connectionString)
: base(connectionString)
{
}
public virtual DbSet<User> Users { get; set; }
}
[TestFixture]
public class MyTests
{
var initialEntities = new[]
{
new User { Id = Guid.NewGuid(), FullName = "Eric Cartoon" },
new User { Id = Guid.NewGuid(), FullName = "Billy Jewel" },
};
var dbContextMock = new DbContextMock<TestDbContext>("fake connectionstring");
var usersDbSetMock = dbContextMock.CreateDbSetMock(x => x.Users, initialEntities);
// Pass dbContextMock.Object to the class/method you want to test
// Query dbContextMock.Object.Users to see if certain users were added or removed
// or use Mock Verify functionality to verify if certain methods were called: usersDbSetMock.Verify(x => x.Add(...), Times.Once);
}
If anyone is still looking for answers I've implemented a small library to allow mocking DbContext.
step 1
Install Coderful.EntityFramework.Testing nuget package:
Install-Package Coderful.EntityFramework.Testing
step 2
Then create a class like this:
internal static class MyMoqUtilities
{
public static MockedDbContext<MyDbContext> MockDbContext(
IList<Contract> contracts = null,
IList<User> users = null)
{
var mockContext = new Mock<MyDbContext>();
// Create the DbSet objects.
var dbSets = new object[]
{
MoqUtilities.MockDbSet(contracts, (objects, contract) => contract.ContractId == (int)objects[0] && contract.AmendmentId == (int)objects[1]),
MoqUtilities.MockDbSet(users, (objects, user) => user.Id == (int)objects[0])
};
return new MockedDbContext<SourcingDbContext>(mockContext, dbSets);
}
}
step 3
Now you can create mocks super easily:
// Create test data.
var contracts = new List<Contract>
{
new Contract("#1"),
new Contract("#2")
};
var users = new List<User>
{
new User("John"),
new User("Jane")
};
// Create DbContext with the predefined test data.
var dbContext = MyMoqUtilities.MockDbContext(
contracts: contracts,
users: users).DbContext.Object;
And then use your mock:
// Create.
var newUser = dbContext.Users.Create();
// Add.
dbContext.Users.Add(newUser);
// Remove.
dbContext.Users.Remove(someUser);
// Query.
var john = dbContext.Users.Where(u => u.Name == "John");
// Save changes won't actually do anything, since all the data is kept in memory.
// This should be ideal for unit-testing purposes.
dbContext.SaveChanges();
Full article: http://www.22bugs.co/post/Mocking-DbContext/
I'm late, but found this article helpful: Testing with InMemory (MSDN Docs).
It explains how to use an in memory DB context (which is not a database) with the benefit of very little coding and the opportunity to actually test your DBContext implementation.
I have a User class that has a GetQueryable method. Another method, Select(), calls GetQueryable(). I want to use the Select method without passing the type User to the Select method, because I have it in this but I can't use it.
Type type = this.GetType();
???
var x = this.GetQueryable< ??? >().ToList();
class Program
{
static void Main(string[] args)
{
var acc = new User();
acc.Select();
}
}
public partial class User
{
public DB_Test001Entities context;
public User()
{
context = new DB_Test001Entities();
}
public void Select()
{
Type type = this.GetType();
var x = this.GetQueryable< **???** >().ToList();
}
public IQueryable<TEntity> GetQueryable<TEntity>(List<string> includes = null) where TEntity : class
{
IQueryable<TEntity> items = context.Set<TEntity>();
if (includes != null && includes.Any())
includes.Where(i => i != null).ToList().ForEach(i => { items = items.Include(i); });
return items;
}
}
You can do it using reflection. The following sample works smoothly. In program you can use Clerk or Manager, just any instance derived from User to call Select. You can improve your program with this.
class Clerk : User { }
class Manager : User { }
internal class User
{
public User() { }
public string Name { get; set; }
public void Select()
{
var list = new List<string>() {"Jack", "Martin"};
Type thisType = GetType();
MethodInfo method = thisType.GetMethod("GetQueryable").MakeGenericMethod(thisType);
method.Invoke(this, new object[] {list});
}
public IQueryable<TEntity> GetQueryable<TEntity>(List<string> includes = null) where TEntity : User, new()
{
if(includes != null)
{
Console.WriteLine(typeof(TEntity));
var entity = new List<TEntity>(includes.Count);
entity.AddRange(includes.Select(item => new TEntity {Name = item}));
return entity.AsQueryable();
}
return null;
}
}
class Program
{
static void Main()
{
User usr = new Manager();
usr.Select();
}
}
I'm using EF code first for developing my 3 layer WinForm Application, I used disconnected POCOs as my model entities. All my entities inherited from BaseEntity class.
I used disconnected POCOs, so I handle entity's State on client side, and in ApplyChanges() method, I attach my entity graph(e.g An Order with it's OrderLines and Products) to my DbContext and then synch each entity's State with it's client side State.
public class BaseEntity
{
int _dataBaseId = -1;
public virtual int DataBaseId // DataBaseId override in each entity to return it's key
{
get { return _dataBaseId; }
}
public States State { get; set; }
public enum States
{
Unchanged,
Added,
Modified,
Deleted
}
}
So, when I want to save a graph of related entities, I used following methods:
public static EntityState ConvertState(BaseEntity.States state)
{
switch (state)
{
case BaseEntity.States.Added:
return EntityState.Added;
case BaseEntity.States.Modified:
return EntityState.Modified;
case BaseEntity.States.Deleted:
return EntityState.Deleted;
default:
return EntityState.Unchanged;
}
}
public void ApplyChanges<TEntity>(TEntity root) where TEntity : BaseEntity
{
_dbContext.Set<TEntity>().Add(root);
foreach (var entry in _dbContext.ChangeTracker
.Entries<BaseEntity>())
{
BaseEntity stateInfo = entry.Entity;
entry.State = ConvertState(stateInfo.State);
}
}
But if my graph contains 2 or more entities with the same key i give this error :
An object with the same key already exists in the ObjectStateManager...
How can i detect entities with the same keys in my graph(root) and make them unique in my ApplyChanges() method?
There is a way to search the database and check if a record with that same primary key already exists, I don't know if that's what you are looking for, but the code is below:
public static class ObjectSetExtensions
{
#region Constants
private const BindingFlags KeyPropertyBindingFlags =
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
#endregion
#region Public Methods and Operators
public static bool RecordExists<TEntity>(
this ObjectSet<TEntity> set,
TEntity entity) where TEntity : class
{
Contract.Requires(set != null);
Contract.Requires(entity != null);
var expressionParameter = Expression.Parameter(typeof(TEntity));
var keyProperties = set.GetKeyProperties();
var matchExpression =
keyProperties.Select(
pi =>
Expression.Equal(
Expression.Property(expressionParameter, pi.Last()),
Expression.Constant(pi.Last().GetValue(entity, null))))
.Aggregate<BinaryExpression, Expression>(
null,
(current, predicate) => (current == null) ? predicate :
Expression.AndAlso(current, predicate));
var existing =
set.SingleOrDefault(Expression.Lambda<Func<TEntity, bool>>(
matchExpression,
new[] { expressionParameter }));
return existing != null;
}
#endregion
#region Methods
private static IEnumerable<PropertyPathCollection> GetKeyProperties<TEntity>(this ObjectSet<TEntity> objectSet)
where TEntity : class
{
Contract.Requires(objectSet != null);
var entityType = typeof(TEntity);
return
objectSet.EntitySet.ElementType.KeyMembers.Select(
c => new PropertyPathCollection(entityType.GetProperty(c.Name, KeyPropertyBindingFlags)));
}
#endregion
}
public sealed class PropertyPathCollection : IEnumerable<PropertyInfo>
{
// Fields
#region Static Fields
public static readonly PropertyPathCollection Empty = new PropertyPathCollection();
#endregion
#region Fields
private readonly List<PropertyInfo> components;
#endregion
// Methods
#region Constructors and Destructors
public PropertyPathCollection(IEnumerable<PropertyInfo> components)
{
this.components = new List<PropertyInfo>();
this.components.AddRange(components);
}
public PropertyPathCollection(PropertyInfo component)
{
this.components = new List<PropertyInfo> { component };
}
private PropertyPathCollection()
{
this.components = new List<PropertyInfo>();
}
#endregion
#region Public Properties
public int Count
{
get
{
return this.components.Count;
}
}
#endregion
#region Public Indexers
public PropertyInfo this[int index]
{
get
{
return this.components[index];
}
}
#endregion
#region Public Methods and Operators
public static bool Equals(PropertyPathCollection other)
{
if (ReferenceEquals(null, other))
{
return false;
}
return true;
}
public static bool operator ==(PropertyPathCollection left, PropertyPathCollection right)
{
return Equals(left, right);
}
public static bool operator !=(PropertyPathCollection left, PropertyPathCollection right)
{
return !Equals(left, right);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof(PropertyPathCollection))
{
return false;
}
return Equals((PropertyPathCollection)obj);
}
public override int GetHashCode()
{
return this.components.Aggregate(0, (t, n) => (t + n.GetHashCode()));
}
#endregion
#region Explicit Interface Methods
IEnumerator<PropertyInfo> IEnumerable<PropertyInfo>.GetEnumerator()
{
return this.components.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.components.GetEnumerator();
}
#endregion
}
And the usage is like this:
var context = this.DbContext;
var adapter = context as IObjectContextAdapter;
var objectContext = adapter.ObjectContext;
objectContext.CreateObjectSet<TEntity>().RecordExists(instance);
When you call _dbContext.Set<TEntity>().Add(root); it tells the context that all the entities in the graph have a state of EntityState.Added. But 2 entities with the same ID and EntityState.Added are not allowed, and an exception is thrown.
Try changing the line to _dbContext.Set<TEntity>().Attach(root);. That will put the graph into the context with EntityState.Unchanged. Entities that are already in the context in some other state will have their state set to Unchanged.
Now you should be able to go fix the states.
Struck this answer out because Attach causes the same error - as per comment
References:
When to use DbSet<T>.Add() vs DbSet<T>.Attach()
Why does Entity Framework Reinsert Existing Objects into My Database?
Making Do with Absent Foreign Keys
Provide better support for working with disconnected entities
DbSet.Attach method
I changed my BaseEntity to
public class BaseEntity
{
public int Id {get; set;}
public States State { get; set; }
public bool MustDelete {get; set;}
public enum States
{
Unchanged,
Added,
Modified,
Deleted
}
}
And also changed following methods in my BaseDomainService<T> class:
public class BaseDomainService<T> where T : class
{
protected readonly DbContext _dbContext;
public BaseDomainService(IUnitOfWork uow)
{
_dbContext = (DbContext)uow;
}
.....
public static EntityState ConvertState(BaseEntity.States state)
{
switch (state)
{
case BaseEntity.States.Added:
return EntityState.Added;
case BaseEntity.States.Modified:
return EntityState.Modified;
case BaseEntity.States.Deleted:
return EntityState.Deleted;
default:
return EntityState.Unchanged;
}
}
public void ApplyChanges<TEntity>(TEntity root) where TEntity : BaseEntity
{
_dbContext.Set<TEntity>().Add(root);
foreach (var entry in _dbContext.ChangeTracker
.Entries<BaseEntity>())
{
if (FoundAnEntityWithSameKeyInDbContext<TEntity>(entry))
entry.State = EntityState.Detached;
else
{
BaseEntity stateInfo = entry.Entity;
if (stateInfo.MustDelete == true)
entry.State = EntityState.Detached;
else
entry.State = ConvertState(stateInfo.State);
}
}
}
private bool FoundAnEntityWithSameKeyInDbContext<TEntity>(DbEntityEntry<BaseEntity> entry) where TEntity : BaseEntity
{
var tmp = _dbContext.ChangeTracker.Entries<BaseEntity>().Count(t => t.Entity.Id == entry.Entity.Id && t.Entity.Id != 0 && t.Entity.GetType() == entry.Entity.GetType());
if (tmp > 1)
return true;
return false;
}
}
So, the problem solved.
I am looking a way to create Generic GetById that get params object[] as parameter, knows to find the key/s field/s and know to find the relevant entity.
In the way to find a solution I thought on a generic method that returns the PK fields definition and a generic method that can return the entity based on fields.
I am looking for something I can use in table with one or more fields as primary key.
EDIT
one or more fields as primary key example =
table Customers have (CompanyId, CustomerName, Address, CreateDate).
The primary key of Customers are CompanyId are CustomerName.
I am looking for generic GetById that will know to handle also those such of tables.
You can't get "generic" approach if you don't know how many members is in the key and what types do they have. I modified my solution for single key to multiple keys but as you can see it is not generic - it uses order in which keys are defined:
// Base repository class for entity with any complex key
public abstract class RepositoryBase<TEntity> where TEntity : class
{
private readonly string _entitySetName;
private readonly string[] _keyNames;
protected ObjectContext Context { get; private set; }
protected ObjectSet<TEntity> ObjectSet { get; private set; }
protected RepositoryBase(ObjectContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
Context = context;
ObjectSet = context.CreateObjectSet<TEntity>();
// Get entity set for current entity type
var entitySet = ObjectSet.EntitySet;
// Build full name of entity set for current entity type
_entitySetName = context.DefaultContainerName + "." + entitySet.Name;
// Get name of the entity's key properties
_keyNames = entitySet.ElementType.KeyMembers.Select(k => k.Name).ToArray();
}
public virtual TEntity GetByKey(params object[] keys)
{
if (keys.Length != _keyNames.Length)
{
throw new ArgumentException("Invalid number of key members");
}
// Merge key names and values by its order in array
var keyPairs = _keyNames.Zip(keys, (keyName, keyValue) =>
new KeyValuePair<string, object>(keyName, keyValue));
// Build entity key
var entityKey = new EntityKey(_entitySetName, keyPairs);
// Query first current state manager and if entity is not found query database!!!
return (TEntity)Context.GetObjectByKey(entityKey);
}
// Rest of repository implementation
}
I don't know how useful this would be because it is generic but you could do this:
public TEntity GetById<TEntity>(params Expression<Func<TEntity, bool>>[] keys) where TEntity : class
{
if (keys == null)
return default(TEntity);
var table = context.CreateObjectSet<TEntity>();
IQueryable<TEntity> query = null;
foreach (var item in keys)
{
if (query == null)
query = table.Where(item);
else
query = query.Where(item);
}
return query.FirstOrDefault();
}
and then you could call it like this:
var result = this.GetById<MyEntity>(a => a.EntityProperty1 == 2, a => a.EntityProperty2 == DateTime.Now);
Disclaimer: this really isn't a GetByid, it's really a "let me give you a couple parameters and give me the first entity that matches". But that being said, it uses generics and it will return an entity if there is a match and you search based on primary keys.
I think you can't implement such thing because you won't be able to join between each passed value with the appropriate key field.
I'd suggest using custom method for each entity:
Assuming Code and Name are keys in Person table:
public IEnumerable<Person> ReadById(int code, string name)
{
using (var entities = new Entities())
return entities.Persons.Where(p => p.Code = code && p.Name = name);
}
Ok this is my second stab at it. I think this would work for you.
public static class QueryExtensions
{
public static Customer GetByKey(this IQueryable<Customer> query, int customerId,string customerName)
{
return query.FirstOrDefault(a => a.CustomerId == customerId && a.CustomerName == customerName);
}
}
So the beauty behind this extension method is you can now do this:
Customer customer = Db.Customers.GetByKey(1,"myname");
You obviously have to do this for every type, but probably worth it if you need it :)
I think the set up is a bit but I do think creating a reusable pattern will pay off in the long run. I just wrote this up and haven't tested, but I based off a searching pattern I use a lot.
Required Interfaces:
public interface IKeyContainer<T>
{
Expression<Func<T, bool>> GetKey();
}
public interface IGetService<T>
{
T GetByKey(IKeyContainer<T> key);
}
Example Entities:
public class Foo
{
public int Id { get; set; }
}
public class ComplexFoo
{
public int Key1 { get; set; }
public int Key2 { get; set; }
}
Implementation Example:
public class FooKeyContainer : IKeyContainer<Foo>
{
private readonly int _id;
public FooKeyContainer(int id)
{
_id = id;
}
public Expression<Func<Foo, bool>> GetKey()
{
Expression<Func<Foo, bool>> key = x => x.Id == _id;
return key;
}
}
public class ComplexFooKeyContainer : IKeyContainer<ComplexFoo>
{
private readonly int _id;
private readonly int _id2;
public ComplexFooKeyContainer(int id, int id2)
{
_id = id;
_id2 = id2;
}
public Expression<Func<ComplexFoo, bool>> GetKey()
{
Expression<Func<ComplexFoo, bool>> key = x => x.Key1 == _id && x.Key2 == _id2;
return key;
}
}
public class ComplexFooService : IGetService<ComplexFoo>
{
public ComplexFoo GetByKey(IKeyContainer<ComplexFoo> key)
{
var entities = new List<ComplexFoo>();
return entities.Where(key.GetKey()).FirstOrDefault();
}
}
Usage:
var complexFoo = ComplexFooService.GetByKey(new ComplexFooKeyContainer(1, 2));
I have the following class:
public class InMemoryRepository : IRepository
{
public void Add(object entity)
{
throw new NotImplementedException();
}
public void Attach(object Entity)
{
throw new NotImplementedException();
}
public T Get<T>(object id)
{
throw new NotImplementedException();
}
public IList<T> GetAll<T>(string queryName)
{
throw new NotImplementedException();
}
public IList<T> GetAll<T>()
{
throw new NotImplementedException();
}
public IQueryable<T> Query<T>()
{
throw new NotImplementedException();
}
public void Remove(object entity)
{
throw new NotImplementedException();
}
public void Save(object entity)
{
throw new NotImplementedException();
}
}
Our default repository implementation uses NHibernate for the backing store, but I'd like to implement an in-memory version of it so I can prototype the domain objects without having to create a backing SQL database. Assuming the convention that all objects have an Id property as the primary key, how would you implement a generic memory store for this?
Some key points I'm having a hard time addressing:
The repository methods themselves are generic, so I need some mechanism for automatically storing and referencing different types. Get<TestEntity>(object id) should be able to query all stored instances of TestEntity and find the one with the matching Id property, but I can't define a collection of TestEntity objects directly, as the repository won't know what types I'm feeding it until runtime.
I need to support LINQ to Objects for the Query() method. Assuming I can come up with a decent way to store the objects, this should be as simple as returning an array of stored objects AsQueryable().
How would you store the objects to meet the above requirements?
Basics are simple:
public class InMemoryRepository : IRepository
{
private readonly IList<object> entities = new List<object>();
public T Get<T>(object id)
{
return entities.OfType<T>.SingleOrDefault(e => e.ID == id);
}
public IList<T> GetAll<T>()
{
return entities.OfType<T>.ToList();
}
public IQueryable<T> Query<T>()
{
return GetAll<T>.AsQueryable();
}
}
However, as soon as it comes to public IList<T> GetAll<T>(string queryName), things get complicated.
Potentially you can resort to an SQLite-based repository implementation for your tests.
I would go with NHibernate configured for in-memory SqlLite database. You can test then your real code and be sure that everything works correct. Writing mock for Repository can be hard and if you change IRepository interface you will have to reimplement you InMemoryRepository.
For me one of big benefits of having NHibernate is the possibility for using in memory database for testing.
With Anton's answer I was able to fix my own InMemoryRepository. I have modified it to match the class in the question:
private readonly ConcurrentDictionary<Type, List<object>> ObjectList = new ConcurrentDictionary<Type, List<object>>();
public int Add<T>(T obj) where T : IIdentifier
{
// instantiate if list does not exist for this object type
if (!ObjectList.ContainsKey(typeof (T)))
ObjectList[typeof(T)] = new List<object>();
// get id
var id = GetId<T>() + 1;
// add object to list
obj.Id = id;
ObjectList[typeof(T)].Add(obj);
return id;
}
public void Attach<T>(T obj) {
// do not need to do anything
}
public T Get<T>(int id) where T : class, IIdentifier
{
// check list exist
if (!ObjectList.ContainsKey(typeof (T)))
return null;
return ObjectList[typeof(T)].OfType<T>().FirstOrDefault(n => n.Id == id);
}
public List<T> GetAll<T>(Func<T, bool> predicate) where T : new()
{
// check list exist
if (!ObjectList.ContainsKey(typeof(T)))
return null;
return ObjectList[typeof(T)].OfType<T>().Where(predicate).ToList();
}
public List<T> GetAll<T>()
{
return ObjectList[typeof(T)].OfType<T>.ToList();
}
public IQueryable<T> Query<T>()
{
return GetAll<T>.AsQueryable();
}
public int Remove<T>(int id) where T : IIdentifier
{
// check list exist
if (!ObjectList.ContainsKey(typeof(T)))
return 0;
// find object with matching id
for (var i = 0; i < ObjectList[typeof(T)].Count; i++)
if (ObjectList[typeof(T)].OfType<T>().ToList()[i].Id == id)
{
ObjectList[typeof(T)].RemoveAt(i);
return id;
}
// object not found
return 0;
}
public int Save<T>(T obj) where T : IIdentifier
{
// check list exist
if (!ObjectList.ContainsKey(typeof(T)))
return 0;
// find object with matching id
for (var i = 0; i < ObjectList[typeof(T)].Count; i++)
if (ObjectList[typeof(T)].OfType<T>().ToList()[i].Id == obj.Id)
{
ObjectList[typeof (T)][i] = obj;
return obj.Id;
}
// object not found
return 0;
}
#region Helper methods
private int GetId<T>() where T : IIdentifier
{
return ObjectList[typeof(T)].Count == 0 ? 0 : ObjectList[typeof(T)].OfType<T>().Last().Id;
}
#endregion
Here is an implementation of fake repository based on DbSet, including find by primary keys:
http://refactorthis.wordpress.com/2011/11/30/generic-repository-fake-idbset-implementation-update-find-method-identity-key/