I’m setting up a generic repository in my application and I’m having troubles with some LINQ queries.
With a non-generic one, this is what I would do:
private IObjectSet<T> _objectSet;
public List<int?> GetGroups()
{
List<object> objectGroups = new List<object>();
List<int?> intGroups = new List<int?>();
var r = (from n in _objectSet
select n.Group_ID).Distinct();
objectGroups = r.OrderBy(n => n.Value).ToList();
foreach (object value in objectGroups)
{
intGroups.Add((int?)value);
}
return intGroups;
}
Since this is a generic one of type T, when typing “n.”, IntelliSense is not listing any options since the type of n is not explicitly defined (right?).
So here’s what I have so far:
public interface IRepository<T> : IDisposable where T : class
{
IQueryable<T> Fetch();
IEnumerable<T> GetAll();
IEnumerable<T> GetAll(bool activeOnly);
IEnumerable<T> GetAll(string groupID, bool activeOnly);
IEnumerable<T> Find(Func<T, bool> predicate);
T Single(Func<T, bool> predicate);
T First(Func<T, bool> predicate);
List<int?> GetGroups();
int Add(T entity);
void Delete(T entity);
void Attach(T entity);
void SaveChanges();
void SaveChanges(SaveOptions options);
}
public class DataRepository<T> : IRepository<T> where T : class
{
/// <summary>
/// The context object for the database
/// </summary>
private ObjectContext _context;
private IEnumerable<T> _previousEntries;
private string _PKName;
/// <summary>
/// The IObjectSet that represents the current entity.
/// </summary>
private IObjectSet<T> _objectSet;
public DataRepository()
{
switch (typeof(T).Name)
{
case "CRM_Patient":
_context = new TheseEntities();
_PKName = "key_patient";
break;
case "CRM_Account":
_context = new ThoseEntities();
_PKName = "accountnumber";
break;
case "CRM_Supplier":
_context = new OtherEntities();
_PKName = "supplierid";
break;
default:
_context = new OtherEntities();
break;
}
_objectSet = _context.CreateObjectSet<T>();
_previousEntries = this.GetAll();
}
/// <summary>
/// Gets all records as an IQueryable
/// </summary>
/// <returns>An IQueryable object containing the results of the query</returns>
public IQueryable<T> Fetch()
{
return _objectSet;
}
/// <summary>
/// Gets all records as an IEnumberable
/// </summary>
/// <returns>An IEnumberable object containing the results of the query</returns>
public IEnumerable<T> GetAll()
{
return Fetch().AsEnumerable();
}
/// <summary>
/// Finds a record with the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A collection containing the results of the query</returns>
public IEnumerable<T> Find(Func<T, bool> predicate)
{
return _objectSet.Where<T>(predicate);
}
public List<int?> GetGroups()
{
List<object> objectGroups = new List<object>();
List<int?> intGroups = new List<int?>();
//var r = (from n in _objectSet
// select n.GetType().GetProperty("Group_ID").GetValue(n, null)).Distinct();
var r = Fetch().Select(n => n.GetType().GetProperty("Group_ID").GetValue(n, null)).Distinct();
objectGroups = r.OrderBy(n => n.GetType().GetProperty("Value").GetValue(n, null)).ToList();
foreach (object value in r)
{
intGroups.Add((int?)value);
}
return intGroups;
}
/// <summary>
/// Gets a single record by the specified criteria (usually the unique identifier)
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record that matches the specified criteria</returns>
public T Single(Func<T, bool> predicate)
{
return _objectSet.Single<T>(predicate);
}
/// <summary>
/// The first record matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record containing the first record matching the specified criteria</returns>
public T First(Func<T, bool> predicate)
{
return _objectSet.First<T>(predicate);
}
/// <summary>
/// Deletes the specified entitiy
/// </summary>
/// <param name="entity">Entity to delete</param>
/// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
public void Delete(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.DeleteObject(entity);
}
/// <summary>
/// Deletes records matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
public void Delete(Func<T, bool> predicate)
{
IEnumerable<T> records = from x in _objectSet.Where<T>(predicate) select x;
foreach (T record in records)
{
_objectSet.DeleteObject(record);
}
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
/// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
Somehow, my reflection tricks seem to be executed in the LINQ statements, which obviously ends up throwing an exception when I assign a value for objectGroups.
LINQ to Entities does not recognize the method ‘System.Object GetValue(System.Object, System.Object[])’ method, and this method cannot be translated into a store expression.
Any ideas ? I really need to preserve the generic nature of the repository. All of the methods where I’m using Reflection are throwing exceptions…
Thanks much.
EDIT: Added most of the Repository’s methods and the interface. Some methods might be missing from the class but done so to lighten reading 🙂
How about defining the
ValueandGroupIDproperties in an interface, and then making your entities implement this interface and adding this as a generic constraint?Then your
GetGroupsmethod will be able to call these properties without reflection.