DataAccess in Windows Forms is a much misunderstood concept. I cannot count the number of times I see people dropping DataAdapters and TableAdapters directly on a Form and accessing their database directly from the Form in this manner. This is a major no-no!!
The DataAccess classes should be totally separated from your User Interface (UI) or, as some people prefer to call it, the Presentation Layer. And forget TableAdapters (see my blog post about them)!
Interaction between the DataAccess layer and the UI layer can be done with either Business object classes or, as I prefer, with DataSets (preferably, strongly Typed DataSets).
Base DataAccess Class
First, you need to have a base DataAccess class that all of your other DataAccess classes should inherit from, and get the connection in it's constructor: Let’s start with the simple DataAccess examples and work to the more complex (but more flexible) version in Part II of this blog.
public class BBDataAccess
{
protected SqlConnection oConnection;
public BBDataAccess
{
// Note that I wouldn't actually hard-code the connection string like this.
// It should be in your config settings.
// Old (1.1), obsolete but still works:
// string MyConnString = System.Configuration.ConfigurationSettings.AppSettings["ConnectionString"];
// Current (2.0)
// string MyConnString = System.Configuration.ConfigurationManager.AppSettings["ConnectionString"];
this.oConnection = new SqlConnection("server=(local);database=MyDataBase;uid=sa;pwd=MyPassword");
}
}
Depending on how complicated your application is, you may want to have a separate DataAccess class for different functionality, each one sub-classing from the BBDataAccess base class.
Getting Data
Retrieving the data is easy. Note that this DataAccess method returns a DataSet, as mentioned above:
public class MyDataAccess : BBDataAccess
{
public DataSet GetMyData()
{
// Note that a Fill does not require a Connection Open/Close. The Fill leaves
// the connection in the state it was found (leaves it open if it was open,
// and if it was closed, it opens it, Fills, then closes again).
SqlDataAdapter da = new SqlDataAdapter("select * from bob", this.oConnection);
DataSet ds = new DataSet();
da.Fill(ds, "MyTable");
return ds;
}
}
Saving Data
You have a few more options when updating the database.
-
DataAdapter.Update() method, with CommandBuilder
First, you can use the Update method of the DataAdapter. In order for this to work, the tables in your database must have a PrimaryKey defined.
You can do it using the CommandBuilder, which will generate update commands for you. (Note: if you use a Stored Proc, the CommandBuilder only generates the proper insert/update/delete commands for the first table retreived from the Stored Proc.) (Another Note: using the CommandBuilder entails an extra round-trip to the server.):
public void UpdateMyData(DataSet ds)
{
// The same applies for the Update. It's not necessary to Open/Close the connection.
SqlDataAdapter da = new SqlDataAdapter("select * from bob", this.oConnection);
SqlCommandBuilder sb = new SqlCommandBuilder(da);
da.Update(ds);
}
-
Without CommandBuilder
Or you can create the various update commands yourself instead of using the CommandBuilder:
public void UpdateMyData(DataSet ds)
{
SqlCommand sc = new SqlCommand();
sc.Connection = this.oConnection;
da = new SqlDataAdapter(sc);
da.InsertCommand = new SqlCommand("Insert into bob (xyz, abc) VALUES ( @xyz, @abc )", sc.Connection);
// Note that with these parameters, the update will simply insert "xyz" and "abc"
// into every inserted row. Probably not what you want.
// See the Do-It-Yourself option below for a better idea.
da.InsertCommand.Parameters.AddWithValue("@xyz", "xyz");
da.InsertCommand.Parameters.AddWithValue("@abc", "abc");
// do the same for da.DeleteCommand & da.UpdateCommand
da.Update(ds);
}
-
Do-It-Yourself
Or, you can take total control, not use the da.Update() and do it all yourself (this is basically the same code that gets done behind the scenes by the da.Update() method:
public void UpdateMyData(DataSet ds)
{
SqlCommand sc = new SqlCommand();
sc.Connection = this.oConnection;
sc.Connection.Open();
foreach (DataRow Row in ds.Tables[0].Rows)
{
switch (Row.RowState)
{
case DataRowState.Added :
// or use a StoredProc, which I prefer
sc.CommandText = "Insert into bob (xyz, abc) VALUES ( @xyz, @abc )";
sc.Parameters.Clear();
sc.Parameters.Add("@xyz", Row["xyz"]);
sc.Parameters.Add("@abc", Row["abc"]);
sc.ExecuteNonQuery();
break;
// Do the same for DataRowState Deleted and Modified
case DataRowState.Deleted :
break;
case DataRowState.Modified :
break;
}
}
sc.Connection.Close();
}
Summary
So, that’s it for this blog. In Part II, I’ll discuss creating a base DataAccess class by programming to the various IDb interfaces. By doing this, all your DataAccess sub-classes are totally "disassociated" from knowing what your back-end database is. Stay tuned …

Hello Bonnie,
ReplyDeleteHave been googling trying to understand DataAccess with VB express 2008. Many experts have opinions but they regularly use TableAdapters and I'm afraid I become totally lost. I like your strong opinions on TableAdapters and their wizards, however I tried your suggestion above but I guess it is not for the Express 2008 environment as I just get heaps of syntax errors when I type this in. Clearly I am in the early stages of learning VB
I don't know too many experts who recommend using TableAdapters. ;0)
ReplyDeleteThe above code is pretty standard stuff ... none of the fancy new stuff is used here because it was originally developed in .NET 1.1 (unlike my more advanced blogs on this topic). Also, it shouldn't be C# specific at all ... the above stuff should work just fine with VB and I can't imagine that some of it is not supported by the Express version!! Express would be pretty useless if that was the case!!
I don't know what kind of errors you're missing, but perhaps you only need to add some references and imports to your project? You definitely need System.Data and System.Data.SqlClient. Also, if you're trying to use a config file instead of hardcoding the connection string, then if you tried using the 2.0 version of my commented out code you'd need to also add a reference to System.Configuration.
Under "Getting Data" should "public BBDataAccess" not read "public BBDataAccess()"?
ReplyDeleteOops, sorry for the double post ... and to beat all ... they were wrong ... the section is "Base DataAccess Class", not "Getting Data"
ReplyDeleteHi Bonnie,
ReplyDeleteI started reading your blogs about DAL and I have one or two questions.
1. what is the difference between what you have in your blog and Enterprise Library Data Access
2. if they function the same, what are the pros and cons of either?
3. My application is a service, so there is no UI layer, just business logic and data access. My solution is made up of a service that calls different application to run. These independent applications are all in different projects that I bring into my solution. Question, if all of my projects for this solution, except the service, need db calls in them should I import and use something like Enterprise Library Data Access in each and every project that needs it or should I create a seperate project that has it and import that project into all other projects that need it????? How would you handle this?
Hi Chuck,
ReplyDeleteThanks for reading my blog!
1) I've never used, nor looked at, the Enterprise Library Data Access Application Block. The basis for my DataAccess classes was started back before that even existed. Since my apps were dependent on my own classes (and worked fine with it), I never considered refactoring to use something else, I just built on and expanded my own base classes. So, I can't really offer a comparison without researching more.
2) The pros about writing your own is that it's more flexible, because you can make it do whatever you want to ... and you most likely understand how things work better having dug into the inner workings of ADO. The only con I can think of is that if you don't fully understand what you're doing, you might leave out some important stuff.
3) You only need one base DAL project, be it one you've created yourself (perhaps based on examples in my blog posts) or one based on the Enterprise Library Data Access Application Block. Every solution can reference your DAL project and/or every project can reference the DAL project. It doesn't matter. I may be a little unclear about your question though ...
Hi Bonnie,
ReplyDeleteThanks for the reply. Lets say I do use Enterprise Library Data Access Block to do all my database stuff in my solution, would there be any advantage to putting the Enterprise Library Data Access Block into a project by itself and then adding that one project to the other projects in my solution? Or should I treat each project independently of each other and import Enterprise Library explicitly into each project?
I know you haven't used Enterprise Library but any thoughts on this would be appreciated.
Hi Bonnie,
ReplyDeleteThanks for the reply. Lets say I do use Enterprise Library Data Access Block to do all my database stuff in my solution, would there be any advantage to putting the Enterprise Library Data Access Block into a project by itself and then adding that one project to the other projects in my solution? Or should I treat each project independently of each other and import Enterprise Library explicitly into each project?
I know you haven't used Enterprise Library but any thoughts on this would be appreciated.
Hi Chuck,
ReplyDeleteSorry for the slow reply ... I got busy and forgot about it.
I suppose you could put the Enterprise Data Access block into it's own project. It might be useful in case you need to further sub-class it to override or extend functionality. Keeping in mind that I know nothing about it ... but I tend to use my own sub-classes of .NET classes anyway (and typically grouped into several different projects, depending on functionality) ... it always a good idea, in my opinion.
Hello Bonnie,
ReplyDeleteMy name is Cherry!
Sorry to bother you!
Might I ask some questions??
I try to use DIY method to update the dataset to datasource, but I can't insert DATETIME data.
cmd->Parameters->Add("dateTime", row["dateTime"]);
It shows "Syntax error in INSERT INTO statement." when I try to add a datetime data.
Do you have any suggestion to me?
Thanks very much!
Hello Bonnie,
ReplyDeleteI found the result that is I use the key word - DATETIME...After I change the field name, the error above I mentioned is disappear.
Your blog gives me some great concepts and methods! Thx~
Hi Cherry ... glad you got it figured out and I'm glad you like my blog! If you have any other questions, please don't hesitate to ask.
ReplyDeleteThanks Bonnie.. this article very helpful for beginner like me
ReplyDeleteYou're welcome, Andi ... glad I could help. =0)Don't forget to read the other posts in the 3-part series also.
ReplyDeleteHi Bonnie, Could you tell me, why we need to create base class for connection?. (the question might be silly!)
ReplyDeleteThanks for reading ... I hope you continue on and read Parts II and III as well. This one, Part I, is very elementary and things don't really get "cooking" until Part II. Part I was more intended to get people thinking about separating out their DataAccess into a separate layer or tier. Part II is more along the lines of what you should actually have in your class.
ReplyDeleteBut to answer your question (and no, it's not a silly question), there really isn't an actual need to create a base class just for the connection. If you've read Part II or III, you'll see that I did it there as well. But again, not necessary. It was a holdover from existing code, and I probably should have refactored it more before posting here (I had already had to refactor all the code before posting ... should have done that one too).
So, thanks for your question ... it gives me an opportunity to clarify that a bit!
Excellent blog, and very informative! Thanks! Only one question about it so far, under the second part when declaring the data adapter, SqlDataAdapter da = new SqlDataAdapter("select * from bob", this.oConnection
ReplyDeleteDoes that statement need to be repeated for every table in the database, or does it work if I just type in the database name?
Are you talking about retreiving the data or using the CommandBuilder? In either case, we're only dealing with one database table at a time. If you need to retrieve data from multiple tables into one DataSet, you can do that also, just separate your SELECT statements by a ;
ReplyDelete"select * from bob; select * from fred"
But you still have to update one table at a time.
If you can tell me what it is you're trying to accomplish, perhaps I can point you in the right direction. Also, you might want to go ahead and read Part II of the series, it's got more useful classes. I guess I didn't provide a link to it in this first blog ... guess I should update it. But, here it is:
http://geek-goddess-bonnie.blogspot.com/2009/10/dataaccess-part-ii.html
I was just reading part II actually and getting ready to see what I can do with it. I was talking about retrieving data (GetMyData()). I'm a newb at SQL, so I just wanted to check whether to aim the select statements at tables or at the entire database. I haven't worked on DALs before and am new at C# (used to using C++ unmanaged code). Right now I'd be content just to get the data successfully and display it on the form.
ReplyDeleteI will probably need to do some JOIN statements, I want to rearrange the data so that it's easier to plug into GUI level searches. Some of the (what should someday be) tables are sitting in separate database files that I have to tinker with. Would it be easier to use the JOIN statements if I leave the datasets separate or should I use the multiple select you displayed?
Thank you for replying!
Have you thought about using Stored Procedures? It's really the preferable way to access your database (and you'll notice in Part II that I've set up the SqlCommand to use Stored Procs).
ReplyDeleteWhether you put each database table into a separate DataSet.DataTable or use a JOIN to put multiple database tables into one DataTable, depends on what you're trying to accomplish and how you plan to display the data in your UI.
Oh, and since you're a newb, perhaps I should mention, in case you're not aware, that you can have many DataTables in one DataSet.
ReplyDeleteI'll look into that, thanks!
ReplyDeleteI'm not sure yet what I'll be doing with the data in this part. It's a very badly arranged database set up. Probably just swapping certain columns into other tables based on certain criteria for now. I'll just practice the techniques here until that's right and have lots of experience by the time it's done.
Thanks again!!
Good luck! I'm sure you'll have a good time learning (frustrating sometimes maybe, but interesting nonetheless). If you get stuck on anything, feel free to ask me about it.
ReplyDeleteHere's just a few things to keep in mind:
1) If you plan to use Typed DataSets (and I advise you to), you need to avoid TableAdapters. They are *evil*! See my rant against TableAdapters here:
http://geek-goddess-bonnie.blogspot.com/2009/09/tableadapters-are-crap.html
That post also contains a link to another of my blog posts:
http://geek-goddess-bonnie.blogspot.com/2010/04/create-xsd.html
about how to go about creating an .xsd and generating the Typed DataSet without all the TableAdapter junk getting put into it.
2) Base all your projects and classes around functionality. IOW, you might have a CustomerDataSet and an InventoryDataSet. Two different DataSets for two different kinds of functionality. Likewise, you'd want a separate DataAccess class (or project, depending on the size of your application) for each of those two functionalities ... so a CustomerDataAccess and an InventoryDataAccess.
3) A DataSet doesn't need to look anything like the tables in the database, especially if it's a poorly designed database. Design your DataSet and its DataTables around the functionality you need to use in your application and let the Stored Procs worry about which tables it needs to utilize to get/save the data. Avoid putting everything into one DataTable though ... remember that a DataSet can have multiple DataTables and you can set up relationships if you want to (or utilize filtered DataViews to simulate relationships).
BTW, what's your name? I occasionally get other "Anonymous" comments, so I'd like to know if I get any additional questions/comments from "Anonymous" whether or not it's from you.
My name is Aaron. Sorry to post under anonymous so many times, I just don't have any accounts set up on any of the links provided.
ReplyDeleteThat's ok Aaron, no problem. I just like to know who I'm "talking" to. =0) You don't need to set up any accounts just for these comments, just let me know it's you somewhere in your comment and that's good enough!
ReplyDeleteHi Bonnie,
ReplyDelete"DataAccess in Windows Forms is a much misunderstood concept."
Seems overly complicated, like a Rube Goldberg device. :) Seriously, a lot about its philosophy seems closer to the older "hierarchical" than to the current "relational" database paradigms. The concept of moving back and from from parent to children records reminds me of that.
But it is what it is. Visual Studio offers a lot of wizardy, so I suppose it's everybody's individual challenge as developers to find the right balance of drag-and-drool and groan-and-code.
"First, you need to have a base DataAccess class that all of your other DataAccess classes should inherit from . . ."
I'm getting the impression that you almost have to develop your own framework, so to speak, something that interacts with .NET along the balanced lines one settles on above?
And what do you think of the barebones approach some developers take, where they don't use datasets, adapters, or even bound controls. Instead, assuming an Add Record form, they programmatically use the control values in parameterized INSERT commands?
Robert in SF
(from the MSDN forums (See? I *am* reading your links!))
Hi Robert! Thanks for reading!
ReplyDelete>>Seems overly complicated, like a Rube Goldberg device. :)<<
Well, not really. The basic concept is that you really need to separate your UI from your DAL. Plain and simple.
You really do need some kind of basic Framework, but it doesn't have to be super complicated. Whether you roll-your-own, go with some free open source stuff or buy some fancy commercial add-ins, it doesn't matter. I've rolled-my-own, but it developed slowly on an as-needed basis. And not just DataAccess, but even Forms and all controls I'd use on the Forms all started out as a sub-class. You start off using your own classes right off the bat, and then you can add to and develop those classes as you need to. Your Framework grows with you. See this blog post:
http://geek-goddess-bonnie.blogspot.com/2009/10/why-and-how-to-sub-class-base-classes.html
>>And what do you think of the barebones approach some developers take, where they don't use datasets, adapters, or even bound controls. Instead, assuming an Add Record form, they programmatically use the control values in parameterized INSERT commands?<<
I think that's a bad idea. You've got to databind your controls ... why wouldn't you want to? Databinding works great in Windows Forms. And Typed DataSets are really nice too (as long as you avoid the TableAdapter pitfalls, see my blog post about that too ... there's a link to it a couple of comments back). There are those who prefer entity classes instead of Typed DataSets (and use stuff like the Entity Framework), but I like DataSets. They've got all the built-in functionality that you'd have to add to an entity class yourself (or with some kind of ORM tool or other code-generator).
BTW, you're from San Francisco? I used to live in Napa about 11 years ago (I lived there for 6 years & my oldest son still lives there). SF is one of my very favorite cities! I just love it!
Yes, I'm from SF. I'm glad you like it! :)
ReplyDeleteI'm still working on this, so I have nothing to add yet.
Robert
I'm working on a basic for this, and it throws errors before I even start with serious coding. Any idea?
ReplyDeletenamespace BlankTest
{
public class DataLayer
{
protected OleDbConnection olCon;
public DataLayer
{ // keeps saying invalid token or struct
//can't call olCon from here
}
}
}
Did I miss something fairly obvious? Not even started and it's telling me I did it wrong...
-Jim
Sorry Jim, looks like I have a typo in my posted code. You need to have parentheses after the constructor of the DataLayer class:
ReplyDeletepublic DataLayer()
{
}
I've been messing around with different blog plug-ins trying to find a good one so that the code I post will be easy to copy/paste. So perhaps when I fix this typo I should decide on a better plug-in start and using it (not that it would have helped you this time, since I goofed).
I try not to copy and paste too much, takes away from the lesson. And I was right, it really should have been fairly obvious.
ReplyDeleteThanks!
-Jim
You're welcome Jim. And, BTW, you'll get more out of the 2nd post in this series ... it's a bit more useful.
ReplyDelete