wpf-403740-controls-and-libraries-data-grid-bind-to-data-bind-to-entity-framework-sources.md
Entity Framework is an ORM library that helps you access and process data stored in-memory or within traditional database engines. This article describes how to use Entity Framework with the DevExpress WPF Data Grid.
This example includes multiple solutions that demonstrate:
View Example: Bind the WPF Data Grid to Data
The example uses the Issues Service database that stores data for two entities: users and issues assigned to these users.
Each Entity Framework project includes the common Issues folder. Files in this folder define the Entity Framework-related logic:
OutlookDataGenerator.cs Generates data.IssueContext.cs Defines the DbContext.IssueContextInitializer.cs Initializes the database with generated data.Issue.cs and User.cs Define entities.
OutlookDataGenerator.cs / OutlookDataGenerator.vb
using System;
namespace EntityFrameworkIssues.Issues {
public static class OutlookDataGenerator {
static Random rnd = new Random(0);
static string[] Subjects = new string[] { "Developer Express MasterView. Integrating the control into an Accounting System.",
"Web Edition: Data Entry Page. There is an issue with date validation.",
"Payables Due Calculator is ready for testing.",
"Web Edition: Search Page is ready for testing.",
"Main Menu: Duplicate Items. Somebody has to review all menu items in the system.",
"Receivables Calculator. Where can I find the complete specs?",
"Ledger: Inconsistency. Please fix it.",
"Receivables Printing module is ready for testing.",
"Screen Redraw. Somebody has to look at it.",
"Email System. What library are we going to use?",
"Cannot add new vendor. This module doesn't work!",
"History. Will we track sales history in our system?",
"Main Menu: Add a File menu. File menu item is missing.",
"Currency Mask. The current currency mask in completely unusable.",
"Drag & Drop operations are not available in the scheduler module.",
"Data Import. What database types will we support?",
"Reports. The list of incomplete reports.",
"Data Archiving. We still don't have this features in our application.",
"Email Attachments. Is it possible to add multiple attachments? I haven't found a way to do this.",
"Check Register. We are using different paths for different modules.",
"Data Export. Our customers asked us for export to Microsoft Excel"};
public static readonly string[] Users = new string[] {
"Peter Dolan",
"Ryan Fischer",
"Richard Fisher",
"Tom Hamlett",
"Mark Hamilton",
"Steve Lee",
"Jimmy Lewis",
"Jeffrey McClain",
"Andrew Miller",
"Dave Murrel",
"Bert Parkins",
"Mike Roller",
"Ray Shipman",
"Paul Bailey",
"Brad Barnes",
"Carl Lucas",
"Jerry Campbell",
};
public static string GetSubject() {
return Subjects[rnd.Next(Subjects.Length - 1)];
}
public static string GetFrom() {
return Users[rnd.Next(Users.Length)];
}
public static Priority GetPriority() {
return (Priority)rnd.Next(5);
}
}
}
Imports System
Namespace Issues
Public Module OutlookDataGenerator
Private rnd As Random = New Random(0)
Private Subjects As String() = New String() {"Developer Express MasterView. Integrating the control into an Accounting System.", "Web Edition: Data Entry Page. There is an issue with date validation.", "Payables Due Calculator is ready for testing.", "Web Edition: Search Page is ready for testing.", "Main Menu: Duplicate Items. Somebody has to review all menu items in the system.", "Receivables Calculator. Where can I find the complete specs?", "Ledger: Inconsistency. Please fix it.", "Receivables Printing module is ready for testing.", "Screen Redraw. Somebody has to look at it.", "Email System. What library are we going to use?", "Cannot add new vendor. This module doesn't work!", "History. Will we track sales history in our system?", "Main Menu: Add a File menu. File menu item is missing.", "Currency Mask. The current currency mask in completely unusable.", "Drag & Drop operations are not available in the scheduler module.", "Data Import. What database types will we support?", "Reports. The list of incomplete reports.", "Data Archiving. We still don't have this features in our application.", "Email Attachments. Is it possible to add multiple attachments? I haven't found a way to do this.", "Check Register. We are using different paths for different modules.", "Data Export. Our customers asked us for export to Microsoft Excel"}
Public ReadOnly Users As String() = New String() {"Peter Dolan", "Ryan Fischer", "Richard Fisher", "Tom Hamlett", "Mark Hamilton", "Steve Lee", "Jimmy Lewis", "Jeffrey McClain", "Andrew Miller", "Dave Murrel", "Bert Parkins", "Mike Roller", "Ray Shipman", "Paul Bailey", "Brad Barnes", "Carl Lucas", "Jerry Campbell"}
Public Function GetSubject() As String
Return Subjects(rnd.Next(Subjects.Length - 1))
End Function
Public Function GetFrom() As String
Return Users(rnd.Next(Users.Length))
End Function
Public Function GetPriority() As Priority
Return CType(rnd.Next(5), Priority)
End Function
End Module
End Namespace
IssuesContext.cs / IssuesContext.vb
using System.Data.Entity;
namespace EntityFrameworkIssues.Issues {
public class IssuesContext : DbContext {
static IssuesContext() {
Database.SetInitializer(new IssuesContextInitializer());
}
public IssuesContext() { }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Issue>()
.HasIndex(x => x.Created);
modelBuilder.Entity<Issue>()
.HasIndex(x => x.Votes);
}
public DbSet<Issue> Issues { get; set; }
public DbSet<User> Users { get; set; }
}
}
Imports System.Data.Entity
Namespace Issues
Public Class IssuesContext
Inherits DbContext
Shared Sub New()
Database.SetInitializer(New IssuesContextInitializer())
End Sub
Public Sub New()
End Sub
Protected Overrides Sub OnModelCreating(ByVal modelBuilder As DbModelBuilder)
MyBase.OnModelCreating(modelBuilder)
modelBuilder.Entity(Of Issue)().HasIndex(Function(x) x.Created)
modelBuilder.Entity(Of Issue)().HasIndex(Function(x) x.Votes)
End Sub
Public Property Issues As DbSet(Of Issue)
Public Property Users As DbSet(Of User)
End Class
End Namespace
IssuesContextInitializer.cs / IssuesContextInitializer.vb
using System;
using System.Data.Entity;
using System.Linq;
namespace EntityFrameworkIssues.Issues {
public class IssuesContextInitializer
: DropCreateDatabaseIfModelChanges<IssuesContext> {
//: DropCreateDatabaseAlways<IssuesContext> {
public static void ResetData() {
using(var context = new IssuesContext()) {
context.Users.Load();
context.Users.RemoveRange(context.Users);
context.SaveChanges();
CreateData(context);
}
}
protected override void Seed(IssuesContext context) {
base.Seed(context);
CreateData(context);
}
static void CreateData(IssuesContext context) {
var users = OutlookDataGenerator.Users
.Select(x =>
{
var split = x.Split(' ');
return new User()
{
FirstName = split[0],
LastName = split[1]
};
})
.ToArray();
context.Users.AddRange(users);
context.SaveChanges();
var rnd = new Random(0);
var issues = Enumerable.Range(0, 1000)
.Select(i => new Issue()
{
Subject = OutlookDataGenerator.GetSubject(),
UserId = users[rnd.Next(users.Length)].Id,
Created = DateTime.Today.AddDays(-rnd.Next(30)),
Priority = OutlookDataGenerator.GetPriority(),
Votes = rnd.Next(100),
})
.ToArray();
context.Issues.AddRange(issues);
context.SaveChanges();
}
}
}
Imports System
Imports System.Data.Entity
Imports System.Linq
Namespace Issues
Public Class IssuesContextInitializer
Inherits DropCreateDatabaseIfModelChanges(Of IssuesContext)
': DropCreateDatabaseAlways<IssuesContext> {
Public Shared Sub ResetData()
Using context = New IssuesContext()
context.Users.Load()
context.Users.RemoveRange(context.Users)
context.SaveChanges()
CreateData(context)
End Using
End Sub
Protected Overrides Sub Seed(ByVal context As IssuesContext)
MyBase.Seed(context)
CreateData(context)
End Sub
Private Shared Sub CreateData(ByVal context As IssuesContext)
Dim users = OutlookDataGenerator.Users.[Select](Function(x)
Dim split = x.Split(" "c)
Return New User() With {
.FirstName = split(0),
.LastName = split(1)
}
End Function).ToArray()
context.Users.AddRange(users)
context.SaveChanges()
Dim rnd = New Random(0)
Dim issues = Enumerable.Range(0, 1000).[Select](Function(i) New Issue() With {
.Subject = OutlookDataGenerator.GetSubject(),
.UserId = users(rnd.Next(users.Length)).Id,
.Created = Date.Today.AddDays(-rnd.Next(30)),
.Priority = OutlookDataGenerator.GetPriority(),
.Votes = rnd.Next(100)
}).ToArray()
context.Issues.AddRange(issues)
context.SaveChanges()
End Sub
End Class
End Namespace
Issue.cs / Issue.vb
using System;
namespace EntityFrameworkIssues.Issues {
public class Issue {
public int Id { get; set; }
public string Subject { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public DateTime Created { get; set; }
public int Votes { get; set; }
public Priority Priority { get; set; }
public Issue() {
Created = DateTime.Now;
}
}
public enum Priority { Low, BelowNormal, Normal, AboveNormal, High }
}
Namespace Issues
Public Class Issue
Public Property Id As Integer
Public Property Subject As String
Public Property UserId As Integer
Public Overridable Property User As User
Public Property Created As Date
Public Property Votes As Integer
Public Property Priority As Priority
Public Sub New()
Created = Date.Now
End Sub
End Class
Public Enum Priority
Low
BelowNormal
Normal
AboveNormal
High
End Enum
End Namespace
User.cs / User.vb
```csharp
using System.Collections.Generic;
namespace EntityFrameworkIssues.Issues {
public class User {
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Issue> Issues { get; set; }
}
}
Imports System.Collections.Generic
Namespace Issues
Public Class User
Public Property Id As Integer
Public Property FirstName As String
Public Property LastName As String
Public Overridable Property Issues As ICollection(Of Issue)
End Class
End Namespace
To bind in-memory data to the GridControl, convert the entity set to List<T> and assign this list to the ItemsSource property as follows:
using System.Windows;
using EntityFrameworkIssues.Issues;
using System.Linq;
namespace EntityFrameworkIssues {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
LoadData();
}
IssuesContext _Context;
void LoadData() {
_Context = new IssuesContext();
grid.ItemsSource = _Context.Users.ToList();
}
}
}
Imports EntityFrameworkIssues.Issues
Imports System.Linq
Class MainWindow
Public Sub New()
InitializeComponent()
LoadData()
End Sub
Private _Context As IssuesContext
Private Sub LoadData()
_Context = New IssuesContext()
grid.ItemsSource = _Context.Users.ToList()
End Sub
End Class
In Server Mode, the GridControl loads data in small portions on demand. To activate this mode, use EntityServerModeSource as a data source. Initialize the data source and bind it to the GridControl as follows:
using System.Windows;
using EntityFrameworkIssues.Issues;
using DevExpress.Data.Linq;
using System.Data.Entity;
using System.Linq;
using DevExpress.Xpf.Grid;
namespace EntityFrameworkIssues {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var context = new IssuesContext();
var source = new EntityServerModeSource {
KeyExpression = nameof(Issue.Id),
QueryableSource = context.Issues.AsNoTracking()
};
grid.ItemsSource = source;
LoadLookupData();
}
void LoadLookupData() {
var context = new IssuesContext();
usersLookup.ItemsSource = context.Users.Select(user => new { Id = user.Id, Name = user.FirstName + " " + user.LastName }).ToArray();
}
}
}
Imports EntityFrameworkIssues.Issues
Imports DevExpress.Data.Linq
Imports System.Data.Entity
Imports System.Linq
Imports DevExpress.Xpf.Grid
Class MainWindow
Public Sub New()
InitializeComponent()
Dim context = New IssuesContext()
Dim source = New EntityServerModeSource With {
.KeyExpression = NameOf(Issue.Id),
.QueryableSource = context.Issues.AsNoTracking()
}
grid.ItemsSource = source
LoadLookupData()
End Sub
Private Sub LoadLookupData()
Dim context = New IssuesContext()
usersLookup.ItemsSource = context.Users.[Select](Function(user) New With {
.Id = user.Id,
.Name = user.FirstName & " " + user.LastName
}).ToArray()
End Sub
End Class
In Instant Feedback Mode, the GridControl loads data in a background thread. To activate this mode, use EntityInstantFeedbackSource as a data source. Initialize the data source and bind it to the GridControl as follows:
using System.Windows;
using EntityFrameworkIssues.Issues;
using DevExpress.Data.Linq;
using System.Data.Entity;
using System.Linq;
using DevExpress.Xpf.Grid;
namespace EntityFrameworkIssues {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var source = new EntityInstantFeedbackSource {
KeyExpression = nameof(Issue.Id)
};
source.GetQueryable += (sender, e) => {
var context = new IssuesContext();
e.QueryableSource = context.Issues.AsNoTracking();
};
grid.ItemsSource = source;
LoadLookupData();
}
void LoadLookupData() {
var context = new IssuesContext();
usersLookup.ItemsSource = context.Users.Select(user => new { Id = user.Id, Name = user.FirstName + " " + user.LastName }).ToArray();
}
}
}
Imports EntityFrameworkIssues.Issues
Imports DevExpress.Data.Linq
Imports System.Data.Entity
Imports System.Linq
Imports DevExpress.Xpf.Grid
Class MainWindow
Public Sub New()
InitializeComponent()
Dim source = New EntityInstantFeedbackSource With {
.KeyExpression = NameOf(Issue.Id)
}
AddHandler source.GetQueryable, Sub(sender, e)
Dim context = New IssuesContext()
e.QueryableSource = context.Issues.AsNoTracking()
End Sub
grid.ItemsSource = source
LoadLookupData()
End Sub
Private Sub LoadLookupData()
Dim context = New IssuesContext()
usersLookup.ItemsSource = context.Users.[Select](Function(user) New With {
.Id = user.Id,
.Name = user.FirstName & " " + user.LastName
}).ToArray()
End Sub
End Class
Virtual Sources allow you to bind the GridControl to any data source, even if the total record count is unknown.
If you want to display data with infinite scrolling, use InfiniteAsyncSource as a data source. Initialize the data source and bind it to the GridControl as follows:
namespace EntityFrameworkIssues {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var source = new InfiniteAsyncSource {
ElementType = typeof(Issue),
KeyProperty = nameof(Issue.Id)
};
source.FetchRows += OnFetchRows;
source.GetTotalSummaries += OnGetTotalSummaries;
grid.ItemsSource = source;
LoadLookupData();
}
// ...
}
// ...
}
Class MainWindow
Public Sub New()
InitializeComponent()
Dim source = New InfiniteAsyncSource With {
.ElementType = GetType(Issue),
.KeyProperty = NameOf(Issue.Id)
}
AddHandler source.FetchRows, AddressOf OnFetchRows
AddHandler source.GetTotalSummaries, AddressOf OnGetTotalSummaries
grid.ItemsSource = source
LoadLookupData()
End Sub
' ...
End Class
You can also use PagedAsyncSource that displays data in pages:
namespace EntityFrameworkIssues {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var source = new PagedAsyncSource {
ElementType = typeof(Issue),
KeyProperty = nameof(Issue.Id),
PageNavigationMode = PageNavigationMode.ArbitraryWithTotalPageCount
};
source.FetchPage += OnFetchPage;
source.GetTotalSummaries += OnGetTotalSummaries;
grid.ItemsSource = source;
LoadLookupData();
}
// ...
}
}
Class MainWindow
Public Sub New()
InitializeComponent()
Dim source = New PagedAsyncSource With {
.ElementType = GetType(Issue),
.KeyProperty = NameOf(Issue.Id),
.PageNavigationMode = PageNavigationMode.ArbitraryWithTotalPageCount
}
AddHandler source.FetchPage, AddressOf OnFetchPage
AddHandler source.GetTotalSummaries, AddressOf OnGetTotalSummaries
grid.ItemsSource = source
LoadLookupData()
End Sub
' ...
End Class
To fetch new data from the database, implement the FetchRows event handler as follows:
public partial class MainWindow : Window {
// ...
void OnFetchRows(object sender, FetchRowsAsyncEventArgs e) {
e.Result = Task.Run<FetchRowsResult>(() => {
var context = new IssuesContext();
var queryable = context.Issues.AsNoTracking()
.SortBy(e.SortOrder, defaultUniqueSortPropertyName: nameof(Issue.Id))
.Where(MakeFilterExpression(e.Filter));
return queryable.Skip(e.Skip).Take(e.Take ?? 100).ToArray();
});
}
// ...
}
Class MainWindow
' ...
Private Sub OnFetchRows(ByVal sender As Object, ByVal e As FetchRowsAsyncEventArgs)
e.Result = Task.Run(Of FetchRowsResult)(Function()
Dim context = New IssuesContext()
Dim queryable = context.Issues.AsNoTracking().SortBy(e.SortOrder, defaultUniqueSortPropertyName:=NameOf(Issue.Id)).Where(MakeFilterExpression(e.Filter))
Return queryable.Skip(e.Skip).Take(If(e.Take, 100)).ToArray()
End Function)
End Sub
' ...
End Class
After you bind the Data Grid to a database, you can implement CRUD operations (create, read update, delete). Refer to the following topic for more information: CRUD Operations in a Data-Bound Grid.
View Example: Implement CRUD Operations in the WPF Data Grid
See Also