Tuesday, August 2, 2011

Entity Framework's NHibernate session.Load

public static class Helpers
{

    public static Ent LoadStub<Ent>(this DbContext db, object id) where Ent : class
    {
        string primaryKeyName = typeof(Ent).Name + "Id";
        return db.LoadStub<Ent>(primaryKeyName, id);
    }

    public static Ent LoadStub<Ent>(this DbContext db, string primaryKeyName, object id) where Ent: class
    {
        var cachedEnt = 
            db.ChangeTracker.Entries().Where(x => ObjectContext.GetObjectType(x.Entity.GetType()) == typeof(Ent)).SingleOrDefault(x =>
            {
                var entType = x.Entity.GetType();
                var value = entType.InvokeMember(primaryKeyName, System.Reflection.BindingFlags.GetProperty, null, x.Entity, new object[] { });

                return value.Equals(id);
            });

        if (cachedEnt != null)
        {
            return (Ent) cachedEnt.Entity;
        }
        else
        {
            Ent stub = (Ent) Activator.CreateInstance(typeof(Ent));

            
            typeof(Ent).InvokeMember(primaryKeyName, System.Reflection.BindingFlags.SetProperty, null, stub, new object[] { id });


            db.Entry(stub).State = EntityState.Unchanged;

            return stub;
        }

    }
}

So instead of using this pattern:
movie.Genres = movie.Genres ?? new List<Genre>();
movie.Genres.Clear();
foreach (int g in input.SelectedGenres)
{
    DbEntityEntry<Genre> cachedGenre = db.ChangeTracker.Entries<Genre>().SingleOrDefault(x => x.Entity.GenreId == g);

    Genre gx = null;
    if (cachedGenre != null)
        gx = cachedGenre.Entity;
    else
    {
        gx = new Genre { GenreId = g };
        db.Entry(gx).State = EntityState.Unchanged;
    }

    movie.Genres.Add(gx);
                       
}

We could now use this:
movie.Genres = movie.Genres ?? new List<Genre>();
movie.Genres.Clear();
foreach (int g in input.SelectedGenres)
    movie.Genres.Add(db.LoadStub<Genre>(g));


Sample use: http://www.ienablemuch.com/2011/07/using-checkbox-list-on-aspnet-mvc-with_16.html

Entity Framework 4.1's NHibernate session.Evict(entity);

If you encountered this error..

System.InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

..you may want to evict your object. You can use this routine, granted that the program can access the old entity memory reference.

// (dbContext as IObjectContextAdapter).ObjectContext.Detach(entity);  // old EF of yore
dbContext.Entry(entity).State = System.Data.EntityState.Detached;


However, if you won't be able to reference the old entity, you can use the following helper to force-evict an entity based on its primary key:


Another approach for force-evicting object that already exists in the ObjectStateManager

private static void Evict(DbContext ctx, Type t, 
    string primaryKeyName, object id)
{            
    var cachedEnt =
        ctx.ChangeTracker.Entries().Where(x =>   
            ObjectContext.GetObjectType(x.Entity.GetType()) == t)
            .SingleOrDefault(x =>
        {
            Type entType = x.Entity.GetType();
            object value = entType.InvokeMember(primaryKeyName, 
                                System.Reflection.BindingFlags.GetProperty, null, 
                                x.Entity, new object[] { });

            return value.Equals(id);
        });

    if (cachedEnt != null)
        ctx.Entry(cachedEnt.Entity).State = EntityState.Detached;
}


Sample use:

Evict(yourDbContextHere, typeof(Product), "ProductId", 1);

Monday, August 1, 2011

Update on ASP.NET MVC checkbox list on EF

Regarding Using checkbox list on ASP.NET MVC with Entity Framework 4.1

The ConcurrencyCheck attribute is replaced with Timestamp attribute. Using Timestamp attribute, we will not need the Unchanged work-around anymore:

db.Entry(movie).Property("Version").OriginalValue = input.TheMovie.Version;
// db.Entry(movie).State = System.Data.EntityState.Unchanged; // not needed anymore when using Timestamp attribute

Formatting your code on Visual Studio

On Visual Studio, if you have misaligned lines of code, to align them altogether, highlight the misaligned code code, then press Ctrl+E+F

        class MyClass
        {
    void A()
    {
            int i = 1;
        if (i == 1)
            {
                Console.WriteLine(""); }
  }
    }

Result:
    class MyClass
    {
        void A()
        {
            int i = 1;
            if (i == 1)
            {
                Console.WriteLine("");
            }
        }
    }


That functionality can also be reached under Edit > Advanced > Format Selection.

To format the whole code, press Ctrl+E+D

Razor tag nuances

Instead of doing this:

@{
    int i = 2;
    if (i == 2)
    {
        <text>Whatever great @i</text>
    }
}

You can do this:
@{
    
    int can = 3;
    if (can == 3) {
        @:Whatever great @can  
    }
}