Prerequisites
You’re in luck this time, there aren’t really any prerequisites! (Some c# coding would of course be useful…)
Code is available on Github HERE
Of if you prefer…
I’ve created a video series to accompany this article on my YouTube channel here: https://www.youtube.com/binarythistle
User Story
As a developer
I want to map my application objects seamlessly to my database
So that I don’t have to worry about manual CRUD* operations
* Create, Read, Update and Delete
What You’ll Learn
When done, you should be able to:
- Understand what Entity Framework (EF) is
- Understand what “code first” and “database” first approaches are
- Perform CRUD operations on a database using c# and Entity Framework
- Understand what Linq is, and how it relates to Entity Framework
Ingredients
- Visual Studio (I use Community Edition) – Free
- SQL Server (I use Express) – Free
- ~ 20 Minutes of your time
My Lab set Up
No need this time as the set up is super simple…
Our Application
The little application we’re going to write from scratch is called ” Task Master”, don’t get too excited though…
With it we can:
- Create simple tasks that get added to our DB
- Read in our Task List and display, (bind), to a data grid
- Update (Edit) an existing Task
- Delete an existing Task
Each Task will have the following attributes:
- Description
- Due Date
- Status (this will be a simple list of values from our DB too)
- To Do
- In Progress
- Done
Wireframe
The user interface of our Windows forms app is “functional” at best… It literally is there only to test our main CRUD functions, so I’m not suggesting it’s something you’d adopt in your actual real world apps!
Idle State
- What our form will look like after start up
- If we have any existing tasks they will be loaded into our data grid (we’ll using “binding” here)
Mid-Edit Stage
- We have clicked on an existing task and clicked “Update” (note Update button now reads “Save”)
- Values are populated into our form elements
- We can either click:
- Cancel – to cancel the edit
- Save – any changes we made to the task detail is saved back
- Delete – self explanatory
- In all cases we revert the “Save” button back to “Update” and clear the form fields
IMPORTANT: We’re not going to expend too much time “augmenting” the UI to ensure incorrect button presses cannot occur, (enable / disable buttons etc.) it adds too much “fluff” to the project…
Class Diagram
The primary classes that will form the basis of this project are shown below:
tmDBContext
If you take anything away from this tutorial it would be the “DbContext” class, (you’ll see that our “tmDBContext” class inherits from this).
From MSDN:
The Entity Framework maps the entities and relationships that are defined in your model to a database. The Entity Framework provides facilities to do the following: materialize data returned from the database as entity objects; track changes that were made to the objects; handle concurrency; propagate object changes back to the database; and bind objects to controls.
The primary class that is responsible for interacting with data as objects is System.Data.Entity.DbContext (often referred to as context). The context class manages the entity objects during run time, which includes populating objects with data from a database, change tracking, and persisting data to the database.
Basically, it’s a big deal – more on this when we code!
Status
The Status class basically represents the different statuses that out task can have, you’ll notice that the tmDBContext class has both Status and Task as properties… You’ll notice that we’ve over ridden the “ToString()” method of this class there’s a good reason for this as you’ll find out when we start to code.
Task
The Task class models the, (surprise, surprise!), tasks that can be added to our db. You’ll notice that it has both:
- StatusID
- Status (class)
again more on this later.
Database Schema
Our very complex database schema is shown below:
Look kind of familiar?
Well it should! It matches almost exactly the Task and Status classes shown above. So where does Entity Framework fit into all this? Well a picture paints a thousand words:
It basically sits in between the code-based classes and the DB… But what exactly is Entity Framework? I’m guessing if you’re here you kind of have an idea, but here’s a few bullet points of clarification, (to be honest the real clarification will come with the code!):
- It’s a “persistence” framework
- It’s a layer of abstraction between the application, (code), and the database
- You may also hear it referred to as an “ORM” – Object Relational Mapper
- It is, but really Persistence Frameworks use ORM’s
Awesome I hear you cry, but why would I use EF, or more especially what are the advantages to using EF? There’s a great article HERE, so have a read, but to summarize:
- Productivity
- Application Design
- Code Reuse
- Application Maintainability
Which Came First: The Chicken or the Egg?
Or more correctly, which came first: the code or the DB?
For some of you this may seem obvious, to which you’d answer: “the database!” This of course is not incorrect, (apologies for the double-negative), but it’s not a clear cut as that…
With Entity Framework we basically have 2 broad approaches on how we could proceed:
- Database First (see answer above)
- Code First (this tutorial will use this approach)
Database First
With database first, we start with the database and “generate” our code classes from this DB schema. (EF provides a “wizard” that gives us this functionality).
This is a totally valid approach, and you may be “forced” to use it in instances where your organization has DBAs and they “control” the DB and schema. You may also opt to use this approach when you have an existing DB that you need to build an application around, (not really ideal for a whole number of reasons, but hey, we live in the real world and this may just be the reality of it).
We are not going to cover this approach here.
Code First
With code first we design and write our classes in the code first, (shock!), and then using something called “migrations” we gradually create our db schema from that.
This is a far more “exciting” approach, but may seem quite alien to some of you. For me the primary benefits of this approach are:
- Better application design (assuming you think about your classes!)
- Incremental DB changes, (via the multiple migrations you create), that can then be replicated across environments: Dev, Test, Prod etc
DBAs will of course be horrified by this approach, but this article is not about them! You may also here that this approach is less flexible and you don’t have the same configuration options over the db… So far I’ve not really found that to be true, but I’m sure there are some obscure examples that someone may want to share…
Anyhow this is the approach we are going use, so get ready!
Alternatives
Before we get into the coding, for some reason I always like to be aware of any “alternatives” – not sure why, I think it just helps me contextualize things…
Wikipedia lists a number of frameworks here, (you’ll notice they refer to them as ORMs).
Personally, I’ve only used 1 of the others listed there, and heard a whole lot about another…
Linq to SQL
Interestingly another Persistence Framework from Microsoft, and one that I used in a “DB First” application. Works fine and shares a lot of similarities with EF, be warned however that Microsoft’s strategic persistence framework is Entity Framework, with Linq to SQL being gradually side-lined. Time to migrate!
nHibernate
Never used used this, but is a major contender in the space…
How does Linq Fit?
Finally, before we actually start coding I just wanted to say a little bit about “Linq”, (not “Linq to SQL” as mentioned above, just plain old “Linq”), and how that fits into the picture – primarily because when I first started I got a bit confused…
So what is Linq?
- Stands for: Language Integrated Query
- It’s not a Persistence Framework
- It’s a ubiquitous way to query data from within your code
- You can use the same query syntax to query different data sources
Put simply, I used Linq queries to query my data source when I was using “Linq To SQL” as my persistence framework, (the name gives it away!). But we also use Linq in a very similar, (in fact exactly the same), way with EF.
Linq allows us to write the same query “in code” that talks to different data sources, e.g.:
- Realational DBs
- XML
Or even the same Data Source, (e.g. MS SQL Server), but via different persistence frameworks, e.g.
- Linq to SQL
- Entity Framework
It abstracts us away from things like SQL, (relational DB’s), or XQuery, (XML), and allows us to use the same syntax.
Hope that was clear? If not moving to the coding should help clarify!
Now we dev!
OK lets begin with the easy bit and get our form user interface set up. As mentioned above it’s purely for testing the EF functionality we’re developing so feel free to come up with something else!
I take it for granted that you’ve created a new Windows Forms project – I’ve named mine “TaskMaster”
Our Form
Form Element Naming
(I’ve grouped the buttons together, hopefully you can follow the formatting I’ve used!)
Setting Up Entity Framework
Install Entity Framework
OK we want to ensure Entity Framework is available to our project, so in Visual Studio:
- Tools -> NuGet Package Manager -> Package Manger Console
- At the Package Manager prompt (PM>) type:
PM> install-package entityframework
Hit and the latest version of Entity Framework will be made available to your application:
I’d personally also just check the project references in solution explorer:
You should see:
- EntityFramework
- EntityFramework.SqlServer
- System.ComponentModel.DataAnnotations
have been added.
Create Your DBContext Class
1st let’s just get organised:
- Right Click you Project
- Select Add
- New Folder
- Name the Folder “Model”
Now:
- Right Click the “Model” folder
- Select Add->Class…
- Name it “tmDBContext” as per the class diagram above
Now in the code of your tmDBContext class ensure:
- You add a “using” reference to: System.Data.Entity
- Ensure the tmDBContext class “inherits” from DbContext
Your code should look similar to the following:
using System.Data.Entity; namespace TaskMaster.Model { class tmDBContext : DbContext { } }
We’re not finished with our db context yet… but first we need to:
Update Our App.config file
The app.config file is where, (surprise, surprise), our applications configuration is stored. Now while Entity Framework is clever, we still need to set up a connection string to our database, this is then used by our tmDBContext class to connect.
In this example we’re using MS SQL Server running locally on our dev machine. We’re also using “Integrated Security” so we don’t need t supply user credentials explicitly, (our current windows login account will be used).
So that being said my connection string looks like:
<connectionStrings> <add name="tmDBContext" connectionString="data source=DESKTOP-H958OBO\SQLEXPRESS;initial catalog=TaskMaster;integrated security=True;" providerName="System.Data.SqlClient"/> </connectionStrings>
I add this string to the app.config file withing the main tags:
There are a few important things to note here:
- The “Name” of the connection string should equal that of your tmDBContext class (for the purposes of this tutorial anyway – you can experiment with other variations later…)
- The “data source” attribute will obviously need to reflect your own local SQL Server instance
- The “initial catalog” attribute is the actual database that will house our entity tables. At the moment this does not yet exist on out SQL Server
Enable Migrations
As mentioned very briefly above we use “migrations” to incrementally make changes to our Database based on the code classes we develop, but we have to enable them first. To do so, go back to NuGet Package Manger Console and type:
PM> Enable-Migrations
You should see:
A folder called: “Migrations” will also appear under you project in solution explorer.
Note: Our database still does not exist yet…
Would you believe that’s the plumbing for Entity Framework established? Yes it is! And you’re probably not that impressed yet, ok well hold on!
Creating Our 1st “Code First Class”
So this is where the magic starts to happen… As per our class diagram above, we want to model 2 things:
- Our Task
- A list of Task Statuses
We’ll start with our “Status” class first.
On the “Model” folder in solution explorer, right click then:
- Add-> Class…
- Name your class “Status”
- And give it 2 attributes: Id (Int) and Name (String)
Your code should look like the following:
namespace TaskMaster.Model { class Status { public int Id { get; set; } public string Name { get; set; } } }
But we’re still not done.
We need to tell Entity Framework that we want this class to be “replicated” to our DB and come under Entity Framework “control” (probably a bad use of words, but hey..)
To do this we go back to our tmDBContext Class and add our “Status” class as a “DbSet”, the code of our tmDBContext class should now look like:
using System.Data.Entity;
namespace TaskMaster.Model
{
class tmDBContext : DbContext
{
public DbSet Statuses { get; set; }
}
}
Before I explain fully what’s going on let’s proceed to the real money shot, and get this baby in the DB. To do this we create a “Migration”, so go to NuGet Package Manager Console and type:
PM> add-migration AddStatus
I’ve used “AddStatus”, (no white-space), as the name of this particular migration as that’s the most recent change we’ve made, but this name could be anything really. It’s best to keep it meaningful though.
Again, our DB, (and table), have still not been created. But what has been created is a “Migration” file that’s been placed in our migration folder:
And the contents of the file its self:
This migration file is:
- Prefixed with a time stamp
- Contains an “Up” method (What needs to be created)
- Contains a “Down” method (what needs to be rolled back should we want to roll back the effects of that migration)
In short these files keep track of all the incremental changes to our DBContext monitored classes since the last migration was created, so if you’ve created 5 new classes since the last migration, this would be reflected in the latest migration file you’d generate. In our case, we’ve just added 1 class and brought into the line of sight of our tmDBContext class.
Finally it’s time to run the migration!
Again, at the Package Manager prompt, type:
PM> update-database
You should see something similar to the following output:
If we check our SQL Server DB…
So what just happened?
Step by step this is what we did, (you can re-use the “recipe” going forward):
- We installed Entity Framework
- We created our own “tmDBContext” Class that inherits from “DBContext”
- We updated the app.config file to include a connection string to our database server
- We “enabled migrations”
- We created a code-based class with 2 attributes
- We then “added” the class as a property of our tmDBContext Class, (go to the class diagram above to see this relationship). We used “DBSet” to achieve this.
- We added a migration, this creates a list of commands to be run against the db in order to create a db entity matching any “modeled” classes in our tmDBContext class
- We then update the database
Steps 1-4 you only have to do once per project, (unless you’re connecting to multiple databases…)
Steps 5-8 will be performed multiple times throughout the development of the project, (indeed smaller change increments are a good thing!).
Seeding Data
The work flow of a Task in our system is very simple:
So we want our user to be able to select one of these statuses, (from our dropdown) when they create or update a task they’ve added to the db.
We could:
- Hard code these values in our app
- Go to our DB and add values to our Status table (using SQL or Management Studio)
- Create a migration and add the values form there
If any of you picked the 1st option, shame on you! This is bad practice for so many reasons I won’t go into here
2nd option is better, but what about when we move from our Development db to our Test DB or even “production-replica” db? We’d have to add those values again manually…
We’ll of course use the 3rd option and use a Entity Framework to create a migration that will add the values for us. This means when we want to stand up another instance of our DB, we just run all the migrations and the table values will be added also.
Create an “Empty Migration”
Go to NuGet Package manager console and type:
PM>add-migration seedStatusData
Again the name of the migration could be anything, but best to make it meaningful…
This will create another migration file, with empty Up and Down Methods, open this file and add to the Up method the following code:
Sql("INSERT INTO Status (Name) VALUES ('To Do');"); Sql("INSERT INTO Status (Name) VALUES ('In Progress');"); Sql("INSERT INTO Status (Name) VALUES ('Done');");
Your migration file should look like this:
Now I won’t over explain this, but basically we’ve effectively put sql into our migration file, so when it’s run can you guess what happens?
Well, lets see… Make sure you save the file then type the following at NUGet Package Manger Console, (NPMC):
PM> update-database
You should get a success message and if we directly query the “Status” table in our DB we should see:
Note: I’ve put square brackets round the table name as “Status” is a reserved work in TSQL. (In retrospect this was probably a bad name for our class/table).
You’ll also note that as the ID column had “identity” set to true as well as being a primary key, (see 1st migration above), we did not explicitly have to provide values for the ID in our 2nd “seed” migration as the DB creates these values for us.
Migrating our Task Class
OK it should now be relatively straightforward to add, (in code), our “Task” class and migrate to our DB – and indeed it is!
On the “Model” folder in solution explorer, right click then:
- Add-> Class…
- Name your class “Task”
- And give it 5 Attributes:
- Id (Int)
- Name (string)
- Status (Status. I.e. the status class we defined above)
- StatusId (int)
- DueDate (DateTime)
so your code should look like this:
namespace TaskMaster.Model { class Task { public int Id { get; set; } public string Name { get; set; } public Status Status { get; set; } public int StatusId { get; set; } public DateTime? DueDate { get; set; } } }
The Id, Name and DueDate attributes are all very self explanatory, so I won’t insult your intelligence by detailing what they are, (the only point of note is that we have made out “DueDate” null-able by declaring it with a “?”…
Navigation Properties and Foreign Keys
The other two properties that I do want to briefly discuss are:
- StatusId (this is a Foreign Key attribute)
- Status (This is a “Navigation Property”)
These 2 properties are really a way for Entity Framework to maintain the relationship between the Task and Status entities, see our DB schema below:
Now relationships, (or associations), and for that matter Navigation Properties are an expansive area that I don’t want to go into too much more detail about here… If you are interested in more detail there’s a great article HERE on the MSDN site, I suggest you read that.
In short though, these properties are a way for us to associate the Task to a given Status. It’s also worth noting that the Navigation property, (“Status”), is not created in the database, it’s used only at the Entity Framework level…
Important: Entity Framework uses something called “convention over configuration” which in short means that if you follow certain naming standards, e.g. “Id” for primary keys and “Id”, e.g. StatusId for the foreign keys, it will scaffold and set up relationships between those classes (or entities if you prefer) as standard.
You can deviate from these standards but it will require you to write more code…
So with that let’s migrate!
In NPMC type:
PM> add-migration addTask
Ooops! What’s wrong with this picture?
Yes that’s right we forgot to bring the “Task” class we’ve just created into “view” of our DBContext.
Before we do that though, what can we do about our “blank” migration..?
Deleting, Overwriting and Rolling-Back Migrations
There are a number of scenarios where you may want to just delete the migration, (we’ll do that in this instance), Overwrite an existing migration, (we could do that too), or actually roll back a migration that you’ve run.
Deleting
Before you just “delete” a migration file, (as simple as right clicking the file and selecting delete), you should first check to see if it’s be run against the DB, (in which case you’d want to first roll back).
In NPMC, type:
PM> get-migrations
This gets the list of migrations that have been applied against the db, see below:
We can see here that while we have 3 migration files, (including our new, but empty “addTask” migration), we have only run 2 against the DB. So in this case we can just delete that migration file and start again.
So let’s do that, (right click and delete it!)
Overwriting
You could also just “overwrite” that empty migration with new one that is not blank, (once we add the Task class to our DBContext). If you wanted to take that approach you’d type the following in NPMC, (after DBContext code changes of course!):
PM> add-migration addTask -Force
Again note you should only do this if the migration has not been run against the DB!
Rolling Back
There may be instances where you have applied a migration, and realised that what you’ve applied is incorrect and you want to roll back. In this case you’d:
- get the list of migrations you’ve applied, (possibly optional if you already know the name of the last migration you want to roll back to)
- Update the database to your selected migration point, or “Target Migration”
So let’s say we want to roll back the data we seeded to our db, you would do the following in NPMC:
You can see that we “targeted” the migration we wanted to roll back to, and the framework then “reverted” all migrations that came after, in this case it was just our “seedStatusData” migration.
Note: if you’ve tried that out as an example, we DO actually want our Status data in data in there, so just make sure you update the database again to put it all back, hint: just “update-database”…
Update DBContext
Ok so the whole point of deliberately making a mistake was to show you how to play with migrations if yu do make a mistake. It should also re-enforce how important the DBContext class is, so with that let’s update it:
using System.Data.Entity;
namespace TaskMaster.Model
{
class tmDBContext : DbContext
{
public DbSet Statuses { get; set; }
public DbSet Tasks { get; set; }
}
}
Save the file then, we add a migration:
PM> add-migration addTask
Huzzah, the generated migration file now has “Stuff” in it:
We then update our database and check that it was applied:
If we actually go to SQL Server Enterprise manager and refresh our DB tables we should see our newly created table:
Checkpoint: Ok we’ve covered a lot! But that’s it for migrations etc. we’ll now move onto our 2nd phase of coding and start working with Entity Framework classes to: Create, Read, Update and Delete Task data in our DB.
Reading Data From the DB (Pt1)
Ok so we’ve gone to all the bother of setting up a status table and populating that data, we now want to read that data into the items collection in our combobox.
The steps we have to follow to achieve this are:
- Create and instance of our tmDBContext class
- Get a “List” of our statuses (from the DB)
- Populate the combo box
So the “code-behind” or form should look like:
using System; using System.Data; using System.Linq; using System.Windows.Forms; using TaskMaster.Model; namespace TaskMaster { public partial class Form1 : Form { private tmDBContext tmContext; public Form1() { InitializeComponent(); tmContext = new tmDBContext(); var statuses = tmContext.Statuses.ToList(); foreach (Status s in statuses) { cboStatus.Items.Add(s); } } } }
The only thing of note is that our “tmContext” gives us direct access to our entity classes (and more importantly that data “behind” them), without having to really having to do that much work – it’s really simple (that’s the power of the EF!)
We then just populate our combobox with each Task object in our list… But there’s a problem, when we run our code and look in our drop down we get this:
While we have added our Status objects to the comboboxes items collection, when the list is rendered in the drop down, we’re getting the object type name, not the Name property… To rectify this we’ll “override” the ToString() method of the Task class so it defaults to returning the “Name” property whenever ToString() is called, the code should look like this.
namespace TaskMaster.Model { class Status { public int Id { get; set; } public string Name { get; set; } public override string ToString() { return Name; } } }
When we re-run our code, you should now have a list of the statuses we put in the DB.
Creating Data in Our DB
Ok, it’s starting to get exciting now as we are going to create a task so that it will get added to our database!
Warning! We’re going to start wiring up the events on our buttons, and while I’m going to put a little bit of “validation” in here, (e.g. not allowing us to create a task unless a status is selected), I’m not going to take this to any extreme as it’s not the point of the tutorial.
Ok so on the form designer, double click our Create button, this should take you to the click event code…
Into this event method, enter the following code:
if (cboStatus.SelectedItem != null && txtTask.Text != String.Empty) { var newTask = new Model.Task { Name = txtTask.Text, StatusId = (cboStatus.SelectedItem as Model.Status).Id, DueDate = dateTimePicker1.Value }; tmContext.Tasks.Add(newTask); tmContext.SaveChanges(); } else { MessageBox.Show("You must select a Task Status and enter a Task description."); }
Let’s test this now, so fire up our app, enter a description and select a task status and click the “Create” button.
If all is correct “nothing” should happen, (or nothing appears to happen at least), but if we check our database we get a different story…
Note: We’ll fix up our dodgy “UI experience” with the next bit of code…
So what did we do here?
- We checked to see if we had a selected value for our Task status and that we had a non empty task description (the 1st “if” statement), otherwise we showed a message.
- If the 1st if statement passed we then:
- Created a “Task” object, (note I used “Model.Task” to declare this (for clarity)
- Set the attributes of our new Task Object, note we had to cast the comboboxes selecteditem as a “Model.Status” in order to obtain the StatusId for our Task.
- Called the tmContext.Task.Add() method in order to add our newly created task to the tmContext (it’s important to realise that the Task has not been added to the DB at this stage, only to the the Entity Framework DBContext…
- Finally we called the tmContext.SaveChanges() method. This actually writes all pending changes in our DBContext to the Db (we only have one).
Reading Data From the DB (Pt2)
Ok, so when we added a record to our db, (above), our app didn’t give us any feedback that the record had been added, (even though it had!), which is a poor user experience.
In order to rectify this, we’re going to “bind” our datagridview to our list of Tasks, (available via the tmDBContext obejct). Observe the diagram below to view 2 binding methods, (they are basically the same, 1 uses a “bindingcontext” object other does not:
While the 2nd option works, all the Microsoft documentation on binding to windows forms elements uses an intermediary “bindingsource” object, so that’s what we’ll use here.
If you want to read more on binding and in particular what the advantages of using a binding source are: I’d reccomend these 2 articles:
We’re actually going to write a simple function that:
- Creates a new bindingsource object
- Using “Linq”, we’ll query our tmDBContect class for all Tasks and set the result to the bindingsource objects “DataSource” property, (see diagram above)
- Set the DataGridView’s “DataSource” property to the Binding Source object
Here’s the function:
private void refreshGrid() { BindingSource bi = new BindingSource(); var query = from t in tmContext.Tasks orderby t.DueDate select new { t.Id, TaskName = t.Name, StatusName = t.Status.Name, t.DueDate }; bi.DataSource = query.ToList(); dataGridView1.DataSource = bi; dataGridView1.Refresh(); }
The only thing of any real novelty is the construction of our “query”. For those of you familiar with SQL, while it’s not the same, it has a similar feel to it, this however is Linq in action… (or more specifically Linq query syntax in action…)
We are effectively selecting every row from the tmContext class Tasks collection, and specifying the columns, (and column names), that we are going to use.
Not wanting to confuse things, but we could have used another Linq “syntax” to get the same result, this is called Linq Extension Method syntax, and it uses “lambda” expressions. I’m not going to cover that here but it is in the video associated to this post, or you can read about the differences here.
To see if this works we’ll pack a call to the method in our form’s constructor:
public Form1()
{
InitializeComponent();
tmContext = new tmDBContext();
var statuses = tmContext.Statuses.ToList();
foreach (Status s in statuses)
{
cboStatus.Items.Add(s);
}
refreshGrid();
}
We should also place a call to it in our CreateTask Button’s click event handler after we call tmContext.SaveChanges() method, (this corrects our poor UI experience by updating the datagridview with any newly added Tasks)
All being well, when the form loads, we should see the Task we just added, in the previous code example:
Deleting Data
Ok we’re now going to delete data from the db, to do this:
- Quit the app if it’s running
- Double click the DeleteTask button to reveal it’s event handler, and enter the following code:
var t = tmContext.Tasks.Find((int)dataGridView1.SelectedCells[0].Value); tmContext.Tasks.Remove(t); tmContext.SaveChanges(); refreshGrid();
You can probably figure out what’s going on here as the code is quite self explanatory, but for completeness we’re:
- “Finding” the task we want to delete via our tmContext class
- In order to do that, were obtaining the value of the “zero-index” cell of our selected row, (ensure you only have 1 row selected – you can enforce that via the DataGridView’s properties)
- We then remove the task from the tmContext (it’s “tagged” for deletion although not yet removed from the DB)
- As with creating Tasks, we call SaveChanges() to apply any pending changes in out fmContext class – this then applies the necessary changes to our db.
- Finally we refresh the DataGridView
Updating Data
The actual act of updating data, is very simple and really just an extension of how we delete a Task… (except we don’t delete the Task, we update it’s elements), see code below:
var updateTask = tmContext.Tasks.Find((int)dataGridView1.SelectedCells[0].Value); updateTask.Name = txtTask.Text; updateTask.StatusId = (cboStatus.SelectedItem as Status).Id; updateTask.DueDate = dateTimePicker1.Value; tmContext.SaveChanges(); refreshGrid();
Essentially we:
- Find the task we want to update
- Update it’s attributes
- Save Changes to the DB
- Refresh Our Grid
The user experience I’ve designed to enable updates in our app is not great but it does allow you to update items, the work-flow is as follows:
- Select the row you want to update
- Click the cmdUpdateTasks button
- We populate our form elements with the relevant values
- We change the text of cmdUpdateTasks button to “Save”
- Click the the cmdUpdateTasks button (we check it’s “Text” value is “Save”
- We then find the the selected task
- Update it’s values to whatever we’ve set in the form items
- Save Changes to DB
- Refresh Our Grid
- Reset all our form elements
We also have the “Cancel” button which just “resets” our form mid-update flow.
Warning: This flow is clunky at best and can probably trip up and throw errors if you don’t follow it correctly… However it’s only real purpose was to show you how you update data, and if done correctly it does that!
The complete code for both the UpdateTask and CancelTask button event handlers is shown below, (it’s also available on github).
private void cmdCancel_Click(object sender, EventArgs e) { cmdUpdateTask.Text = "Update"; txtTask.Text = string.Empty; dateTimePicker1.Value = DateTime.Now; cboStatus.Text = "Please Select..."; } private void cmdUpdateTask_Click(object sender, EventArgs e) { if (cmdUpdateTask.Text == "Update") { txtTask.Text = dataGridView1.SelectedCells[1].Value.ToString(); dateTimePicker1.Value = (DateTime)dataGridView1.SelectedCells[3].Value; foreach (Status s in cboStatus.Items) { if (s.Name == dataGridView1.SelectedCells[2].Value.ToString()) { cboStatus.SelectedItem = s; } } cmdUpdateTask.Text = "Save"; } else if (cmdUpdateTask.Text == "Save") { var updateTask = tmContext.Tasks.Find((int)dataGridView1.SelectedCells[0].Value); updateTask.Name = txtTask.Text; updateTask.StatusId = (cboStatus.SelectedItem as Status).Id; updateTask.DueDate = dateTimePicker1.Value; tmContext.SaveChanges(); refreshGrid(); cmdUpdateTask.Text = "Update"; txtTask.Text = string.Empty; dateTimePicker1.Value = DateTime.Now; cboStatus.Text = "Please Select..."; } }
Final Thought
Well that was a long post! Entity Framework is a huge area, but I hope this introductory post has enabled you to understand what it is and how to start using it
References
Code on Github Here: https://github.com/binarythistle/VP-17-Intro-to-Entity-Framework