Reputation: 2827
The issue is about getting the Max value within a sequence of elements.
The type of the object is unknown and the property follows the convention <EntityName>_Id
.
At the moment I resolved it like this:
public static int GetMaxId<TEntity>()
{
try
{
var key = typeof(TEntity).Name;
var adapter = (IObjectContextAdapter)MyDbContext;
var objectContext = adapter.ObjectContext;
// 1. we need the container for the conceptual model
var container = objectContext.MetadataWorkspace.GetEntityContainer(
objectContext.DefaultContainerName, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace);
// 2. we need the name given to the element set in that conceptual model
var name = container.BaseEntitySets.Where((s) => s.ElementType.Name.Equals(key)).FirstOrDefault().Name;
// 3. finally, we can create a basic query for this set
var query = objectContext.CreateQuery<TEntity>("[" + name + "]");
// working with result
var baseEntity = container.BaseEntitySets.Where((s) => s.ElementType.Name.Equals(key)).FirstOrDefault();
var property = baseEntity.ElementType.Members.FirstOrDefault(_property => _property.Name.EndsWith("Id"));
var tmpResult = query.ToList();
var currentMaxID = tmpResult.Max(element =>
{
PropertyInfo pInfo = typeof(TEntity).GetProperty(property.Name);
int value = (int)pInfo.GetValue(element);
return value;
});
return currentMaxID;
}
catch (Exception ex)
{
string exMessage = "GetMaxId.Exception";
EntityModelDataProviderLogger.Fatal(exMessage, ex);
throw;
}
}
Is there a better/cleaner way to do it?
I have read about dynamic Expressions but it wasn't so clear to me.
LINQ expression with generic property This example compares Dates. In my case I have "only one element" and I don't know how to construct the expressions.
Upvotes: 0
Views: 687
Reputation: 12410
I would define an interface with property Id and add a constraint on your method to only accept types of that interface. That removes the need for fragile naming conventions and makes reflection unnecessary.
public interface IEntity
{
int Id { get; set; }
}
public static int GetMaxId<TEntity>() where TEntity : IEntity
{
return MyDbContext.Set<TEntity>().AsEnumerable().Max(e => e.Id);
}
public Entity : IEntity
{
public int MyId { get; set;}
public int Id { get{ return MyId; } set{ MyId = value; } }
}
Usage:
var max = GetMaxId<Entity>();
Upvotes: 0
Reputation: 111820
It is quite long, and I can't test it here:
public static int GetMaxId<TEntity>()
{
try
{
var key = typeof(TEntity).Name;
var adapter = (IObjectContextAdapter)MyDbContext;
var objectContext = adapter.ObjectContext;
// 1. we need the container for the conceptual model
var container = objectContext.MetadataWorkspace.GetEntityContainer(
objectContext.DefaultContainerName, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace);
// 2. we need the name given to the element set in that conceptual model
var baseEntity = container.BaseEntitySets.Single(s => s.ElementType.Name == key);
// 3. finally, we can create a basic query for this set
var query = objectContext.CreateQuery<TEntity>("[" + baseEntity.Name + "]");
// Looking for the property
string propertyId = baseEntity.Name + "_" + "Id";
// The PropertyInfo connected to the EdmMember
PropertyInfo property = (PropertyInfo)typeof(TEntity).GetProperty(propertyId);
// Building the Expression
ParameterExpression par = Expression.Parameter(typeof(TEntity));
MemberExpression prop = Expression.Property(par, property);
// cast to (int?)property
// Note the use of int?, because the table could be empty!
UnaryExpression conv = Expression.Convert(prop, typeof(int?));
// An expression like x => x.Entity_Id
Expression<Func<TEntity, int?>> lambda = Expression.Lambda<Func<TEntity, int?>>(conv, par);
int? currentMaxID = ((IQueryable<TEntity>)query).Max(lambda);
// We change null to 0
return currentMaxID ?? 0;
}
catch (Exception ex)
{
string exMessage = "GetMaxId.Exception";
EntityModelDataProviderLogger.Fatal(exMessage, ex);
throw;
}
}
If it works I'll explain how/why it works...
Note that what you were doing is conceptually wrong... You where SELECTing all the table with the ToList()
and then locally looking for the Max()
. This code instead creates an Expression Tree to do the Max()
database-side.
Upvotes: 1