Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Querying objects after AddObject before SaveChanges?

In EntityFramework, is that possible to query the objects that have just been added to the context using AddObject but before calling the SaveChanges method?

Thanks

like image 224
StarCub Avatar asked Aug 09 '11 01:08

StarCub


3 Answers

To persist an entity you usually add it to it's DbSet in the context.

For example

var bar = new Bar();
bar.Name = "foo";
var context = new Context();
context.Bars.Add(bar);

Surprisingly, querying context.Bars, the just added entity cannot be found

var howMany = context.Bars.Count(b => b.Name == "foo");
// howMany == 0

After context.SaveChanges() the same line will result 1

The DbSet seems unaware to changes until they're persisted on db.

Fortunately, each DbSet has a Local property that acts like the DbSet itself, but it reflect all in-memory operations

var howMany = context.Bars.Local.Count(b => b.Name == "foo");
// howMany == 1

You can also use Local to add entities

context.Bars.Local.Add(bar);

and get rid of the weird behavior of Entity Framework.

like image 192
Arialdo Martini Avatar answered Nov 07 '22 07:11

Arialdo Martini


you can query objects like this,

context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(obj => obj.Entity).OfType<TheEntityType>()

this will query the objects which are in added state. If you want other states too you can pass all other states to GetObjectStateEntries method like this.

GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged)
like image 44
Jayantha Lal Sirisena Avatar answered Nov 07 '22 07:11

Jayantha Lal Sirisena


In hibernate transient instances are already attached to context. Just stumbled upon this EF restriction.

I did not managed to intersect/union the ObjectSet with its transient entities ObjectSet.Local but for our usecase the below find method is sufficient.

In our cases we create some entities lazy depending on unique criteria during an iteration

Find method

If you are using an repository pattern you can create a method like:

public interface IRepository<T> where T : class, IEntity
{
    /// <summary>
    /// Finds the unique Entity with the given predicate.
    /// Depending on implementation also checks transient / local (unsaved) Entities.
    /// </summary>
    /// <param name="predicate"></param>
    /// <returns></returns>
    IQueryable<T> FindAll(Expression<Func<T, bool>> predicate);
}

public class EfRepository<T> : IRepository<T> where T : class, IEntity
{
    protected readonly ObjectContext context;
    protected readonly ObjectSet<T> objectSet;

    /// <summary>
    /// Creates a new repository of the given context.
    /// </summary>
    /// <param name="context"></param>
    public EfRepository(ObjectContext context)
    {
        if (context == null)
            throw new ArgumentException("Context must not be null.");
        this.context = context;
        this.objectSet = context.CreateObjectSet<T>();
    }

    /// <summary>
    /// Also takes local context into consideration for unsaved changes
    /// </summary>
    /// <param name="predicate"></param>
    /// <returns></returns>
    public T Find(Expression<Func<T, bool>> predicate)
    {
        T result = this.objectSet.Where(predicate).FirstOrDefault();
        if (result == null)
            result = this.objectSet.Local().Where(predicate).FirstOrDefault();
        return result;
    }
}
like image 1
djmj Avatar answered Nov 07 '22 08:11

djmj