I have a data layer class where I create all the SQL statements inside methods. In my UI layer i have a combo box. What I want to do is, I want to fill the combo box from the data layer, not from the UI layer.
So far I have typed this code inside data layer....
public void ComboImageList()
{
try
{
SqlCommand command = new SqlCommand("SELECT ImageName FROM [ImageWithTags]", con);
con.Open();
SqlDataReader reader = command.ExecuteReader();
while(reader.Read())
{
string name = reader.GetString(reader.GetOrdinal("ImageName"));
}
con.Close();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Since I cannot access UI layer directly from Data layer, I dont know how to pass the values. I dont know if I can pass the combo box to the data layer or I have to pass the string to the UI layer.
can someone help?
You shouldn't do that, because such design leads to numerous problems: awkward testing, hard to refactor, relying on particular database and UI controls where you shouldn't, it will be hard to reuse DAL from other places.
Diagram
User -> UI -> DAL -> Database
|
User <- UI <- DAL <------
Proper way of doing this will be to have a method in DAL like
IEnumerable<string> GetNames()
{
// execute your SQL and return result as some abstract collection
using( /* connection setup */
{
using(var command = new SqlCommand("SELECT ImageName FROM [ImageWithTags]", con))
{
con.Open(); // check this, maybe it could be opened in first using
SqlDataReader reader = command.ExecuteReader();
while(reader.Read())
{
yield return reader.GetString(reader.GetOrdinal("ImageName"));
}
}
}
}
And in UI layer you just call it like so
public void FillCombos()
{
var repo = new DALRepo();
var names = repo.GetNames();
// now you can assign names to your combobox
// ...
}
Related
I have recently started refactoring an old system designed by someone with little experience in OOP. Thankfully, (nearly) all access to the database are within a single 3000 lines long file. That files contains a Dictionary<string, SqlCommand>, the SqlConnection, a very long function adding every single SQL query to the dictionary like this:
cmd = new SqlCommand(null, _sqlConnection);
cmd.CommanText = "SELECT * FROM User WHERE User.UserID = #id;" // Most queries are far from being this simple
cmd.Parameters.Add(new SqlParameter("#id", SqlDbType.Int, 0));
cmd.Prepare();
_cmds.Add("getUser", cmd);
Those queries are used by functions within that same file that would look like this:
public void deleteUser(int userId)
{
if (_cmds.TryGetValue("deleteUser", out SqlCommand cmd))
{
lock(cmd)
{
cmd.Parameters[0].Value = userId;
cmd.ExecuteNonQuery();
}
}
}
public int isConnected(int userId, out int amount)
{
bool result = false;
amount = 0;
if (_cmds.TryGetValue("userInfo", out SqlCommand cmd))
{
lock (cmd)
{
cmd.Parameters[0].Value = userId;
using (SqlDataReader reader = new cmd.ExecuteReader())
{
if (reader.HasRows)
while (reader.Read())
{
amount = (int)Math.Round(reader.GetDecimal(0));
result = reader.GetInt32(1);
}
}
}
}
return result;
}
Now this is horrible to work with and maintain. I finally have the time to refactor this. I wanted to turn this into a proper DAL with repositories which would be used by services and be dependency injectable.
I don't really care to change the functions or the queries (using a ORM for example). What I'm more interested in is to split the file into many files in a way that would allow me to mock, test and modify it more easily. I'm looking for a way to better structure the existing code, though I know a lot of copy/pasting and recoding will be required.
Would recommend replacing the manually written object-mapping code with using an Object-Relational Mapper like NHibernate, which will save the time and effort of creating and maintaining a data access layer.
Check out Dapper. It is a "micro-ORM" and offers high-performance object-oriented data access. You can continue to use all the existing queries, but replace all the boiler-plate ADO.NET code with Dapper.
This is going to take some repetitive work, but here are a few ideas on how to get a handle on it. This won't put the code in some ideal state, but might make it a little bit more manageable. One challenge is that every method has parts in two places - one in the method and one where the command is stored in the dictionary.
Don't add any more SQL to this class, ever. Begin defining and using the new repositories you want.
Being able to mock it is easy, too. You can use the extract interface refactoring to create an interface so that you can mock this class, even in its current form. That's going to be a big, ugly interface, but at least you can mock methods if you need to.
That's the easy part. How can the entire class be refactored without breaking any one part of it? These steps are just some ideas:
A first step is just to inject the connection string the class needs:
public class YourDataAccessClass
{
private readonly string _connectionString;
public YourDataAccessClass(string connectionString)
{
_connectionString = connectionString;
}
}
You'll use it one method at a time. Initially you can leave most of the class, including the dictionary, as-is. That way the methods you haven't modified will continue to work.
Next, you could open up the class in two separate windows so that you can see the dictionary function that contains the SQL and the functions that use it side-by-side. This will be a lot harder if you have to scroll back up and down.
You'll likely want to move the SQL for each function into that function. You could do this as you refactor each function, but it might be less painful to do it all at once so that you gain efficiency from repetition.
You could define a new variable in each function and copy and paste:
var sql = "SELECT * FROM User WHERE User.UserID = #id;";
(Again, not the way I'd normally write this.)
Now you've got a function or 100 functions that look like this:
public void deleteUser(int userId)
{
var sql = "DELETE User WHERE User.UserID = #id;";
if (_cmds.TryGetValue("deleteUser", out SqlCommand cmd))
{
lock(cmd)
{
cmd.Parameters[0].Value = userId;
cmd.ExecuteNonQuery();
}
}
}
For the non-query commands you could write a function like this in your class which will eliminate the repetitive code to open a connection, create a command, etc:
private void ExecuteNonQuery(string sql, Action<SqlCommand> addParameters = null)
{
using (var connection = new SqlConnection(_connectionString))
using (var command = new SqlCommand(sql))
{
addParameters?.Invoke(command);
connection.Open();
command.ExecuteNonQuery();
}
}
Save the following snippet of code. You might even just be able to keep it in the clipboard most of the time. Paste it into each one of your non-query methods right beneath the SQL:
ExecuteNonQuery(sql, command =>
{
});
After you paste it, move the line or lines that add parameters into the body of the cmd argument (which is named cmd so that you can move the lines without changing the variable name) and then delete the existing code that executed the query previously.
ExecuteNonQuery(sql, cmd =>
{
cmd.Parameters[0].Value = userId;
});
Now your function looks like this:
public void deleteUser(int userId)
{
var sql = "DELETE User WHERE User.UserID = #id;";
ExecuteNonQuery(sql, cmd =>
{
cmd.Parameters[0].Value = userId;
});
}
I'm not saying that's fun, but it will make the process of editing those functions more efficient since you're typing less and just moving things around in exactly the same way over and over.
The ones that actually return data are less fun, but still manageable.
First, take pretty much the same boilerplate code. This could likely be improved because it's still a little repetitive, but at least it's more self-contained:
using (var connection = new SqlConnection(_connectionString))
using (var cmd = new SqlCommand(sql)) // again, named "cmd" on purpose
{
connection.Open();
}
Starting with this:
public int isConnected(int userId, out int name)
{
var sql = "SELECT * FROM User WHERE User.UserID = #id;";'
bool result = false;
amount = 0;
if (_cmds.TryGetValue("userInfo", out SqlCommand cmd))
{
lock (cmd)
{
cmd.Parameters[0].Value = userId;
using (SqlDataReader reader = new cmd.ExecuteReader())
{
if (reader.HasRows)
while (reader.Read())
{
amount = (int)Math.Round(reader.GetDecimal(0));
result = reader.GetInt32(1);
}
}
}
}
}
Paste your boilerplate into the method:
public int isConnected(int userId, out int name)
{
var sql = "SELECT * FROM User WHERE User.UserID = #id;";'
bool result = false;
amount = 0;
using (var connection = new SqlConnection(_connectionString))
using (var cmd = new SqlCommand(sql)) // again, named "cmd" on purpose
{
connection.Open();
}
if (_cmds.TryGetValue("userInfo", out SqlCommand cmd))
{
lock (cmd)
{
cmd.Parameters[0].Value = userId;
using (SqlDataReader reader = new cmd.ExecuteReader())
{
if (reader.HasRows)
while (reader.Read())
{
amount = (int)Math.Round(reader.GetDecimal(0));
result = reader.GetInt32(1);
// was this a typo? The code in the question doesn't
// return anything or set the "out" variable. But
// if that's in the method then that will be part of
// what gets copied.
}
}
}
}
}
Then, just like before, move the part where you add your parameters above connection.Open(); and move the part where you use the command just beneath connection.Open(); and delete what's left. The result is this:
public int isConnected(int userId, out int name)
{
var sql = "SELECT * FROM User WHERE User.UserID = #id;";'
bool result = false;
amount = 0;
using (var connection = new SqlConnection(_connectionString))
using (var cmd = new SqlCommand(sql)) // again, named "cmd" on purpose
{
cmd.Parameters[0].Value = userId;
connection.Open();
using (SqlDataReader reader = new cmd.ExecuteReader())
{
if (reader.HasRows)
while (reader.Read())
{
amount = (int)Math.Round(reader.GetDecimal(0));
result = reader.GetInt32(1);
}
}
}
}
You can probably get into a groove and do these in a minute or two each, which means that it will only take a few hours.
Once all of this is done you can delete your massive dictionary function. Now the class depends on an injected connection string and opens and closes connections normally instead of storing a connection and using it over and over.
You can also break it up. One way is to move the connection string and the helper function into a base class (or just duplicate the helper function - it's really small) and you can move any of the query functions into a smaller class because each function is self-contained.
I have a page load method that loads asp dropdowns with sql queries to my SQL Server 2012 database. I'm new to this and basically independently learned a lot of needed to be done for the co op project I'm working on.
I've been running into problems with connections not being closed properly and having my connection pool blow up with only moderate usage of my app so I've been trying to improve how I'm executing my queries in my c# code behind. But I am not confident in my understanding of this so I'm going to post an example of my code and hope that someone much more fluent might be able to guide me a bit.
string constr = ConfigurationManager.ConnectionStrings["CurrencyDb"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr)) {
using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.Category")) {
try {
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
con.Open();
//Populate Category Dropdown
DDCategory.DataSource = cmd.ExecuteReader();
DDCategory.DataTextField = "CategoryName";
DDCategory.DataValueField = "CategoryId";
DDCategory.DataBind();
}
catch (SqlException sqlex) {
throw new Exception("SQL Exception loading data from database. " + sqlex.Message);
}
catch (Exception ex) {
throw new Exception("Error loading Category data from database. " + ex.Message);
}
}
using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.SubCategory ORDER BY SubCategoryName")) {
try {
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
//Populate SubCategory Dropdown
DDSubCategory.DataSource = cmd.ExecuteReader();
DDSubCategory.DataTextField = "SubCategoryName";
DDSubCategory.DataValueField = "SubCategoryId";
DDSubCategory.DataBind();
}
catch (SqlException sqlex) {
throw new Exception("SQL Exception loading data from database. " + sqlex.Message);
}
catch (Exception ex) {
throw new Exception("Error loading Subcategory data from database. " + ex.Message);
}
}
}
The above are two queries of about 8 on my page load method.
My latest error was
There is already an open DataReader associated with this Command which must be closed first.
This prompted me to ask the question, I set MultipleActiveResultSets=true in my Web.config connection string and my app works now, but I feel as though that's a patch to cover what is likely crappy code.
What is the best practice for doing this? Thanks so much in advance!
Well, there could be multiple way but I feel you should wrap all this queries in a stored procedure and call that SP in your code behind. Instead of using DataReader use a DataSet and fill it up with the different resultset.
You can also use NextResult() method of datareader instance to get the next SELECT result and do your processing.
To your immediate question, you are using two cmds per one connection.
You could be better off using one-trip, one command, with a multiple-result.
This will allow you to properly .Close() your datareader and your connection...."returning them back to the pool"
Since you are using SqlServer, it supports multiple resultsets in one "trip".
SELECT * FROM dbo.Category;SELECT * FROM dbo.SubCategory
Use that as your select statement.
Use an .ExecuteReader() method.
And use a .NextResult() to move from one result (category), to the next one (subcategory)
You can see a more complete sample here:
https://msdn.microsoft.com/en-us/library/system.data.idatareader.nextresult(v=vs.110).aspx
Or even with Entity Framework here:
https://msdn.microsoft.com/en-us/library/jj691402%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396
Now some extra stuff.
You are mixing your datalayer and your presentation layer.
You should have your datalayer populate "Dto" sometimes referred to as "Poco" objects, and return those to the Presentation layer.
public class Category
public string CategoryKey{get;set;}
public string CategoryName{get;set;}
public ICollection<SubCategory> SubCategories {get;set;}
..
public class SubCategory
public string SubCategoryKey{get;set;}
public string SubCategoryName{get;set;}
..
public class MyDataLayerObject
public ICollection<Category> GetAllCategories()
{
ICollection<Category> categories;
ICollection<SubCategory> subcats;
// write a datareader call here, and use it to populate multiple Category and SubCategory objects
// make sure you close the datareader when done
//now "match up" the subcats to its parent category
}
Then have your presentation layer "bind" to the ICollection of Categories.
You might have a business layer between the presentation and datalayers, but at least to the presentation and datalayers.
I also answered a question at the link below...that is similar to yours. Their objects are "Question" and "Answer" where a Question has 0:N number of Answers.
Find my answer at the below question.
Return objects with populated list properties from stored procedure
Note, you can just use the 2 select queries in a string (the second line of my/this answer), aka, you don't have to create a stored procedure.
Another option is to use nuget to get Microsoft.EnterpriseLibrary.Data.
This encapsulates alot of great practices for you...including closing connections.
You'll still have to close your datareader, but the code is alot cleaner.
These two should be in different methods. One to get category and one to get sub-categories. Then each will have a different connection and you won't have this problem.
You should call Dispose on your DataReader or either put it inside a using statement so that Dispose() is called for you.
The typical IDataReader pattern looks like this:
using (IDataReader r = query.ExecuteReader())
{
while (r.Read())
{
// etc.
}
}
When you don't call Dispose() on the IDataReader he can't be cleaned up. And I expect that your connection also can't be cleaned up even when you call Dispose() on the connection, because it's stil used by your IDataReader.
And yes per Connection you can only use one IDataReader.
So as others wrote you should split this up into more than one method and use one connection per method.
Btw.:
It would be better not to use "SELECT *". Just query the fields you need.
I want to create an autocomplete textbox with my database.
I'm programming my application in a layered architecture (models, DAL, BLL, Presentation).
I've already made a method with an arraylist that reads and returns my select command in the database, which is filling (I've tested on a combobox).
But when I try to insert in the the textbox, nothing happens... it doesn't show the suggestion.
I looked for something in the forum but I just found examples with one layer and, since I'm developing in layers I cannot increment the property AutoCompleteStringCollection in my DAL to be filled by my select command.
If anyone has any idea how to solve this problem, please explain to me!
Additional information: I'm using winForm with C# and SQL Server.
I think you want to say that "But when i try to insert in the textbox, nothing happens... it doesn't show the sugestion."
well i cannot just code all layers here but can suggest in your DAL create a method which returns List and then on your form page provide code like this
txtName.AutoCompleteMode = AutoCompleteMode.Suggest;
txtName.AutoCompleteSource = AutoCompleteSource.CustomSource;
var autoCompleteCollection = new AutoCompleteStringCollection();
autoCompleteCollection.AddRange(DAL.GetMethod().ToArray());
textbox.AutoCompleteCustomSource = autoCompleteCollection;
Thanks for the help!!
I used your suggest and had made some little changes and it work just fine to me...
Turns out that the only problem was my method list, once I change it do a List < String > things got better.
For who is wondering, here is how I do it:
DAL LAYER:
public List<string> LoadList()
{
List<string> tagsList = new List<string>();
using (SqlConnection connection = new SqlConnection(ADados.StringDeConexao))
{
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT column FROM table";
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
if (!reader.IsDBNull(0))
tagsList.Add(reader.GetString(0));
}
reader.Close();
}
connection.Close();
return tagsList;
}
PRESENTATION LAYER (Event TextChanged):
PedidoBLL pedido = new PedidoBLL();
txtName.AutoCompleteMode = AutoCompleteMode.Suggest;
txtName.AutoCompleteSource = AutoCompleteSource.CustomSource;
AutoCompleteStringCollection popula = new AutoCompleteStringCollection();
popula.AddRange(pedido.LoadList().ToArray());
txtName.AutoCompleteCustomSource = popula;
In the BLL Layer i just call and return the DAL method LoadList...
Sorry in advance im going to try and explain this as best as possible....
I have 2 asp.net pages one named membermaster and the second named memberdetails. I created a class library which contains 2 functions
My first function returns a list depending on the search result...
I added a linkbutton to the gridviews first column which when clicked it passes through querystring the membershipgen. What i wanted to do is for my second function i created this
public DataTable GetMembers(int MEMBERSHIPGEN)
{
DataTable table = null;
SqlConnection con = null;
SqlCommand cmd = null;
SqlDataAdapter ad = null;
SqlParameter prm = null;
try
{
table = new DataTable();
using (con = new SqlConnection(connectionString))
{
using (cmd = new SqlCommand("usp_getmemberdetail", con))
{
using (ad = new SqlDataAdapter(cmd))
{
prm = new SqlParameter("#MEMBERSHIPGEN", SqlDbType.Int);
prm.Value = MEMBERSHIPGEN;
cmd.Parameters.Add(prm);
ad.Fill(table);
}
}
}
}
catch (Exception ex)
{
//write your exception code here
}
return table;
}
In the attempt to try and send the membershipgen to this and it return the results. But once i compile the DLL and add it to my project I am not sure how i would reference this function to populate individual textboxes and labels with the information.
What I am trying to do is when a user clicks the viewdetails button on the gridview I can then use that membershipgen that I passed through querystring to populate the page through a stored procedure but the smarts would be stored in a DLL.
You probably want your method to return a value. Currently the return type is void, so the values it populates internally just go away when the call stack leaves the method. It sounds like you want something like this:
public DataTable GetMembers(int MEMBERSHIPGEN)
Then, in your method, after you've populated the DataTable and exited the using blocks, you'd do something like this:
return table;
This would return the DataTable to whatever called the method. So your page would have something like this:
DataTable table = GetMembers(membershipgen);
So the page would be responsible for:
Get the membershipgen value from the input (query string)
Call the method and get the result of the method
Display the result from the method (bind to a grid? or whatever you're doing to display the data)
And the method is responsible for:
Interact with the database
This is a good first step toward the overall goal of "separation of concerns" which is a very good thing to do. You can continue down this path by always asking yourself what each method, class, etc. should be responsible for. For example, your GetMembers method should also be responsible for ensuring that the value passed to it is valid, or that the value returned from it is not null.
You need to change GetMembers to return data instead of void. If you want to use DataTables, you can just modify your code to this:
public DataTable GetMembers(int MEMBERSHIPGEN)
{
DataTable table = new DataTable();
SqlConnection con = new SqlConnection(connectionString);
using (SqlCommand cmd = new SqlCommand("usp_getmemberdetail", con))
{
using (SqlDataAdapter ad = new SqlDataAdapter(cmd))
{
SqlParameter prm = new SqlParameter("#MEMBERSHIPGEN", SqlDbType.Int);
prm.Value = MEMBERSHIPGEN;
cmd.Parameters.Add(prm);
ad.Fill(table);
return table;
}
Then in your Page_Load it might be something like this (more robust than this hopefully):
{
DataTable table = yourDll.GetMembers(Convert.ToInt32(Request.QueryString["membership"]));
label1.Text = Convert.ToString(table.rows[0]["Name"]);
}
One way to go might be to construct the button so that it navigates to a url along the lines of:
http://localhost/DetailPage.aspx?membershipgen=4
Then in the load of the DetailPage.aspx:
Page_Load(Object sender, EventArgs e)
{
if (!this.IsPostback)
{
int membershipgen;
if (int.TryParse(Request.QueryString["membershipgen"], out membershipgen)
{
//Get the data (replace DataAccess with the name of your data access class).
//Also, you probably want to change GetMembers so it returns the data.
DataTable table = DataAccess.GetMembers(membershipgen);
//TODO: Display the results
}
}
else
{
//Display an error
}
}
I'm a big fan of keeping my code simple and trim so it can be re-usable, on thing i'm struggling with is using the data reader for different types of objects, I had it in a method and found there were problems with connections closed or being left open. SO I am being forced, for the mean time to copy and paste the code, which is something I hate!!!
Is there any way I can scale this down so I can put it in a method and make it re-usable and nice?
ENT_AuctionBid ret = new ENT_AuctionBid();
try
{
SqlParameter[] Params = new SqlParameter[]{
new SqlParameter("#ID", ID )
};
using (SqlConnection conn = new SqlConnection(this.ConnectionString))
{
using (SqlCommand command = new SqlCommand("GetItem", conn))
{
SqlDataReader reader;
command.CommandType = CommandType.StoredProcedure;
conn.Open();
command.Parameters.AddRange(Params);
reader = command.ExecuteReader(CommandBehavior.SingleRow);
while (reader.HasRows)
{
while (reader.Read())
{
//
ret = this.Convert(reader);
}
reader.NextResult();
}
reader.Close();
}
}
}
catch (Exception ex)
{
}
return ret;
You should use SQLDataAdapter.
Here's a nice example on how to use it:
http://www.dotnetperls.com/sqldataadapter
Also, you might want to consider switching to Entity Framework, it will make your data access much, much easier, but might be complicated in an existing project.
You can make it using a lot less lines:
// Skipped creating temp variable
try {
using (SqlConnection conn = new SqlConnection(this.ConnectionString))
using (SqlCommand command = new SqlCommand("GetItem", conn) { CommandType = CommandType.StoredProcedure} ) {
command.Parameters.AddWithValue(#ID, ID);
conn.Open();
// reader is IDisposable, you can use using
using (var reader = command.ExecuteReader(CommandBehavior.SingleRow)) {
// Skipped parsing multiple result sets, you return after the first
// otherwise there's no point using SingleRow
// If nothing is read, return default value
return reader.Read() ? this.Convert(reader) : new ENT_AuctionBid();
}
}
}
catch (Exception ex) {
// Handle your exception here
}
// Return default value for error
return new ENT_AuctionBid();
All connections are closed using this code (because using is used). No unneeded loops are created, becuase you only expect a single row. And the temporary variable is not needed, so the abondend object is not created, only when it is used it is created.
This is a bit smaller:-
try
{
using (SqlConnection conn = new SqlConnection(this.ConnectionString))
{
using (SqlCommand command = new SqlCommand("GetItem", conn))
{
command.Paramaters.AddWithValue("#ID",ID);
command.CommandType = CommandType.StoredProcedure;
conn.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
//
ret = this.Convert(reader);
}
}
}
}
catch (Exception ex)
{
}
Create helper methods for creating and returning an object of type SqlCommand. Pass a connection object to this helper method as well as stored procedure name and parameters list (if any). If you have different objects that are created from the data reader, pass the data reader to a constructor and let it generate an object based on that data.
As for closing the connection you should always have try...catch...finally. In the finally section close the connection.
In my projects i usually solve this problem creating an utility class that contains all the methods to access to the DB and manage inside all the stuff related to the db connection and the adapter.
For example a class called DBSql which contains a connection (SqlConnection connection;) as private member and the following methods:
//execute the query passed to the function
public System.Data.DataSet ExecuteQuery(string query)
//returns if a query returns rows or not
public bool HasRows(string query)
//execute commands like update/insert/etc...
public int ExcuteNonQuery(string sql)
In my class, you just pass a string and the class initialize the various DataAdapter and Command to execute it and return a dataset. Obiously you can complicate it to manage parameters/transaction and everything else.
In this way you are sure that the connection and the object are always handled the same way, and, hopefully, in a correct way.
You can use a utility file, such as SqlHelper.cs from Microsoft Data Access Application Block. Then all the code you need is this:
using (SqlDataReader sdr = SqlHelper.ExecuteReader(this.ConnectionString, "GetItem", ID))
{
while (sdr.Read())
{
ret = this .Convert(sdr);
}
}
You could start using LINQ-to-SQL, which has it's own DataClass system in which you just drag-&-drop your database tables and stored procedures. Then you just have to create an instance at the top of your classes -- private MyCustomDataClass _db = new MyCustomDataClass(); and then you can just type in _db.<Here all datatables and SPROCs will appaer for you to choose>.
Example (from when all SPROCs are added to the DataClass)
private MyCustomDataClass _db = new MyCustomDataClass();
public void MethodToRunSPROC(string email, Guid userId)
{
_db.MySPORC_AddEmailToUser(email, userId);
}