The short example about Entity Framework 4 CTP4 - Part 1
A few days ago, I have read an article from
Mr. huyrua, the person that I have a deep respect. The code
that he implemented is very good to me. And I have
an idea for implementing something in Entity
Framework 4. You can go to http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern and
see the best solution on multiple ObjectContext,
according to me; it is a best solution in Entity
Framework.
Microsoft just released Microsoft ADO.NET Entity Framework Feature Community Technology Preview 4 in 2010/07/13. And I really interested in it. In this new release, I found many features very cool. And now they support Convention over Configuration on DbContext class, instead of ObjectContext class in old version. So I want to explore it in this post. Because this post maybe long, so I divided into 3 parts:
Part 1: Step up ADO.NET Entity Framework 4
CTP4
-
Prepare tool for stepping up EF
4
-
The first introduce about my
approaches
-
Build the heart of EF4
(Entity)
-
Mapping entity with
database table
-
Build your DbContext, DbContext factory
Part 2: Repository
and Unit of Work on
Entity Framework
4
-
Create the
repository
for
persistence
ignorance
-
Unit of
Work on
EF4
Part 3:
Invoke
store
procedure
in
ADO.NET
Entity
Framework
4
-
Come
with
Entity
Framework
extensions
-
Talk
about Materializer and DbCommand
-
Build
Contract
Model
for
storing
result
that
return
from
store
procedure
-
Re-code
the
DbContext
for
mapping
store
procedure
Part
1:
Step
up
ADO.NET
Entity
Framework
4
CTP4
-
Prepare
tool
for
stepping
up
EF
4
If
you
want
to
use
EF
4
CTP4,
you
must
download
it
at http://www.microsoft.com/downloads/details.aspx?FamilyID=4e094902-aeff-4ee2-a12d-5881d4b0dd3e&displayLang=en
After
installed
it,
you
shall
have
enough
tool
for
run
EF
4
CTP,
and
the
important
dll
that
you
should
focus
is
Microsoft.Data.Entity.CTP.dll,
product
version
is
4.0.30202.0.
I
usually
copy
it
to
one
folder
in
my
solution
(at
here
I
create
a
folder
with
named
[References]
and
put
that
dll
into
this
folder).
Later
we
must
reference
it
in
our
solution.
-
The
first
introduce
about
my
approaches
Why
did
I
choose
EF4
CTP4?
It
is
simple
that
EF4
CTP4
is
code-first
approach,
so
I
can
control
more
things
in
EF4.
I
can
create
model,
mapping,
db
context…
All
things
in
code-first
are
code,
I
love
it.
I
always
resist
the
problem
must
read
the
auto-generated
code
from
the
Designer
tool.
In
old
days,
we
usually
use
the
ObjectContext
for
making
the
context
for
our
solution.
So
you
must
create
the
EntityConnection
instance
for
injecting
it
into
the
ObjectContext.
It
must
be
a
concrete
instance.
Now
in
the
CTP4
version,
ADO.NET
EF4
team
introduced
the
new
class
with
named
DbContext,
this
class
using
Convention
over
Configuration
for
choosing
the
Database
Provider.
If
you
do
not
specific
the
connection
name,
DbContext
will
create
the
Database
for
you,
and
else
DbContext
will
use
the
specific
connection
name
that
you
pass
into
the
ctor
of
DbContext
class.
In
an
example
in
this
post,
I
used
DbContext
for
making
Database
Context.
-
Build
the
heart
of
EF4
(Entity)
In
the
new
methodology,
we
do
not
care
about
creating
database
schema.
We
only
focus
on
model
and
trivial
model
become
the
heart
of
modern
software.
In
this
example,
I
also
focus
on
model
and
use
the
model
to
generate
the
database
schema.
For
easy
to
understanding,
I
will
describe
something
about
my
example.
I
have
the
category
that
manages
the
news.
Certainly,
one
category
must
have
much
news.
And
I
keep
this
example
as
simple
as
possible.
After
analyze,
we
have
2
tables,
one
is
Category
and
one
is
News.
And
we
also
have
one
relationship
on
it
(one
category
has
many
news).
Before
implementing
that
2
classes,
I
must
build
the
base
class
for
them,
I
called
it
is
IEntity
public interface IEntity
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
int Id { get; set; }
}
Next
I
created
the
based
class
that
implement
the
IEntity,
purpose
of
this
base
class
is
store
something
that
you
think
it
common
and
share
for
derive-classes.
public abstract class EntityBase : IEntity
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public virtual int Id { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is delete.
/// </summary>
/// <value><c>true</c> if this instance is delete; otherwise, <c>false</c>.</value>
public virtual bool IsDelete { get; set; }
/// <summary>
/// Gets or sets the created date.
/// </summary>
/// <value>The created date.</value>
public virtual DateTime CreatedDate { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>The description.</value>
public virtual string Description { get; set; }
}
Read
the code
convention
carefully
before
you
start to
code the
entity
class http://blogs.msdn.com/b/efdesign/archive/2010/06/01/conventions-for-code-first.aspx. Next
I
implement
2
classes
(called
it is
entity)
as
below:
public class Category : EntityBase
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public virtual string Name { get; set; }
private readonly ICollection<News> _news;
/// <summary>
/// Initializes a new instance of the <see cref="Category"/> class.
/// </summary>
public Category()
{
_news = new List<News>();
}
/// <summary>
/// Gets the news.
/// </summary>
/// <value>The news.</value>
public virtual ICollection<News> News
{
[DebuggerStepThrough]
get { return _news; }
}
}
And,
public class News : EntityBase
{
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public virtual string Title { get; set; }
/// <summary>
/// Gets or sets the content.
/// </summary>
/// <value>The content.</value>
public virtual string Content { get; set; }
/// <summary>
/// Gets or sets the category.
/// </summary>
/// <value>The category.</value>
public virtual Category Category { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="News"/> class.
/// </summary>
public News()
{
}
}
-
Mapping
entity
with
database
table
One
feature
that I
like the
EF4 CTP4
is we
can
create
the
mapping
class
for
entity
with the
database
schema.
I used
to
coding
on
Fluent
NHibernate
for
short
time,
when EF
still
supported
the
mapping
class
yet, and
now it
is the
time I
try to
use the
EF4 with
mapping
classes.
Everyone
also
knew the
hell
when you
used
mapping
file on
XML in
old
days.
That
problem
happened
when you
type
wrong
something
in XML
file.
The
errors
happen
consequence,
I called
it is
chain of
errors.
Luckily,
the
community
published
the
Fluent
NHibernate,
which is
a good
ORM
mapping
I have
ever
used. It
is
integrated
with
strong
type for
typing
mapping
configuration.
And now
the EF4
CTP4
also
supports
the
same.
And even
it is
easily
understanding
than
Fluent
NHibernate.
Try to
understanding
the
relationship
in EF4
before
you
implement
mapping
class.
Mapping
for
Category:
public class CategoryMapping : EntityConfiguration<Category>
{
/// <summary>
/// Initializes a new instance of the <see cref="CategoryMapping"/> class.
/// </summary>
public CategoryMapping()
{
HasKey(a => a.Id);
Property(a => a.Id).IsIdentity();
Property(a => a.Name).IsRequired().HasMaxLength(50);
Property(a => a.IsDelete);
Property(a => a.CreatedDate);
Property(a => a.Description).HasMaxLength(500);
MapSingleType(a => new
{
ID = a.Id,
a.Name,
a.IsDelete,
a.CreatedDate,
a.Description
})
.ToTable("Categories");
}
}
And
News,
public class NewsMapping : EntityConfiguration<News>
{
/// <summary>
/// Initializes a new instance of the <see cref="NewsMapping"/> class.
/// </summary>
public NewsMapping()
{
HasKey(a => a.Id);
Property(a => a.Id).IsIdentity();
Property(a => a.Title).IsRequired().HasMaxLength(500);
Property(a => a.Content).IsRequired().HasMaxLength(1000);
Property(a => a.IsDelete);
Property(a => a.CreatedDate);
Property(a => a.Description).HasMaxLength(500);
MapSingleType(a => new
{
ID = a.Id,
CategoryId = a.Category.Id,
a.Title,
a.Content,
a.IsDelete,
a.CreatedDate,
a.Description
})
.ToTable("News");
}
}
If you
are
familiar
with
Fluent
NHibernate
mapping,
it is
enough
simple
for you
to
understanding,
isn’t
it? If
you are
new
person,
it is
not
problems.
Go to http://blogs.msdn.com/b/adonet/archive/2010/07/14/ctp4codefirstwalkthrough.aspx,
read
it, and
you
shall
clear.
-
Build
your
DbContext,
DbContext
factory
Now, we
shall
start to
investigate
the
DbContext,
new
features
in CTP4,
and next
step we
shall
implementation
the
DbContext
Factory
for
getting
new
DbContext’s
instance.
Because
the
DbContext
is very
complex
and long
code if
your
application
become
larger
in the
future,
so I
build it
is a
partial
class
and
separated
it to
small
classes.
public partial class CSPDbContext : DbContext
{
/// <summary>
/// Initializes a new instance of the <see cref="CSPDbContext"/> class.
/// </summary>
/// <param name="model">The model.</param>
public CSPDbContext(DbModel model)
: base("CallStoreProc", model)
{
}
}
public partial class CSPDbContext
{
/// <summary>
/// Called when [model creating].
/// </summary>
/// <param name="modelBuilder">The model builder.</param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new CategoryMapping());
modelBuilder.Configurations.Add(new NewsMapping());
}
/// <summary>
/// Gets or sets the categories.
/// </summary>
/// <value>The categories.</value>
public DbSet<Category> Categories { get; set; }
/// <summary>
/// Gets or sets the news.
/// </summary>
/// <value>The news.</value>
public DbSet<News> News { get; set; }
}
As you
see on
the
OnModelCreating
method,
I
overridden
it and
added 2
mapping
files
into
ModelBuilder.
And I
also
exported
2 DbSets
for
outside
could
access
and used
it.
Next, I
must
build
the
DbContext
Factory,
and
first
thing is
a
contract
for
it
And
detail's
implementation
is
public interface IDatabaseFactory : IDisposable
{
/// <summary>
/// Gets this instance.
/// </summary>
/// <returns></returns>
DbContext Get();
}
public class DatabaseFactory : IDatabaseFactory
{
private static readonly ModelBuilder builder = CreateModelBuilder();
private readonly DbProviderFactory _providerFactory;
private readonly string _connectionString;
//private ObjectContext _objectContext;
private DbContext _dbContext;
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseFactory"/> class.
/// </summary>
/// <param name="providerFactory">The provider factory.</param>
/// <param name="connectionString">The connection string.</param>
public DatabaseFactory(DbProviderFactory providerFactory, string connectionString)
{
this._providerFactory = providerFactory;
this._connectionString = connectionString;
}
/// <summary>
/// Gets this instance.
/// </summary>
/// <returns></returns>
public DbContext Get()
{
if (_dbContext == null)
{
var model = builder.CreateModel();
_dbContext = new CSPDbContext(model);
return _dbContext;
}
return _dbContext;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
[DebuggerStepThrough]
public void Dispose()
{
if (_dbContext != null)
{
_dbContext.Dispose();
}
}
/// <summary>
/// Creates the model builder.
/// </summary>
/// <returns></returns>
private static ModelBuilder CreateModelBuilder()
{
ModelBuilder modelBuilder = new ModelBuilder();
IEnumerable<Type> configurationTypes = typeof(DatabaseFactory).Assembly
.GetTypes()
.Where(type => type.IsPublic && type.IsClass && !type.IsAbstract && !type.IsGenericType && typeof(StructuralTypeConfiguration).IsAssignableFrom(type) && (type.GetConstructor(Type.EmptyTypes) != null));
foreach (StructuralTypeConfiguration configuration in configurationTypes.Select(type => (StructuralTypeConfiguration)Activator.CreateInstance(type)))
{
modelBuilder.Configurations.Add(configuration);
}
return modelBuilder;
}
}
This is
a
factory
that I
learnt
from
Shrinkr
project
on
Codeplex http://shrinkr.codeplex.com/
Prepare tool for stepping up EF 4
The first introduce about my approaches
Build the heart of EF4 (Entity)
Mapping entity with database table
Build your DbContext, DbContext factory
Create the repository for persistence ignorance
Unit of Work on EF4
Come with Entity Framework extensions
Talk about Materializer and DbCommand
Build Contract Model for storing result that return from store procedure
Re-code the DbContext for mapping store procedure
Part 1: Step up ADO.NET Entity Framework 4 CTP4
-
Prepare
tool
for
stepping
up
EF
4
If
you
want
to
use
EF
4
CTP4,
you
must
download
it
at http://www.microsoft.com/downloads/details.aspx?FamilyID=4e094902-aeff-4ee2-a12d-5881d4b0dd3e&displayLang=en
After
installed
it,
you
shall
have
enough
tool
for
run
EF
4
CTP,
and
the
important
dll
that
you
should
focus
is
Microsoft.Data.Entity.CTP.dll,
product
version
is
4.0.30202.0.
I
usually
copy
it
to
one
folder
in
my
solution
(at
here
I
create
a
folder
with
named
[References]
and
put
that
dll
into
this
folder).
Later
we
must
reference
it
in
our
solution.
-
The
first
introduce
about
my
approaches
Why
did
I
choose
EF4
CTP4?
It
is
simple
that
EF4
CTP4
is
code-first
approach,
so
I
can
control
more
things
in
EF4.
I
can
create
model,
mapping,
db
context…
All
things
in
code-first
are
code,
I
love
it.
I
always
resist
the
problem
must
read
the
auto-generated
code
from
the
Designer
tool.
In
old
days,
we
usually
use
the
ObjectContext
for
making
the
context
for
our
solution.
So
you
must
create
the
EntityConnection
instance
for
injecting
it
into
the
ObjectContext.
It
must
be
a
concrete
instance.
Now
in
the
CTP4
version,
ADO.NET
EF4
team
introduced
the
new
class
with
named
DbContext,
this
class
using
Convention
over
Configuration
for
choosing
the
Database
Provider.
If
you
do
not
specific
the
connection
name,
DbContext
will
create
the
Database
for
you,
and
else
DbContext
will
use
the
specific
connection
name
that
you
pass
into
the
ctor
of
DbContext
class.
In
an
example
in
this
post,
I
used
DbContext
for
making
Database
Context.
-
Build
the
heart
of
EF4
(Entity)
In
the
new
methodology,
we
do
not
care
about
creating
database
schema.
We
only
focus
on
model
and
trivial
model
become
the
heart
of
modern
software.
In
this
example,
I
also
focus
on
model
and
use
the
model
to
generate
the
database
schema.
For
easy
to
understanding,
I
will
describe
something
about
my
example.
I
have
the
category
that
manages
the
news.
Certainly,
one
category
must
have
much
news.
And
I
keep
this
example
as
simple
as
possible.
After
analyze,
we
have
2
tables,
one
is
Category
and
one
is
News.
And
we
also
have
one
relationship
on
it
(one
category
has
many
news).
Before
implementing
that
2
classes,
I
must
build
the
base
class
for
them,
I
called
it
is
IEntity
public interface IEntity
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
int Id { get; set; }
}
Next
I
created
the
based
class
that
implement
the
IEntity,
purpose
of
this
base
class
is
store
something
that
you
think
it
common
and
share
for
derive-classes.
Prepare
tool
for
stepping
up
EF
4
The
first
introduce
about
my
approaches
Build
the
heart
of
EF4
(Entity)
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
int Id { get; set; }
}
public abstract class EntityBase : IEntity
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public virtual int Id { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is delete.
/// </summary>
/// <value><c>true</c> if this instance is delete; otherwise, <c>false</c>.</value>
public virtual bool IsDelete { get; set; }
/// <summary>
/// Gets or sets the created date.
/// </summary>
/// <value>The created date.</value>
public virtual DateTime CreatedDate { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>The description.</value>
public virtual string Description { get; set; }
}
public class Category : EntityBase
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public virtual string Name { get; set; }
private readonly ICollection<News> _news;
/// <summary>
/// Initializes a new instance of the <see cref="Category"/> class.
/// </summary>
public Category()
{
_news = new List<News>();
}
/// <summary>
/// Gets the news.
/// </summary>
/// <value>The news.</value>
public virtual ICollection<News> News
{
[DebuggerStepThrough]
get { return _news; }
}
}
public class News : EntityBase
{
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public virtual string Title { get; set; }
/// <summary>
/// Gets or sets the content.
/// </summary>
/// <value>The content.</value>
public virtual string Content { get; set; }
/// <summary>
/// Gets or sets the category.
/// </summary>
/// <value>The category.</value>
public virtual Category Category { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="News"/> class.
/// </summary>
public News()
{
}
}
Mapping
entity
with
database
table
public class CategoryMapping : EntityConfiguration<Category>
{
/// <summary>
/// Initializes a new instance of the <see cref="CategoryMapping"/> class.
/// </summary>
public CategoryMapping()
{
HasKey(a => a.Id);
Property(a => a.Id).IsIdentity();
Property(a => a.Name).IsRequired().HasMaxLength(50);
Property(a => a.IsDelete);
Property(a => a.CreatedDate);
Property(a => a.Description).HasMaxLength(500);
MapSingleType(a => new
{
ID = a.Id,
a.Name,
a.IsDelete,
a.CreatedDate,
a.Description
})
.ToTable("Categories");
}
}
public class NewsMapping : EntityConfiguration<News>
{
/// <summary>
/// Initializes a new instance of the <see cref="NewsMapping"/> class.
/// </summary>
public NewsMapping()
{
HasKey(a => a.Id);
Property(a => a.Id).IsIdentity();
Property(a => a.Title).IsRequired().HasMaxLength(500);
Property(a => a.Content).IsRequired().HasMaxLength(1000);
Property(a => a.IsDelete);
Property(a => a.CreatedDate);
Property(a => a.Description).HasMaxLength(500);
MapSingleType(a => new
{
ID = a.Id,
CategoryId = a.Category.Id,
a.Title,
a.Content,
a.IsDelete,
a.CreatedDate,
a.Description
})
.ToTable("News");
}
}
Build
your
DbContext,
DbContext
factory
public partial class CSPDbContext : DbContext
{
/// <summary>
/// Initializes a new instance of the <see cref="CSPDbContext"/> class.
/// </summary>
/// <param name="model">The model.</param>
public CSPDbContext(DbModel model)
: base("CallStoreProc", model)
{
}
}
public partial class CSPDbContext
{
/// <summary>
/// Called when [model creating].
/// </summary>
/// <param name="modelBuilder">The model builder.</param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new CategoryMapping());
modelBuilder.Configurations.Add(new NewsMapping());
}
/// <summary>
/// Gets or sets the categories.
/// </summary>
/// <value>The categories.</value>
public DbSet<Category> Categories { get; set; }
/// <summary>
/// Gets or sets the news.
/// </summary>
/// <value>The news.</value>
public DbSet<News> News { get; set; }
}
public interface IDatabaseFactory : IDisposable
{
/// <summary>
/// Gets this instance.
/// </summary>
/// <returns></returns>
DbContext Get();
}
public class DatabaseFactory : IDatabaseFactory
{
private static readonly ModelBuilder builder = CreateModelBuilder();
private readonly DbProviderFactory _providerFactory;
private readonly string _connectionString;
//private ObjectContext _objectContext;
private DbContext _dbContext;
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseFactory"/> class.
/// </summary>
/// <param name="providerFactory">The provider factory.</param>
/// <param name="connectionString">The connection string.</param>
public DatabaseFactory(DbProviderFactory providerFactory, string connectionString)
{
this._providerFactory = providerFactory;
this._connectionString = connectionString;
}
/// <summary>
/// Gets this instance.
/// </summary>
/// <returns></returns>
public DbContext Get()
{
if (_dbContext == null)
{
var model = builder.CreateModel();
_dbContext = new CSPDbContext(model);
return _dbContext;
}
return _dbContext;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
[DebuggerStepThrough]
public void Dispose()
{
if (_dbContext != null)
{
_dbContext.Dispose();
}
}
/// <summary>
/// Creates the model builder.
/// </summary>
/// <returns></returns>
private static ModelBuilder CreateModelBuilder()
{
ModelBuilder modelBuilder = new ModelBuilder();
IEnumerable<Type> configurationTypes = typeof(DatabaseFactory).Assembly
.GetTypes()
.Where(type => type.IsPublic && type.IsClass && !type.IsAbstract && !type.IsGenericType && typeof(StructuralTypeConfiguration).IsAssignableFrom(type) && (type.GetConstructor(Type.EmptyTypes) != null));
foreach (StructuralTypeConfiguration configuration in configurationTypes.Select(type => (StructuralTypeConfiguration)Activator.CreateInstance(type)))
{
modelBuilder.Configurations.Add(configuration);
}
return modelBuilder;
}
}