[Entity Framework] DbContext API – DbSet Class

When you are working with DbContext, you are actually working with entity sets. DbSet represents a typed entity set that is used to perform create, read, update, and delete operations. You are not creating DbSet objects and using them indepedently. DbSet can be only used with DbContext.

 

1. DbSet

DbSet implements IQueryable and IEnumerable interfaces, therefore you can use it with LINQ.

The basic structure of the class is like this:

public class DbSet<TEntity> : DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable
{
  public ObservableCollection<TEntity> Local { get; }  // entities of the set that are currently being tracked by the context

  public TEntity Add(TEntity entity);
  public TEntity Attach(TEntity entity);
  public TEntity Create();
  public TEntity Remove(TEntity entity);

  public TEntity Find(params Object[] keyValues);

  public DbSqlQuery<TEntity> SqlQuery(string sql, params Object[] parameters);
}

 

2. Adding a Set to Context

The first step to use a set is to add a DbSet object to DbContext.

  • Create a subclass of DbContext 
  • Add a property as a reference to the stronly typed DbSet
public class ProductContext : DbContext
{
  public DbSet<Category> Categories { get; set; }
  public DbSet<Product> Products { get; set; }
}

 

3. Adding and Removing Enitities To/From a Set

In the first version of EF, you needed to use the “AddObject(string entitySetName, Object entity)” method to add an entity to a set.

In EF 4, ObjectSet was introduced and you can use the strongly typed “AddObject(TEntity entity)” method.

context.AddObject("Products", p);
context.Products.AddObject(p);  // EF 4

DbContext does not provide the general “AddObject” method any more. So you need to use DbSet‘s strongly typed methods:

  • public TEntity Add(TEntity entity)
  • public TEntity Remove(TEntity entity)
context.Products.Add(p);

Note that the “Object” suffix is also removed from DbSet.

 

4. Entity States

Any operation to a DbSet will change the status of its entities.

System.Data.EntityState” enum represents the state of an entity.

  • Detached : An entity exists but is not being tracked.
  • Unchanged :  An entity has not been modified
  • Added: An entity is new and has been added to the context. After the changes are saved, the state changes to “Unchanged“.
  • Deleted: An entity has been deleted from the context. After the changes are saved, the state changes to “Detached“.
  • Modified: One of the properties on the entity was modified. After the changes are saved, the state changes to “Unchanged“.

For example, added entites are marked “Added” and removed entities are marked as “Deleted“.

 

5. Attaching Entities to a Set

Adding and attaching look similar but they differ how the states are handled.

  • public TEntity Add(TEntity entity);
  • public TEntity Attach(TEntity entity);

When an entity is added to a set, the status becomes “Added“. When the context is saved, the “Added” entities are inserted into the database and the entity status changes to “Unchanged“.

When an entity is attached, the status is “Unchanged“. It is used to repopulate a context with an entity that is known to already exist in the database. “SaveChanges()” will not insert an attached entity into the database.

 

6. Finding an Entity with its Key Value

One of the most frequent operation of database programming is to retrieve an entity by providing its key value, such as finding a Product object with an id.

  • public TEntity Find(params Object[] keyValues);

You can provides key values as a parameter list and the method will return the matched entity object or null.

Even though you can do this using LINQ, DbContext’s Find() method provides the easier way to do the task. Also it has more benefits:

  • The Find() method does not query to database when the target entity is already in memory unlike “SingleOrDefault()”.
  • The Find() also returns an entity that has been added to the context but have not yet been saved to the database.
using (var context = new ProductContext())
{
  context.Products.Add(new Product { Id = 1, Name = "Toy" });

  int productId = 1;

  Product p = context.Products.Find(productId); // Not added to db yet
  Console.WriteLine("{0}: {1}", p.Id, p.Name);

  Product p1 = context.Products.SingleOrDefault(x => x.Id == productId);
  Console.WriteLine("{0}", p1 == null); // cannot find the entity
}

 

7. Checking Underlying SQL Statement

Sometimes it is very useful to know how your LINQ to Entity query is translated into a SQL statement.

Let’s check what the type of a query is?

var query = from p in context.Products select p;
Console.WriteLine(query.GetType().FullName);

Surprisingly (might not), the type is “System.Data.Entity.Infrastructure.DbQuery“.

DBQuery overrides the “ToString()” method for you to return the underlying query. It is very simple and useful!!!

var query = from p in context.Products
            select p;
Console.WriteLine(query.ToString());

 

8. Loading Data

The “Load()” method is an extension method on IQueryable that enumerates the results of the query. This is equivalent to calling “ToList()” without actually creating the list.

  • public static void Load(this IQueryable source)

It is useful to populate the “Local” property.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s