Вот несколько методов расширения, которые я создал для эмуляции загрузки NHibernate. Второй - для сущностей с составными ключами. Гораздо страшнее, чем первый, но он работает.
public static class EntityFrameworkExtensions
{
// Loads when the Id property is always "Id" based on derived types of EntityBase<TId>
public static TEntity LoadEntity<TEntity,TId>(this DbContext context, TId id) where TEntity : EntityBase<TId>, new()
{
var entity = context.ChangeTracker.Entries<TEntity>().SingleOrDefault(e => e.Entity.Id.Equals(id))?.Entity;
if (entity == null)
{
entity = new TEntity { Id = id };
context.Set<TEntity>().Attach(entity);
}
return entity;
}
// Loads when you're dealing with a composite key and need to specify both how to identify the key and how to assign if attaching a newly created entity.
public static TEntity LoadEntity<TEntity>(this DbContext context, Func<TEntity, bool> predicate, Action<TEntity> idAssignmentAction) where TEntity : class, new()
{
var entity = context.ChangeTracker.Entries<TEntity>().SingleOrDefault(e => predicate(e.Entity))?.Entity;
if (entity == null)
{
entity = new TEntity();
idAssignmentAction(entity);
context.Set<TEntity>().Attach(entity);
}
return entity;
}
}
// Loads by allowing you to specify an expression identifying the primary key property
public static TEntity LoadEntity<TEntity, TIdProperty>(this DbContext context,
Expression<Func<TEntity, TIdProperty>> idExpression, TIdProperty id) where TEntity : class, new()
{
var parameterExpression = Expression.Parameter(typeof(DbEntityEntry<TEntity>), "ent");
Expression entityProperty = Expression.Property(parameterExpression, "Entity");
var keyValue = Expression.Invoke(idExpression, entityProperty);
var pkValue = Expression.Constant(id, typeof(TIdProperty));
Expression equalsExpression = Expression.Equal(keyValue, pkValue);
var lambda = Expression.Lambda<Func<DbEntityEntry<TEntity>, bool>>(equalsExpression, parameterExpression);
var lambdaC = lambda.Compile();
var entity = context.ChangeTracker.Entries<TEntity>().SingleOrDefault(lambdaC)?.Entity;
if (entity == null)
{
entity = new TEntity();
var valueParameterExpression = Expression.Parameter(typeof(object));
var targetExpression = idExpression.Body is UnaryExpression
? ((UnaryExpression) idExpression.Body).Operand
: idExpression.Body;
var assign = Expression.Lambda<Action<TEntity, object>>
(
Expression.Assign(targetExpression,
Expression.Convert(valueParameterExpression, targetExpression.Type)),
idExpression.Parameters.Single(),
valueParameterExpression
);
assign.Compile().Invoke(entity, id);
context.Set<TEntity>().Attach(entity);
}
return entity;
}
Пример использования:
var account1 = _dbContext.LoadEntity<Account, int>(request.AccountId);
var account2 = _dbContext.LoadEntity<AccountWithComposite>(a => a.X == 1 && a.Y == 2, a => { a.X = 1; a.Y = 2; });
var account3 = _dbContext.LoadEntity<Account, int>(a => a.AccountId, request.AccountId);