In this article I will explain how you can cache object in
memory in WPF application to get better performance.
Cached data will be stored in memory so you will get faster access
to that data compare to accessing it from File or Database. So whenever you need to make frequent
calls to some database or file, better you cache that data and read from cached object this will improve
your application performance via reducing multiple calls to database or file.
Caching mechanism provided by .Net framework 4.0. Caching is
available under System.Runtime.Caching namespace and you need to add this
namespace in your project references.
See below example, where multiple calls are being made to
database for getting employee details based on employee name.
XAML -
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Name of employee -
" HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.Row="0" Grid.Column="0" />
<TextBox Name="EmpName" VerticalContentAlignment="Center"
Text="{Binding Path=EmployeeName, Mode=TwoWay}"
Height="30" Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" />
<Button Name="Search" Content="Search" Click="Search_Click"
Grid.Row="0" Grid.Column="2" Height="30"
Width="150"
/>
<ListBox Height="200" Width="200" Name="EmployeeList"
ItemsSource="{Binding}" Grid.Row="1" Grid.Column="0"
Grid.ColumnSpan="3" />
</Grid>
Code –
public partial class MainWindow : Window
{
ObservableCollection<string> Employees = new ObservableCollection<string>();
public MainWindow()
{
InitializeComponent();
}
//Search Button
event handler
private void Search_Click(object sender, RoutedEventArgs e)
{
Employees = GetEmployeesByName(EmpName.Text);
this.DataContext = Employees;
}
//Make calls to
database to get employee details based on given name
private ObservableCollection<string> GetEmployeesByName(string name)
{
ObservableCollection<string> employeeNames = new ObservableCollection<string>();
string connectionString = "Data
Source=MITESH-PC\\SQLEXPRESS;Initial Catalog=TestSQLDB;Integrated
Security=SSPI";
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
string cmd = "select firstname + ' ' + lastname as [EmployeeName] from
HR.Employees where firstname like @FirstName";
SqlCommand command = new SqlCommand(cmd, sqlConnection);
command.Parameters.Add(new SqlParameter("@FirstName", name));
sqlConnection.Open();
using
(SqlDataReader reader =
command.ExecuteReader())
{
while (reader.Read())
{
employeeNames.Add(reader["EmployeeName"].ToString());
}
}
sqlConnection.Close();
}
return employeeNames;
}
}
Output –
In above example, for each employee search is costing
database call. To avoid database call for each employee, we can cache all
employee name at once and for each subsequent search we can get it from cached
object. This will reduce database call every time we search on employee and
increase performance. See below code which implements caching.
Code –
public partial class MainWindow : Window
{
ObservableCollection<string> Employees = new ObservableCollection<string>();
//cache object
ObjectCache cache = MemoryCache.Default;
public MainWindow()
{
InitializeComponent();
//Gets all employess
from database
Employees = GetAllEmployees();
//sets employees in
cache
cache.Set("AllEmployees",Employees, new CacheItemPolicy() { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(5) });
}
//Search Button
event handler
private void Search_Click(object sender, RoutedEventArgs e)
{
//Get employeelist
from cached object
ObservableCollection<string> employeeList = cache["AllEmployees"] as ObservableCollection<string>;
//if cache is expired
or no object available in cache then set cache object again
if (employeeList == null)
{
employeeList = GetAllEmployees();
cache.Set("AllEmployees", Employees, new CacheItemPolicy() { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(5) });
}
//search employee
name from employee list loaded from cache object
var selecteEmployees = (from emp in Employees
where
emp.ToLower().Contains(EmpName.Text.ToLower())
select emp).ToList();
this.DataContext =
selecteEmployees;
}
//Gets all employess
from database
private ObservableCollection<string> GetAllEmployees()
{
ObservableCollection<string> employeeNames = new ObservableCollection<string>();
string connectionString = "Data
Source=MITESH-PC\\SQLEXPRESS;Initial Catalog=TestSQLDB;Integrated
Security=SSPI";
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
string cmd = "select firstname + ' ' + lastname as [EmployeeName] from
HR.Employees";
SqlCommand command = new SqlCommand(cmd, sqlConnection);
sqlConnection.Open();
using
(SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
employeeNames.Add(reader["EmployeeName"].ToString());
}
}
sqlConnection.Close();
}
return employeeNames;
}
}
Output –
Output is same as above example, but in this
example In-Memory caching is being used. In constructor, I loaded all the employees
from database and added to the cache object. When every time user search for
employee, search will be perform on in memory object and result will be returned.
In this example cache expiry is set to 5 minutes but you can set as per your
need. After every 5-minute cache object will get expired
and employee list will be null. After cache expired, if you still need cached object then you need to load it again from
database or file and add to cache again.
In memory caching is very beneficial and recommended for applications which
are making multiple calls to database, file system or others. You can set cache expiry time if you want to delete or update or refresh cache object after certain time. Caching will
improve performance and scalability of your application.
Hope this article helps you to understand how to cache data objects in WPF application. Please leave your feedback in comments below.
References –
See Also –