Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the NHibernate session.Load equivalent in Entity Framework?

What's the following NHibernate lazy loading equivalent to Entity Framework?

product.Categories.Add(s.Load<Category>(cat));

I tried this, but it read the Category table from the database:

product.Categories.Add(db.Categories.Find(cat));
like image 833
Hao Avatar asked Jul 15 '11 12:07

Hao


2 Answers

There is no equivalent and there will probably never be because of a fundamental design flaw in how EF does lazy-loading.

I asked your exact question a while ago in MS' forums: http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/fccfcf68-2b53-407f-9a87-a32426db6f36

like image 151
Diego Mijelshon Avatar answered Oct 20 '22 13:10

Diego Mijelshon


While Entity Framework doesn't have an exact equivalent to the Load() method, it does provide the ability to persist a new association without first retrieving an entity or exposing foreign key properties (i.e. arguably the most common use of NHibernate's Load method).

The following extension methods either return an entity already tracked or returns a newly attached entity which can then be used to save new associations:

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;
    }

Example usage:

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);
like image 31
Derek Greer Avatar answered Oct 20 '22 15:10

Derek Greer