Bind to Entity Framework Sources
- 11 minutes to read
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.
#Main Example
This example includes multiple solutions that demonstrate:
- Different binding mechanisms: virtual sources, server mode sources, and local data.
- MVVM and code-behind patterns.
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.
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);
}
}
}
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; }
}
}
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();
}
}
}
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 }
}
```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; }
}
}
#Local Data
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();
}
}
}
#Server Mode
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();
}
}
}
#Instant Feedback
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();
}
}
}
#Virtual Sources
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();
}
// ...
}
// ...
}
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();
}
// ...
}
}
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();
});
}
// ...
}
#Next Steps
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.