Good question!
Regarding data binding, you should look at it as one component of your overall data access strategy. My strategy consists of three components (I hit your DataBinding in component 2):
First, I always create (or reuse) a data access layer (DAL) to simplify data access. This is not only due to the fact that I can someday exchange your database for another - the probability of this is thin enough that it does not guarantee all the work that will be required (YAGNI). You do this so that you can: A) remove all the clutter from the normal database code (for example, get a connection string, establish and close connections, etc.) and B) simplify common operations with selected functions.
Second, you absolutely must implement ObjectDataSources that encapsulate a DataBinding for your interface elements. If you created a good DAL, it becomes quite trivial. For example, here is an ObjectDataSource object that uses my DAL:
[DataObjectMethodAttribute(DataObjectMethodType.Select, true)] public List<EnrollListMemberData> GetNameList(int classID, DateTime classDate) { using (BSDIQuery qry = new BSDIQuery()) { return qry.Command( "Select a.ClassDate, a.ClientID, b.FirstName, b.LastName, b.ID From ClassEnroll a inner join Folder b on (a.ClientID = b.ClientID) Where (a.ClassID=@ClassID) AND ") .ParamVal(classID) .Append("(DateDiff(d, a.ClassDate, @ClassDate) = 0) Order By LastName;") .ParamVal(classDate) .ReturnList<EnrollListMemberData>(); } }
A few notes: the DataObjectMethodAttribute attribute will make this method visible to the development environment, so you will see it in the drop-down list of data sources when you go to link your grid (for example, or something else). You will also need the [DataObjectAttribute] attribute for the class that provides this method (this class, if included in my business layer). Finally, this is a fairly simple example and does not have some common constructs, such as the startRowIndex and maximumRows parameters for returning the output.
Please note that the specific call here from my DAL is not LinqToSQL, although it has surface similarities. I like SQL, and I don't want C # idioms that just have an arbitrary mapping back to SQL anyway . Please note that if I tried to implement all this in direct ADO calls, this function would be three times longer and have LOTS code, which really was not suitable for expressing my goals.
Thirdly, I always place multistage database operations in stored procedures to minimize the number of wire calls in the database. For example, I provide the Check function in one product that accepts a verification request, checks it against the membership table, retrieves the previous history check (how many visits in the last month?), Accrues reward points, if necessary, etc. Running a few queries and database changes in C # code would be terribly complicated and quite expensive. In accordance with our DAL philosophy, I also encapsulate the stored procedure call in my DAL, so that the actual call from the code is simple:
int status = dal.CheckIn (userID, ref checkInHistory);
As you can see, using a stored procedure and encapsulating a C # class method also makes the code much easier to read. My code just says what it does (as stated above), instead of having over 100 lines of code setting up queries, etc.
I hope this helps!