Using the Entity Framework Repository and UnitOfWork Pattern in C# ASP .NET

Introduction

Web application data access layers have evolved over time to provide increasing flexibility and maintainability in software architecture. Often, many web applications begin by manually maintaining database connections and SQL query strings. However, as architecture designs grow, more web applications are using ORM models to help increase code reuse and maintainability of ever-growing C# ASP .NET web applications.

The latest version of the Entity Framework EF4 provides several new features, which support implementing a loosely-coupled repository pattern design. Using the Repository Pattern, along with a UnitOfWork pattern, we can greatly enhance the maintainability of our web applications with a solid architecture, greater code reuse, and unit test capability.

In this article, we’ll step through 3 methods of accessing the Entity Framework data access layer in C# ASP .NET. We’ll then detail an implementation of the Repository and UnitOfWork Pattern to get the most out of the design.

Method 1: The Beginner’s Out-of-the-Box Entity Framework Design

The Entity Framework is quite powerful. With a few clicks in Visual Studio, we can instantly create an ORM relational model of our database, ready for querying. One of the most simplest implementation of accessing data with the Entity Framework in a C# ASP .NET web application would be the following:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

void Main()

{

// Instantiate the Entity Framework data context in a using statement.

using (MyEntities context = new MyEntities())

{

// Create a dragon.

Dragon dragon = new Dragon();

dragon.Name = "Fang";

dragon.Age = 210;

// Create a weapon.

Weapon weapon = new Weapon();

weapon.Name = "Fire";

weapon.Type = "Breath";

weapon.Damage = 100;

// Set the dragon's weapon.

dragon.Weapon = weapon;

context.AddToDragons(dragon);

// Save the dragon and weapon to the database.

context.SaveChanges();

}

}

In the above code, we’ve implemented the basic Entity Framework data context within a using block. The using block will automatically dispose of the context object upon completion. Note, the database connection itself is opened and closed automatically upon calling SaveChanges().

The basic C# ASP .NET Entity Framework sample demonstrates using the ORM objects and persisting to the database. However, a more complicated web application, containing large numbers of business rules for saving and relating entities, could get quite complex using the above design. For example, what if the Dragon object could only accept weapons of type “Breath”? A new block of code would need to be inserted to perform the check. Similarly, a new block of code may need to be added to the Weapon as well. To abstract the more complicated business logic that may need to be added, we can utilize the Repository Pattern.

The Repository Pattern is a Repository .. is a Repository

The C# ASP .NET Repository Pattern is a layer created to manage the persistence of data to and from the Entity Framework ORM. While the repository pattern is often extended to include business logic (unrelated to persistence), it is often a cleaner approach to keep the repository pattern strictly for data persistence. A separate business manager class or repository wrapper can be created to manage business logic. In either case, as your repository business logic spans across the various domain objects, you’ll find yourself requiring the Entity Framework data context in each method, in order to save data.

Multiple Entity Framework Context Blocks

One way of resolving the issue of using multiple repositories for a single work transaction may be to open and close individual Entity Framework context objects, within using blocks, throughout the code. However, for more data expensive C# ASP .NET web applications, executing queries over many database connections would be less than optimal.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

voidPage_Load()

{

// Create a weapon. A database connection will be opened and closed.

Weapon weapon = WeaponRepositoryManager.CreateWeapon();

// Create a dragon with the weapon. A database connection will be opened and closed.

DragonRepositoryManager.CreateDragon(weapon);

}

publicstaticclass DragonRepositoryManager

{

publicstaticvoidCreateDragon()

{

using (MyEntities context = new MyEntities())

{

// Do some work with the dragon.

// Check business rules.

}

}

}

publicstaticclassWeaponRepositoryManager()

{

publicstatic Weapon CreateWeapon()

{

using (MyEntities context = new MyEntities())

{

// Do some work with the weapon.

// Check business rules.

}

}

}

In the above code, you can see how two separate queries will be executed against the database, each one requiring a database connection. For better performance, the queries could be combined into a single database query and transaction. However, to continue using the repository pattern and it’s separation of code concerns, we’ll need a way of sharing the context between them. This is where the UnitOfWork pattern comes in.

Method 2: Sharing the Context with the UnitOfWork Pattern

The UnitOfWork pattern is a design for grouping a set of tasks into a single group of transactional work. The UnitOfWork pattern is the solution to sharing the Entity Framework data context across multiple managers and repositories. The UnitOfWork pattern allows us to execute a single database transaction (implicitly as part of the Entity Framework), which spans across multiple blocks of code, methods, classes, and repositories.

There are two primary ways to implement the Repository and UnitOfWork patterns. One method allows individual “using” blocks, similar to the above code, but sharing the context between them. For example:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

publicstaticclassDragonRepositoryManager

{

publicstaticvoidCreateDragon()

{

Dragon dragon = new Dragon();

using (MyEntities context = new MyEntities())

{

UnitOfWork uow = new UnitOfWork(context);

// ...

Weapon weapon = uow.Weapons.GetById(12);

dragon.Weapon = weapon;

uow.Dragons.Add(dragon);

uow.Commit();

}

return dragon;

}

}

Notice in the above code, we’ve shared the Entity Framework’s data context across the two repositories. Both, the Dragon and Weapon repositories are using the same data context to persist and query in the database. The UnitOfWork pattern, in this case, holds a copy of the data context and passes it to the repositories for usage. The actual database SQL execution is delayed until calling uow.Commit(). We can implement this design using the code shown below.

With the framework setup, we can define the concrete repository implementations as follows:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

publicclassDragonRepository : Repository<Dragon>

{

public DragonRepository(ObjectContext context)

: base(context)

{

}

publicoverride Dragon GetById(object id)

{

return _objectSet.SingleOrDefault(s => s.DragonId == (int)id);

}

}

publicclassWeaponRepository : Repository<Weapon>

{

public WeaponRepository(ObjectContext context)

: base(context)

{

}

publicoverride Weapon GetById(object id)

{

return _objectSet.SingleOrDefault(s => s.WeaponId == (int)id);

}

}

While the above design allows us to share the Entity Framework context across repositories and query within individual using blocks, it still doesn’t allow us to share a context across multiple classes or repository managers. For this, we would need to pass the UnitOfWork around between the classes. Rather than passing the UnitOfWork object, we can instead create a static global implementation, per http web request, which may better suit an enterprise architecture.

Method 3: A Global Context with the UnitOfWork Pattern

Another method for managing the Entity Framework Repository and UnitOfWork pattern is by using a global UnitOfWork object. The UnitOfWork object will have a lifetime of a single HTTP web request. Once the web request completes, the Entity Framework context is disposed. This can be considered as having one big using block around the web application code-base. In this design, each repository can query against the Entity Framework ORM, without requiring individual using blocks. Once all necessary business logic has been performed, a final UnitOfWork.Commit() can be called to execute the database queries and persist data.

For example, the global UnitOfWork pattern allows us to utilize code as follows:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

voidPage_Load()

{

// Create a weapon. No SQL is executed yet.

Weapon weapon = WeaponManager.CreateWeapon();

// Create a dragon with the weapon. No SQL is executed yet.

DragonManager.CreateDragon(weapon);

// The complete unit of work is executed and changes committed to the database.

UnitOfWork.Commit();

}

publicstaticclass DragonManager

{

publicstaticvoidCreateDragon(Weapon weapon)

{

Dragon dragon = new Dragon();

// Do some work with the dragon.

// Check business rules.

dragon.Weapon = weapon;

_dragonRepository.Add(dragon);

}

}

publicstaticclassWeaponManager()

{

publicstatic Weapon CreateWeapon()

{

Weapon weapon = new Weapon();

// Do some work with the weapon.

// Check business rules.

_weaponRepository.Add(weapon);

}

}

Notice in the above code, the main method calls two separate repository business managers, which query two individual repositories. The actual database execution is delayed until the final UnitOfWork.Commit() call is executed. This allows the complete unit of work (creation of a Weapon and a Dragon entity) to occur within the same transactional context. We can easily call multiple repositories across methods and classes, and still utilize the same UnitOfWork database context. The actual context is persisted per HTTP web request, allowing a thread-safe solution. The global Repository and UnitOfWork pattern can be implemented as discussed below, with original design from Entity Framework 4.0 POCO ObjectSet, Repository and UnitOfWork.

The Generic IRepository Interface

We’ll define three generic interfaces to support the repository pattern, unitofwork pattern, and a factory for producing UnitOfWork objects.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

public interface IRepository<T> where T : class

{

IQueryable<T> GetQuery();

IEnumerable<T> GetAll();

IEnumerable<T> Find(Expression<Func<T, bool>> where);

T Single(Expression<Func<T, bool>> where);

T First(Expression<Func<T, bool>> where);

voidDelete(T entity);

voidAdd(T entity);

voidAttach(T entity);

voidSaveChanges();

}

public interface IUnitOfWork : IDisposable

{

voidCommit();

}

public interface IUnitOfWorkFactory

{

IUnitOfWork Create();

}

By using a generic template interface, we can bind repositories to individual Entity Framework entities and reuse the same interface for all required repositories. In effect, each entity in the database will have its own repository.

UnitOfWork Implementation

The core to the global Repository and UnitOfWork design is the base UnitOfWork class itself. This class will take care of providing a thread-safe unit of work pattern, committing data, and persisting itself for the lifetime of the context. For web applications, the lifetime will be per HTTP request. For desktop applications, the lifetime will be per thread.

Notice the above code is using the HttpConext.Current.Items[] array to store the actual UnitOfWork context. For web applications, a new context will be created for each web request. We can create a concrete implementation of the UnitOfWork interface, specifically for the Entity Framework, as follows:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

publicclassEFUnitOfWork : IUnitOfWork, IDisposable

{

public ObjectContext Context { get; privateset; }

public EFUnitOfWork(ObjectContext context)

{

Context = context;

context.ContextOptions.LazyLoadingEnabled = true;

}

public void Commit()

{

Context.SaveChanges();

}

public void Dispose()

{

if (Context != null)

{

Context.Dispose();

Context = null;

}

GC.SuppressFinalize(this);

}

}

The above implementation of IUnitOfWork simply wraps the C# ASP .NET Entity Framework. The constructor takes an ObjectContext and implements committing the changes and disposing of the context. The Dispose method will be called automatically by garbage collection. However, to tie up loose ends, this method can be called from the Global.asax event Application_EndRequest().

To help decouple the UnitOfWork implementation details from the layers of the design, we’ll include a factory pattern, as follows:

The factory pattern simply calls the delegate method for instantiating a new UnitOfWork context. We’ll provide the setup for this delegate method in the initialization code for the C# ASP .NET web application, similar to the following code:

1

2

// Select an Entity Framework modeltousewith the factory.

EFUnitOfWorkFactory.SetObjectContext(() => new MyEntities());

The Entity Framework Repository Pattern

With the UnitOfWork implementation complete, we can define a concrete base class for the generic repository pattern. This implementation will be designed for the Entity Framework.

The above repository implementation obtains an Entity Framework context from the UnitOfWork class. It also instantiates an ObjectSet collection for the specific entity this repository will handle. The repository class also provides a set of common query methods for accessing the entity data, with support for LINQ queries.

Initializing the repository pattern can be performed within the Global.asax event Application_Start() as follows:

Repository Business Logic Managers

The Repository and UnitOfWork pattern are now complete for the Entity Framework. While the repositories provide the necessary methods for querying the Entity Framework objects, most C# ASP .NET web applications will require additional business logic for manipulating entities and persisting data. This type of logic can be added within manager classes, which wrap the repositories, and utilize/share the global UnitOfWork object.

Example Using the Repository and UnitOfWork Pattern

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

protectedvoidPage_Load()

{

if (!IsPostBack)

{

BindGrid();

}

}

privatevoidBindGrid()

{

lstDragons.Items.Clear();

// Call the repository manager to query all Dragon entities.

foreach (Dragon dragon in DragonManager.GetAll())

{

lstDragons.Items.Add(dragon.Name);

}

}

protectedvoidbtnAdd_Click()

{

// Create a weapon. No SQL is executed yet.

Weapon weapon = WeaponManager.CreateWeapon();

// Create a dragon with the weapon. No SQL is executed yet.

DragonManager.CreateDragon(weapon);

// The complete unit of work is executed and changes committed to the database.

Notice in the above code, we’ve created individual business managers, which wrap the repositories. This allows us to perform specific business functionality along with the processing of Entity Framework repository entities. The managers themselves do not commit to the database, although they certainly could if required. Instead, upon an HTTP request, any business logic executed will queue up SQL calls to finally be executed at the end of the web page event, providing a single transactional event per web request.

We take advantage of the Global.asax EndRequest event to dispose of the UnitOfWork and Entity Framework context. While disposing would occur automatically via garbage collection, and database connections are automatically managed by the Entity Framework context (opening and closing the database connection only upon calling SaveChanges), it’s still good practice to be aware of the physical creation and disposing of the context. An added benefit to the design of the business managers is that they provide an easy access point for unit testing and TDD test driven development.

Conclusion

The C# ASP .NET Entity Framework is a powerful ORM for managing database entities in an object oriented design. By enhancing .NET web application design and providing a Repository and UnitOfWork architecture, benefits can be achieved in code reuse, maintainability, and support for unit testing. Two primary implementations of the Entity Framework Repository and UnitOfWork pattern provide a design for persisting entities within individual blocks of context or via a global context per web request. The Repository and UnitOfWork pattern provide a solid architecture to help take C# ASP .NET web applications into the future.

About the Author

This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.