I'm trying to implement a generic repository pattern that implements CRUD operations of generic items in an IEnumerable. I'm having issue of generically finding an item that may already be in the IEnumerable.
I need to programmatically pass which properties make up the 'key' or distinct record and then using LINQ do a Enumerable.Any() on the defined properties so it would see if the object already exists in the IEnumerable.
Here is my code so far:
// Generic Method
public void AddItem(TEntity item)
{
var entities = GetAllItems().ToList(); // Method gets cached IEnumerable<TEntity>
if(true) // Generically see if TEntity is already in the list based of defined properties
{
entities.Add(item);
}
}
// Same function but non-generic
private void AddItem(MyObject object)
{
var objects = GetAllItems().ToList(); //Method gets cached IEnumerable<MyObject>
if(!objects.Any(a=> a.ID == MyObject.ID ))
{
objects.Add(object);
_cache.AddReplaceCache(objects);
}
}
NOTE: the keys can be any property or properties on the object MyObject
You can make your entities inherit from a common interface:
public interface IEntity
{
int ID { get; set; }
}
Then you can redefine your method like
public void AddItem<TEntity>(TEntity entity) where TEntity : IEntity
{
// Now you can access entity.ID
}
Now, if you don't always want to compare via ID, then you can add a predicate to your method:
public void AddItem<TEntity>(TEntity entity, Func<TEntity, bool> predicate)
{
var objects = GetAllItems().ToList();
// You might need some logic in the predicate to check for null
if(!objects.Any(a => predicate(a as TEntity))
{
objects.Add(entity);
_cache.AddReplaceCache(objects);
}
}
Then you would use your function as
repository.AddItem(entity, e => e.ID == entity.ID && e.OtherProperty == entity.OtherProperty);
If I understood you correctly your problem is TEntity doesn't have property ID. So make your entities to inherit common interface which have for example the ID column.
public interface IObject
{
int ID {get; set;}
//define all other properties which are shared between your Entities.
}
public class MyObject : IObject
{
public int ID {get; set;}
//other properties.
}
public void AddItem(TEntity item): where TEntity:IObject
{
var entities = GetAllItems().ToList(); //Method gets cached IEnumerable<TEntity>
if(!objects.Any(a=> a.ID == item.ID ))//Generically see if TEntity is already in the list based of defined properties
{
entities.Add(item);
}
}
Related
I have a standard EF Core data model with several one-many and many-to-many relationships.
I want to create a way to produce various data calculations or commonly run procedures. These should return values to be used throughout the application and not values to be stored in the database.
I'll give a fictional example here of a scenario:
Entity 1 - YearGroup
Entity 2 - Student
Relationship - One YearGroup to many Students
Now I understand you could simply write in the controller:
int student = _context.Students.Where(s => s.YearGroupId == ygId).Count()
But let's say I wanted to simplify this by creating a method somewhere which returns data such as this, so I don't have to specify the query every time I get the number of Students in a YearGroup. I appreciate this is a simple example, but others could be more complex.
I was thinking adding a field to yeargroup.cs model such as:
public int TotalStudents { //code here to get students from students table }
Which I could then use like:
#model.YearGroup.TotalStudents
But I don't know how to get this to include relational data, i.e. from the students table.
I would prefer not create random methods in separate, unrelated classes such as GetStudentsInYearGroup(ygId). It would be nice to include this in the object model if possible.
What would be the best practice way of acheiving this?
Note: I don't have a code editor nor a project setup so what I am going to write might not be compilable.
As simple as your fictional example
For any case as simple as your fictional example, if you just want to get the total students per a year group, and I assume their relationships are properly setup, you can just simply use the navigation property:
// Find the target year group
var yearGroup = _context.YearGroups
.SingleOrDefault(x => x.Id == ygId);
int totalStudents = 0;
if (yearGroup != null)
{
totalStudents = yearGroup.Students.Count();
}
Extension methods
The other way I can think of is to define whatever you need as extension methods of the entity:
public static class YearGroupExtensions
{
public static int GetTotalStudents(this YearGroup yearGroup)
{
if (yearGroup == null)
{
return 0;
}
return yearGroup.Students.Count();
}
public static int GetTotalStudents(this YearGroup yearGroup, Gender gender)
{
if (yearGroup == null)
{
return 0;
}
if (gender == null)
{
return yearGroup.GetTotalStudents();
}
return yearGroup
.Students
.Count(x => x.Gender == gender);
}
}
// usage
// YearGroup yearGroup = GetYearGroup(ygId);
// int totalStudents = yearGroup.GetTotalStudents();
Repository Pattern
If you find yourself repeating similar methods you need for most of your entities, it might be good to define a generic repository for them.
I am not here arguing whether this is just a wrapper of DbContext as that itself is already using repository pattern.
public interface IEntity { }
public interface IRepository<T> where T : IEntity
{
IEnumerable<T> GetAll();
T GetById(int id);
void Insert(T entity);
...
}
public abstract class RepositoryBase<T> : IRepository<T> where T : IEntity
{
protected readonly AppDbContext _dbContext;
protected DbSet<T> _entities;
private RepositoryBase(AppDbContext dbContext)
{
_dbContext = dbContext;
_entities = dbContext.Set<T>();
}
public virtual IEnumerable<T> GetAll()
{
return _entities.AsEnumerable();
}
...
}
public class YearGroupRepository : RepositoryBase<YearGroup>
{
...
}
Separate Persistence and Domain Model
This is my preference just because I'm a DDD guy, and I want to build anything from the domain first (what businesss problems you're trying to solve), without thinking its backend persistence.
The basic idea here is to have 2 sets of models. They could be similar, or totally different. 1 set of models is called the domain model, which reflects your business domain. The other set of models is called the persistence model. That could be your regular Entity Framework Entities.
More on this: https://stackoverflow.com/a/14042539/2410655
Given a DbContext, an entity and a navigation property, you can construct an IQueryable as follows;
public static IQueryable AsQueryable(DbContext context, object entity, string navigation){
var entry = context.Entry(entity);
if (entry.State == EntityState.Detatched)
return null;
var nav = entry.Navigation(navigation);
return nav.Query();
}
I feel like there should be an existing method for this, but I can't seem to find one right now.
You should then be able to use this method in a fairly generic way for any navigation property, without needing to replicate the foreign key criteria all over the place.
public int TotalStudents(DbContext context) =>
AsQueryable(context, this, nameof(Students))?.Count() ?? 0;
While this would add some minor performance overhead, you could extract the base entity and navigation property from a LamdaExpression, and write some extension methods;
public class QueryableVisitor : ExpressionVisitor
{
private object obj;
public object BaseObject { get; private set; }
public string Navigation { get; private set; }
protected override Expression VisitConstant(ConstantExpression node)
{
BaseObject = obj = node.Value;
return base.VisitConstant(node);
}
protected override Expression VisitMember(MemberExpression node)
{
Visit(node.Expression);
BaseObject = obj;
if (node.Member is PropertyInfo prop)
obj = prop.GetValue(obj);
else if (node.Member is FieldInfo field)
obj = field.GetValue(obj);
Navigation = node.Member.Name;
return node;
}
}
public static IQueryable<T> AsQueryable<T>(this DbContext context, Expression<Func<IEnumerable<T>>> expression)
{
var visitor = new QueryableVisitor();
visitor.Visit(expression);
var query = AsQueryable(context, visitor.BaseObject, visitor.Navigation);
return (IQueryable<T>)query;
}
public static int Count<T>(this DbContext context, Expression<Func<IEnumerable<T>>> expression) =>
AsQueryable(context, expression)?.Count() ?? 0;
Enabling strongly typed usage like the following;
public int TotalStudents(DbContext context) =>
context.Count(() => this.Students);
public int ActiveStudents(DbContext context) =>
context.AsQueryable(() => this.Students)?.Where(s => s.Active).Count() ?? 0;
I have a database that has multiple tables that are objects in my application, Member, Employer, Invoice etc.
I want to create a generic method that will allow me to retrieve a single object from any of the tables. ex. public Object GetRow(ClassType type, ClassKey key, object valueToFind)
so the Member method right now would be
Member member = _manager.Members.Where(m => m.MemberKey == valueToFind).FirstOrDefauilt();
Employer employer = _manager.Employers.Where(e => e.EmployerKey == valueToFind).FirstOrDefault();
Invoice invoice = _manager.Invoices.Where(i => i.InvoiceKey == valueToFind).FirstOrDefault();
How do i write a generic method that would handle all 3 cases?
I am assuming you are using entity framework. In such case you can write a method like:
public T RetrieveFirst<T>( Expression<Func<T, bool>> filter)
{
return _manager.Set<T>().FirstOrDefault(filter);
}
You can then use it as follows:
var member = RetrieveFirst<Member>(m => m. MemberKey = valueToFind );
If you wanted to unify the filtering, the entities would all have to implement an interface and have the same name of the key property.
I think #ajawad987 has written the perfect answer for you :-)
You could implement an interface that all your entities (employee, manager, etc.) inherit and then implement a generic method that works with that interface.
Basically you define a simple interface that defines the common Key property:
public interface IHasKeyProperty
{
int Key { get; set; }
}
public class Manager : IHasKeyProperty
{
public int Key { get; set; }
// Rest of manager code...
}
public class Employee : IHasKeyProperty
{
public int Key { get; set; }
// Rest of employee code...
}
And then you can write a generic query like this:
public TEntity GetByKey<TEntity>(int key)
where TEntity : IHasKeyProperty, class
{
return this._dbContext.Set<TEntity>().FirstOrDefault(x => x.Key == key);
}
I'm assuming you're using Entity Framework Core, hence the _dbContext variable in my snippet above.
Using the method would look like this:
var myEmployee = GetByKey<Employee>(207);
var myManager = GetByKey<Manager>(101);
To extend #ajawad987's answer, if you need to support different key types you could do:
public interface IHasKeyProperty<TId>
{
TId Key { get; set; }
}
public class Manager : IHasKeyProperty<int>
{
public int Key { get; set; }
// Rest of manager code...
}
public class Employee : IHasKeyProperty<Guid>
{
public Guid Key { get; set; }
// Rest of employee code...
}
public TEntity GetByKey<TEntity, TId>(TId key) where TEntity : IHasKeyProperty<TId>
{
return this._dbContext.Set<TEntity>().FirstOrDefault(x => x.Id == key);
}
I'm new to LINQ and I'm doing a simple project to learn the features of the technology.
Currently I've got a static class that wraps an array of object (a kind of simple factory). Let's say it looks like the following:
public static class Factory
{
private static Item[] items = new Item[]
{
// items are created here
};
// ...
}
Now I can add some functions to my Factory that allow me to query the inner array, e.g.
public static Item GetByID(ItemID id)
{
var query =
from item in items
where item.ID == id
select item;
return query.First();
}
However, this requires me to modify the internals of the Factory class. Is there a way to write such queries from the 'outer world' instead ?
public class OtherClass
{
var result = from it in Factory select ...
}
?
Yes, you can. Just use linq on the Factory from the 'outer world':
public class OtherClass
{
public Item Get(ItemId id)
{
return Factory.Items.SingleOrDefault(i => i.ID == id);
}
}
Of course, to do this, you'd need to change the access modifier of the items array to be public.
There are so many options.
The easiest thing to do is just to expose a public property that allows just what you want to have allowed:
public static class Factory
{
private static Item[] items = new Item[]
{
// items are created here
};
public static IEnumerable<IReadableItem> Items{ get { return items; } }
// ...
}
The above code assumes that the Item class implements an IReadableItem interface that only has the methods and properties on it that you want to allow people to access. You could also clone the items list before returning it each time, if you're worried someone might re-cast the Items or try to modify it using reflection. Because the standard LINQ methods all work off of IEnumerable<>s, this would allow someone to effectively produce a LINQ query on your items, without exposing overmuch data.
List<string> bestItemNames = Factory.Items.Where(i => i.IsBest)
.Select(i => i.Name)
.ToList();
If you wanted to get really fancy, you could even implement your own LINQ provider. From a language perspective, LINQ expression syntax just maps to specific method names, so if you had a class that implemented a .Where() and a .Select() method, then you could implement that pretty much however you wanted, and people wouldn't know any different until they tried doing something that your methods didn't support.
One possibility is to implement IQueryable<T> for a non-static class:
public class Factory<T> : IQueryable<T>
{
protected T[] _items = new T[]{};
public Type ElementType
{
// or typeof(T)
get { return _items.AsQueryable().ElementType; }
}
public System.Linq.Expressions.Expression Expression
{
get { return _items.AsQueryable().Expression; }
}
public IQueryProvider Provider
{
get { return _items.AsQueryable().Provider; }
}
public IEnumerator<T> GetEnumerator()
{
return ( IEnumerator<T> )_items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
}
Derive non-generic class to populate array (if desired)
public class ItemFactory : Factory<Item>
{
public ItemFactory()
{
// items are created here
}
}
Create static instance
public static class Factories
{
private static ItemFactory _itemFactory = new ItemFactory();
public static ItemFactory ItemFactory { get { return _itemFactory; } }
}
Usage:
var specificItem = Factories.ItemFactory
.Where( item => item.ID == id )
.SingleOrDefault();
use an expression tree
public class OtherClass
{
public Item Get(ItemId id)
{
return Factory.Get(i => i.id == id);
}
}
and change the get method to
public Item Get(Expression<Func<Item,bool>> filter)
{
return items.SingleOrDefault(filter);
}
however, this approach makes little sense unless you are encapsulating some other logic in your factory class i.e. select only rows that are not soft deleted.
Is it possible with Generics to get an object from my EntityFramework without knowing the type?
I'm thinking of something along the lines of:
public T GetObjectByID<T>(int id)
{
return (from i in myDatabase.T where i.ID == id select i);
}
Is that doable? Could I use Reflection to somehow take T.GetType().Name and use that for the table?
EDIT
Another hangup, is that not all tables available to me use "ID" as their unique column name.
I think the Find() method may be able to do what you're looking for (DbSet.Find Method).
var someEntity = dbSet.Find(keyValue);
You can define interface implemented by all your entities:
public interface IEntity
{
int Id { get; }
}
and method to retrieve your entity:
public T GetObjectById<T>(int id) where T : class, IEntity
{
return context.CreateObjectSet<T>().SingleOrDefault(e => e.Id == id);
}
You can also use similar approach to one provided in the linked question. You just have to use another method to get your entity:
public virtual T GetByKey<T>(int id) where T : class, IEntity
{
string containerName = context.DefaultContainerName;
string setName = context.CreateObjectSet<T>().EntitySet.Name;
// Build entity key
var entityKey = new EntityKey(containerName + "." + setName, "Id", id);
return (TEntity)Context.GetObjectByKey(entityKey);
}
The difference is that first method always query the database even if you have already loaded the instance to the context whereas second approach first checks if the instance is already loaded. The method is not so efficient because it builds these names over and over. Here is more general approach which can work with any key type and name and here is approach working with complex keys.
Neither of this method directly works with inheritance - you must provide base type to make it work.
It's hard to make a completely generic solution because Entities can have composite keys, but this will work for a simple single key case.
The key is to restrict T to be of type System.Data.Objects.DataClasses.EntityObject, which then has as EntityKey property (which represents the primary key).
static T GetById<T>(object id) where T : EntityObject
{
using (var context = new MyEntities())
{
return context.CreateObjectSet<T>()
.SingleOrDefault(t => t.EntityKey.EntityKeyValues[0].Value == id);
}
}
Finally solved the issue with this:
http://pastebin.com/kjXUKBNS
To call the code I use this:
// Get the id of the object we are saving
PropertyInfo prop = GetProperty<TEntity>(entity, entity.EntityKey.EntityKeyValues[0].Key);
string entityID = prop.GetValue(entity, null).ToString();
// Get the current version of this object
var originalEntity = GetEntity<TEntity>(PropertyEquals, entityID);
This makes the assumption that the Primary Key you are searching on is the first one in the list of Primary Keys.
An other way to get entity from id on entitiy framework
public T Get<T>(int id) where T : EntityObject;
{
var ObjectSet = _context.CreateObjectSet<T>();
var PropName = ObjectSet.EntitySet.ElementType.KeyMembers[0].ToString();
return ObjectSet.Where("it." + PropName + "=" + id).FirstOrDefault();
}
Result
SELECT TOP (1)
[Extent1].[CatId] AS [CatId],
[Extent1].[CatName] AS [CatName],
[Extent1].[CatType] AS [CatType],
FROM [dbo].[Categories] AS [Extent1]
WHERE [Extent1].[CatId] = 1
I think this could help
public static TEntity Find<TEntity>(this ObjectSet<TEntity> set, object id) where TEntity : EntityObject
{
using (var context = new MyObjectContext())
{
var entity = set.Context.CreateObjectSet<TEntity>();
string keyName = entity.FirstOrDefault().EntityKey.EntityKeyValues.FirstOrDefault().Key;
return entity.Where("it." + keyName + " = " + id).FirstOrDefault();
}
}
One thing I've done that has worked is to define a "KeyComparator" interface:
public interface IHasKeyComparator<TObject> {
Expression<Func<TObject, bool>> KeyComparator { get; }
}
And then you can implement it on some object even with a multi-valued key:
public sealed class MultiKeyedObject : IHasKeyComparator<MultiKeyedObject> {
public int ID1 { get; set; }
public string ID2 { get; set; }
readonly Expression<Func<MultiKeyedObject, bool>> mKeyComparator;
public Expression<Func<MultiKeyedObject, bool>> KeyComparator {
get { return mKeyComparator; }
}
public MultiKeyedObject() {
mKeyComparator = other => other.ID1 == ID1 && other.ID2 == ID2;
}
}
And can use it generically if you provide the constraint:
public TObject Refresh<TObject>(TObject pObject)
where TObject : IHasKeyComparator<TObject> {
return this.Set<TObject>().Single(pObject.KeyComparator);
}
Doing it this way requires you to have an instance of the object, though, although you can fill an empty one with key values and then use it to pull from the DB.
Here's the method I use to get an Entity object by ID.
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);
}
You may find this a more robust solution than some of those provided above. I've used it as part of a generic repository in a lot of projects. Hope it helps you out.
Im quite new to generics and as a learning exercise Im trying to create a simple method that picks an entity from an entity set (table) with a specified ID.
public T GetEntity<T>(int id) where T : class
{
return db.GetTable<T>().SingleOrDefault(o => o.id == id);
}
The above obviously wont work because o in o.id is unknown and thus cannot access the property id - can someone help with how to achieve this?
If all of your entities contain an id property then you can define an interface
public interface IEntity
{
int id { get; }
}
implement it on all your classes and change your method to
public T GetEntity<T>(int id) where T : IEntity
{
return db.GetTable<T>().SingleOrDefault(o => o.id == id);
}
Such interface can be useful in all the places where you want to do something for all your entities, ie. Delete by id. You can add more properties to your interface, ie. timestamp.
If you cannot change your entity classes (for implementing a common interface), you could use a predicate function and pass it into the method
public T GetEntity<T>(int id, Func<T, int, bool> predicate) where T : class
{
return db.GetTable<T>().SingleOrDefault(o => predicate(o,id));
}
Another solution, not recommended for performance reasons, but i shall post it anyway.
public T GetEntity<T>(int id) where T : class
{
return db.GetTable<T>().AsEnumerable().SingleOrDefault(o => (int)o.GetType().GetProperty("Id").GetValue(o,null) == id);
}
You need to set a constrain on your T so the compiler can be sure there is a id property. Once way is to use interfaces, ie.
public interface IUnique
{
int id { get; set; }
}
Then you can say
public T GetEntity<T>(int id) where T : class, IUnique
{
return db.GetTable<T>().SingleOrDefault(o => o.id == id);
}