As part of my common utilities I used in all my line of business applications, I have this code...
using System.Web.UI.WebControls;
public class Database
{
/// <summary>
/// Creates a DataView object using the provided query and an SqlDataSource object.
/// </summary>
/// <param name="query">The select command to perform.</param>
/// <returns>A DataView with data results from executing the query.</returns>
public static DataView GetDataView(string query)
{
SqlDataSource ds = GetDBConnection();
ds.SelectCommand = query;
DataView dv = (DataView)ds.Select(DataSourceSelectArguments.Empty);
return dv;
}
/// <summary>
/// Creates a SqlDataSource object with initialized connection string and provider
/// </summary>
/// <returns>An SqlDataSource that has been initialized.</returns>
public static SqlDataSource GetDBConnection()
{
SqlDataSource db = new SqlDataSource();
db.ConnectionString = GetDefaultConnectionString(); //retrieves connection string from .config file
db.ProviderName = GetDefaultProviderName(); //retrieves provider name from .config file
return db;
}
}
Then, in my projects, to retrieve data from databases I'll have some code like..
DataView dv=Database.GetDataView("select mycolumn from my table");
//loop through data and make use of it
I have taken some heat from people for using SqlDataSource in this manner. People don't seem to like that I'm using a Web control purely from code instead of putting it on an ASPX page. It doesn't look right to them, but they haven't been able to tell me a downside. So, is there a downside? This is my main question. Because if there's a lot of downsides, I might have to change how I'm doing many internal applications I've developed.
My Database class even works from non-ASP.NET situations, so long as I add the System.Web assembly. I know it's a slight increase in package size, but I feel like it's worth it for the type of application I'm writing. Is there a downside to using SqlDataSource from say a WPF/Windows Forms/Console program?
Well, there are no hard rules stopping anyone from doing such implementation.
However, following are few questions that need to be answered before doing that implementation.
Is this usage thread safe? (because there is every possibility the same call can be made by multiple consuming applications.
Will there be a layered differentiation (UI.Control being used in a Data layer)?
What if that control becomes obsolete / restricted in the next framework releases?
Given how easy it is to replace this code, whilst removing the temptation to use dynamic SQL queries to pass parameters, I think the question should be: is there any benefit to keeping the code as-is?
For example:
public static class Database
{
private static readonly Func<DbCommandBuilder, int, string> getParameterName = CreateDelegate("GetParameterName");
private static readonly Func<DbCommandBuilder, int, string> getParameterPlaceholder = CreateDelegate("GetParameterPlaceholder");
private static Func<DbCommandBuilder, int, string> CreateDelegate(string methodName)
{
MethodInfo method = typeof(DbCommandBuilder).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null);
return (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), method);
}
private static string GetDefaultProviderName()
{
...
}
private static string GetDefaultConnectionString()
{
...
}
public static DbProviderFactory GetProviderFactory()
{
string providerName = GetDefaultProviderName();
return DbProviderFactories.GetFactory(providerName);
}
private static DbConnection GetDBConnection(DbProviderFactory factory)
{
DbConnection connection = factory.CreateConnection();
connection.ConnectionString = GetDefaultConnectionString();
return connection;
}
public static DbConnection GetDBConnection()
{
DbProviderFactory factory = GetProviderFactory();
return GetDBConnection(factory);
}
private static void ProcessParameters(
DbProviderFactory factory,
DbCommand command,
string query,
object[] queryParameters)
{
if (queryParameters == null && queryParameters.Length == 0)
{
command.CommandText = query;
}
else
{
IFormatProvider formatProvider = CultureInfo.InvariantCulture;
DbCommandBuilder commandBuilder = factory.CreateCommandBuilder();
StringBuilder queryText = new StringBuilder(query);
for (int index = 0; index < queryParameters.Length; index++)
{
string name = getParameterName(commandBuilder, index);
string placeholder = getParameterPlaceholder(commandBuilder, index);
string i = index.ToString("D", formatProvider);
command.Parameters.AddWithValue(name, queryParameters[index]);
queryText = queryText.Replace("{" + i + "}", placeholder);
}
command.CommandText = queryText.ToString();
}
}
public static DataView GetDataView(string query, params object[] queryParameters)
{
DbProviderFactory factory = GetProviderFactory();
using (DbConnection connection = GetDBConnection(factory))
using (DbCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
ProcessParameters(factory, command, query, queryParameters);
DbDataAdapter adapter = factory.CreateDataAdapter();
adapter.SelectCommand = command;
DataTable table = new DataTable();
adapter.Fill(table);
return table.DefaultView;
}
}
}
With this version, you can now pass in parameters simply and safely, without relying on custom code to try to block SQL injection:
DataView dv = Database.GetDataView(
"select mycolumn from my table where id = {0} and name = {1}",
1234, "Robert');DROP TABLE Students;--");
EDIT
Updated to support parameters for different providers, with help from this answer.
The only issues I see are
(1) this is like reinventing the wheel. There is Enterprise library v5 for FW3.5 and v6 for FW4.5, which has data access components. Use that.
With EL you can make a call and have 2,3,4 tables loaded in Dataset. With your method this is not possible, only one at the time.
Enterprise library is a complete Data Access suite provided by Microsoft. It takes care of all the little details and all you need is to call your data. This is complete data access layer. And if you look deeper, EL allows for integration of Data and Caching, and other things. But you don't have to use what you don't need. If you need data access you can use only that.
And (2) Generally, this is not a good idea to write low level assembly with high-level assembly in reference. Anything System.Web.... is UI and client related stuff. In a layered cake design this is like the top of it and Data Access is on the bottom. All references [save for "common"] should travel from bottom to the top and you have it in opposite direction.
Look at this picture:
This is from Microsoft. You see the layers of the "cake". All references are going up. What you've done - you took UI-related component and wrote Data Access in it.
You can call it opinion-based - but this opinion is standard practice and pattern in software development. Your question is also opinion based. Because you can code everything in single file, single class, and it will work. You can set references to System.Windows.Forms in Asp.net application, if you want to. Technically, it is possible but it is really bad practice.
Your application now have limited reusability. What if you write WPF component or service that need to use same Data Access. You have to drag all System.Web into it?
Related
When I try to search data in my user control from my database it does search or filter the data that I typed in the search textbox. Here's the code that I'm using to try and search or filter
SqlConnection cn;
SqlCommand cm;
SqlDataReader dr;
private Label name;
private Label amount;
private Label descrip;
public Form1()
{
InitializeComponent();
cn = new SqlConnection(#"Data Source=(LocalDB)");
}
private void Form1_Load(object sender, EventArgs e)
{
GetData();
}
private void GetData()
{
cn.Open();
cm = new SqlCommand("Select * from Bills where (billname) like '%" + txtSearch.Text + "%'", cn);
dr = cm.ExecuteReader();
while (dr.Read())
{
long len = dr.GetBytes(0, 0, null, 0, 0);
byte[] array = new byte[System.Convert.ToInt32(len) + 1];
dr.GetBytes(0, 0, array, 0, System.Convert.ToInt32(len));
name = new Label();
name.Text = dr["billname"].ToString();
descrip = new Label();
descrip.Text = dr["billdescrip"].ToString();
amount = new Label();
amount.Text = dr["billamount"].ToString();
}
dr.Close();
cn.Close();
}
private void txtSearch_TextChanged(object sender, EventArgs e)
{
GetData();
}
When I type something into the txtSearch.text box, the results come back empty and doesn't display the what im trying to search for in the txtSearch.text box.
"Select * from Bills where (billname) like '%" + txtSearch.Text + "%'"
It seems to me that you have a database table Bills, with a column BillName. The operator types some text in TextBox txtSearch, and you want to fetch all Bills that have a BillName that starts with the text in the TextBox.
I see several problems here
SQL Injection
SQL injection is a code injection technique that might destroy your database.
SQL injection is one of the most common web hacking techniques.
SQL injection is the placement of malicious code in SQL statements
Look what your Sql text would be if the operator types the following text:
"John%; DROP TABLE Bills;--"
Select * from Bills where (billname) like %John%; DROP TABLE Bills; --%
You would lose all Bills!
More information about SQL Injection
Solution: Never, ever add input data into your sql string! Always add it as a parameter!
Start using using statements
The database connection is a scarce resource: you should not keep it alive longer than needed. Also, if you SQL query encounters an exception, the connection and the datareader are not closed.
Make it a habit, that whenever an object implements IDisposable you should use it using a using statement.
This way, you can be assured that whatever happens, at the end of the using statement everything is properly flushed, written, closed and disposed.
SqlConnection, SqlCommand and SqlDataReader should be private members of GetData. This way you can be certain that no one can tamper with your connection; you hide how you fetch the data from the database (SQL and SqLCommand, or Entity Framework and LINQ?), thus making future changes easier. Readers of your code won't have to check where these variables are used, and that no one is misusing it, thus making your code easier to understand. And of course this will make it possible to reuse GetData for other purposes.
Which brings me to the third improvement:
Separate data from how it is displayed
In modern programming you see more and more a separation between the date (= model) and the way that the data is displayed (= view).
Separation makes it better to reuse the code, for instance: if you want to use your model in a console program, or in a WPF program, or even a different Form, you can reuse the model classes.
Separation hides how and where you fetch your data: is it a database? is it a CSV file, or XML? are you using Entity Framework
This hiding allows future changing without having to change all your Forms
This hiding also makes your Forms smaller and easier to understand
While developing the form, you can mock the actual data: just create a dummy class that provides you with sample data, without having to bother about the database
You can unit test the model, without needing a form
It is hardly any extra work.
So you'll have Model classes: your data, and how it is saved, fetched again; and View classes: your Forms. You'll need an adapter class to adapt the model to the view: the ViewModel. Together these three abbreviate to MVVM. Consider to do some background reading about MVVM.
Implementing the three advices
We create a class that makes it possible to save Bills (and other items: Customers? Orders? Products? etc) and later you can retrieve them again, even after you restarted the program. Something like a warehouse, a repository, where you store items and fetch them again.
interface IOrderRepository
{
int AddBill(Bill bill); // return Id of the Bill
Bill FindBill(int Id); // null if not found
// your GetData:
IEnumerable<Bill> FetchBillsWithNameLike(string name);
... // other methods, about Customers, Orders, etc
}
Implementation:
class OrderRepository : IOrderRepository
{
private string ConnectionString {get;} = #"Data Source=(LocalDB)";
private IDbConnection CreateConnection()
{
return new SqlConnection(this.ConnectionString);
}
The implementation of FetchBillsWithNameLike:
public IEnumerable<Bill> FetchBillsWithNameLike(string name)
{
using (IDbConnection dbConnection = this.CreateConnection())
{
const string sqlText = "Select Id, BillName, CustomerId, ..."
+ " from Bills where (billname) like %#Name%";
using (IDbCommand = dbConnection.CreateCommand())
{
// fill the command and the parameter:
dbCommand.CommandText = sqlText;
dbCommand.AddParameterWithValue("#Name", name);
// execute the command and enumerate the result
dbConnection.Open();
using (IDatareader dbReader = dbCommand.ExecuteReader())
{
while (dbReader.Read())
{
// There is a Bill to read
Bill bill = new Bill
{
Id = dbReader.ReadInt32(0),
Name = dbReader.ReadString(1),
CustomerId = dbReader.ReadInt32(2),
...
};
yield return bill;
}
}
}
}
}
// implement rest of interface
}
Several improvements:
Connection string is a property. If you decide to use a different connection string for all your 100 methods: only one place to change.
You hide where you get the connection string: here it is a constant, but if you decide in future versions to read it from the config file: no one has to know, except this method
You hide that you are using a SqlConnection, you return the interface. If in future versions you decide to create a different form of IDbConnection, for instance for a different kind of database, like SQLite, no one has to know that you create a SqlLiteConnection object instead of a SqlConnection.
Similarly: hide SqlCommand, use the interface IDbCommand.
The database connection is not opened before it is needed. This makes it possible that others can use the database as long as you will not use it.
using statements all over the place: if any exception happens, all objects are properly closed and disposed.
I don't create the DbCommand myself, I ask the DbConnection to create it for me, so I don't have to bother which type of commands the actual DbConnection uses: Is it SqlCommand? SQLiteCommand?
I specify which columns from the table if want. If in future some columns are added, and I don't need them, I won't fetch more data than I want. Similarly: if columns are reordered, it will still work.
The most important change: Use SQL parameter to prevent malicious SQL Injection.
Parameters in SQL text are often recognized by prefix #
Parameters are added with the extension method AddParameterWithValue`. Some databases have this as a method in DbCommand (for example: SQLite)
When reading the fetched data, I won't read more Bills than my caller wants. So if he calls me using the following code: not all Bills will be read:
IOrderRepository repository = ...
string name = this.ReadName();
bool billsWithNameAvailable = repository.FetchBillsWithName(name).Any();
Here, my caller only wants to know if there are any Bills with the Name at all. The reader won't create any Bills at all.
Because the SQL text is Select Id, ... and I read dbReader.GetInt32[0] etc, my code will still work, even after inserting or reordering the columns of the table.
The nice thing is, that you will be able to unit test method FetchBillsWithName without having to use a Form: you can test what happens if there is no database at all, or if there is no Bills table, or an empty table, or the table doesn't contain a column BillName. Or what happens if the input text is empty. You can unit test all kinds of errors without need of a Form.
The Form
class Form1 : ...
{
private IOrderRepository Repository {get;} = new OrderRepository();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
GetData();
}
private void GetData()
{
string name = this.txtSearch.Txt;
foreach(Bill fetchedBill in this.Repository.FetchBillsWithNameLike(name))
{
this.ProcessBill(fetchedBill);
}
}
private void ProcessBill(Bill fetchedBill)
{
// do your stuff with the label;
}
}
Because I separated the model from the view, the view is much simpler: much easier to see what really happens: you focus on the form, not on how and where you get the data.
During development, while you don't have a database yet, you can create a dummy repository and test your form:
class DummyRepository : IOrderRepository
{
private Dictionary<int, Bill> Bills {get;} = ... // fill with some sample Bills
// TODO: implement IOrderRepository, using this.Bills
}
If later you decide that you won't get your data from a database, but for instance from the internet, your form will hardly have to change. It can still use IOrderRepository
Conclusion
By separating the model from the view, both model and view are much easier to red and understand. Much easier to reuse, change, maintain and unit test. Both can be developed independantly
Procedure are small and have only one task: this makes that we can reuse the procedures. Changes are only in one procedure
By using interfaces, I hide how and where the data is fetched: SQL? CSV-file? Internet?
By using using statements the program is more full proof: after exceptions everything is property closed and disposed
By using SQL parameters I prevented malicious use of SQL injection.
I need some guidance on the following if possible please
Explanation
I have a main project.cs file in the App_Code which contains main functions. One of these functions is a SQL_Inject which inserts data into the database.
I then have multiple pages that utilize this function from multiple client machines at the same time.
Question
The answer i am after is, is this a safe method of choice? Or should i be creating a new connection separately on each .cs page.
Reason/Problem
Reason this is becoming a concern, we are currently a small company but growing. It has happened that a page crashes due to the SQL Connection is still open. I am worried its due to two connections trying to be made at the same time. I am not sure if this is the issue or if it comes from something else.
//GLOBAL DECLARATIONS
//DB CONNECTIONS - retrieve from config file
public static string ConProjectms = System.Configuration.ConfigurationManager.ConnectionStrings["conProject"].ConnectionString;
//DB CONNECT TO SQL
public static SqlConnection SqlConn = new SqlConnection();
public static SqlCommand SqlCmd = new SqlCommand();
public static SqlDataReader SqLdr;
public static string SqlStr;
public static string ConnString;
public static void SqlInject(string query, string dataBase)
{
SqlConn.ConnectionString = ConProjectms;
//Set the Connection String
SqlConn.Open();
//Open the connection
SqlCmd.Connection = SqlConn;
//Sets the Connection to use with the SQL Command
SqlCmd.CommandText = query;
//Sets the SQL String
SqlCmd.ExecuteNonQuery();
//put Data
SqlClose();
}
public static void SqlClose()
{
if (SqlConn.State != ConnectionState.Open) return;
SqlConn.Close();
SqlCmd.Parameters.Clear();
}
SQL can handle multiple connections at the same time. However, you're code is very likely to be be run by two clients at the same time, and they'll be using the same connection not two separate connections. That's bad thing #1.
SQL Server does a fantastic job of connection pooling - and I assume other DBs have similar capabilities. In such a world, you shouldn't try to keep and reuse any of your data-related objects around - but create them as you need them and when SQL sees that you're using a connection it's created before and since freed up, it'll use that. You don't have to do anything weird to get this functionality.
With that in mind, your static objects should mostly go away, and your SQLInject method might look something like this:
public static void SqlInject(string query, string dataBase)
{
var connectionString =
System
.Configuration
.ConfigurationManager
.ConnectionStrings["conProject"]
.ConnectionString;
using ( var connection = new SqlConnection( connectionString ) )
{
connection.Open( );
using ( var command = connection.CreateCommand( ) )
{
command.CommandText = query;
command.CommandType = CommandType.Text;
command.ExecuteNonQuery( );
}
}
}
Notice that you don't have to worry about closing the connection per se; the using blocks handle the disposition of your open, active objects. This is largely how folks are doing direct SQL from c#. By the way, neither your code nor mine uses the dataBase argument. Maybe you're supposed to edit the base connection string with it??
But wait - there's more!
Having said all that, and since you raised a concern about security, you should know that this isn't safe code at all - yours or mine. SqlInject is probably a good name, because it allows pretty much anything in the query argument (which, BTW, if you're doing ExecuteNonQuery, then maybe query isn't a good name).
You're far far better allowing arguments to a library of known statements (maybe stored procedures), validating those arguments, and using SQL Injection attack mitigation to parameterize your known statements (look up that phrase and you'll find an abundance of examples and advice).
Just for yuks, here's a scaffold of what you might consider:
public static void SqlInject(string commandName, params[] object commandArgs )
{
//--> no point in going on if we got no command...
if ( string.IsNullOrEmpty( commandName ) )
throw new ArgumentNullException( nameof( commandName ) );
var connectionString =
System
.Configuration
.ConfigurationManager
.ConnectionStrings["conProject"]
.ConnectionString;
using ( var connection = new SqlConnection( connectionString ) )
{
connection.Open( );
using ( var command = connection.CreateCommand( ) )
{
command.CommandType = CommandType.Text;
command.CommandText = "select commandText from dbo.StatementRepository where commandName = #commandName";
command.Parameters.AddWithValue( "#commandName", commandName );
var results = command.ExecuteScalar( );
if ( results != null && results != DbNull.Value )
{
//--> calling a separate method to validate args, that returns
//--> an IDictionary<string,object> of parameter names
//--> and possibly modified arguments.
//--> Let this validation method throw exceptions.
var validatedArgs = ValidateArgs( commandName, commandArgs );
command.Parameters.Clear( );
command.CommandText = query;
foreach( var kvp in validatedArgs )
{
command.Parameters.AddWithValue( kvp.Key, kvp.Value );
}
command.ExecuteNonQuery( );
}
else
{
throw new InvalidOperationException( "Invalid command" );
}
}
}
}
I didn't attempt to write an actual argument validating method, because that's all wrapped up in your application logic...but I wanted to give you an idea of how you might get to a safer state.
There's no reason why database code inside App_Code shouldn't work. It sounds more like your connection pooling doesn't work very well. Look at the connection string, IIS settings and the performance of your database. If for some reason connection pooling is not possible, then the running time of the query becomes the problem.
I'm new to .Net MVC and I wanted to run a raw query on my UserInRoles table.
I think I will need a database context to run it.
I'm unsure what context to use. Can some one recommend me a direction to take? Currently, the ObjectContext does not allow me to instantiate without a connection string. Is directly grabbing the connection string from web config correct?
Error 1 'System.Data.Entity.Core.Objects.ObjectContext' does not contain a constructor that takes 0 arguments
using (var ctx = new ObjectContext())
{
string query = "INSERT INTO dbo.webpages_UsersInRoles (RoleId,UserId) values ("+chk+","+id+");";
ExecuteSql(ctx,query);
}
ExecuteSql is using ADO.net connections different from what EF recommends but I need to do this manual insert in order for this section to work.
static void ExecuteSql(ObjectContext c, string sql)
{
var entityConnection = (System.Data.EntityClient.EntityConnection)c.Connection;
DbConnection conn = entityConnection.StoreConnection;
ConnectionState initialState = conn.State;
try
{
if (initialState != ConnectionState.Open)
conn.Open(); // open connection if not already open
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
}
finally
{
if (initialState != ConnectionState.Open)
conn.Close(); // only close connection if not initially open
}
}
The ObjectContext (MSDN Link) requires you to provide a connection string to execute against. The ObjectContext is not your EF container but a context to query against a database and transform to objects. Because it is a generic query mechanism it does not know which connection string to use. In addition, your query is a simple insert query making the ObjectContext the wrong tool for the job. As the ObjectContext provides mapping mechanisms that you will not require I would suggest using your EF container to execute the query. This can be done by calling (sample code)
dbContainer.Database.ExecuteSqlCommand(query)
This command will grab your existing connection string from your configuration file and execute the query provided. Finally there is another option if you really want a ObjectContext without having to enter the connection string. You can do this by providing a wrapper, or static method to create an ObjectContext without the connection string. Such as.
Method 1: Wrapper (inheritance)
class MyObjectContext : ObjectContext
{
public MyObjectContext()
: base(MyObjectContext.connectionString)
{ }
/// <summary>
/// the connection string id in the config
/// </summary>
const string connectionStringID = "dbCon";
/// <summary>
/// gets the connection string
/// </summary>
static string connectionString
{
get
{
return ConfigurationManager.ConnectionStrings[connectionStringID].ConnectionString
}
}
}
Method 2. Static Property (or method)
static ObjectContext New
{
get
{
return new ObjectContext(ConfigurationManager.ConnectionStrings["dbCon"].ConnectionString);
}
}
Now personally I prefer method 1 (if i had to do this) as it also gives me the ability to extend this class to define my queries in one class such as.
class MyObjectContext : ObjectContext
{
//.....
public void Insert_UserInRole(string roleID, string id)
{
///TODO DO: insert role
}
//.....
}
And can be called such as.
using (var context = new MyObjectContext())
{
context.Insert_UserInRole("abc", "123");
}
[Just a thought]
Important In addition your query is begging for SQL Injection. Use "parameters" in your queries. Using parameters will prevent SQL inject atacks. Here is a SO topic regarding parameters. How to pass parameters to the DbContext.Database.ExecuteSqlCommand method?
I hope this helps.
I have a simple WinForms data entry app that uses SQLite. It will always be a single-user app and always with a local database. I have multiple tabs, with UserControls serving as content for the tabs. Each time a tab is selected, an appropriate UserControl is initialized, and the old one is removed (using TabPage.Controls.Remove).
Each UserControl initializes a generic DataAccess object, which wraps all the database stuff and can be reused with any tab content. The issue is that I have an open SQLiteConnection for the duration of the life of the tab (UserControl). I've read elsewhere that it's not a good practice. I don't want to overkill on the design with elaborate data layers and business object layers, partly because I don't know how to do it, and partly because I don't think it's necessary for this app.
I'm basically keeping the same connection, adapter, DataTable, SqlCommand, etc objects in memory and just reusing them with different sql query parameters, and to get that cached data with other methods (like RowCount). I had a problem with LoadData method as it was not clearing out previous query results from DataTable, so I'm doing it manually in the beginning.
I tried figuring out a way to use "using" with SQLiteConnection and other objects, but then I'd have to redo the whole DataLoad stuff or similar for simple things like RowCount. So I'm just looking for suggestions and comments on this approach with data access.
Below is my DataAccess class.
public class DataAccess
{
private SQLiteConnection connection = new SQLiteConnection(Global.DbConnectionString);
private DataTable dataTable = new DataTable();
private SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter();
private SQLiteCommandBuilder commandBuilder = new SQLiteCommandBuilder();
private SQLiteCommand command = new SQLiteCommand();
private BindingSource bindingSource = new BindingSource();
public DataAccess()
{
dataAdapter.SelectCommand = command;
commandBuilder.DataAdapter = dataAdapter;
bindingSource.DataSource = dataTable;
}
~DataAccess()
{
connection.Dispose();
}
public BindingSource BindingSource
{
get { return bindingSource; }
}
///*
public void LoadData(string sql, Dictionary<string, string> parameters)
{
try
{
dataTable.Clear();
command.Connection = connection;
// Ignore sql parameter if we already have CommandText. This assumes sql never changes per instance
if (command.CommandText == null)
command.CommandText = sql;
foreach (KeyValuePair<string, string> parameter in parameters)
{
if (command.Parameters.Contains(parameter.Key))
command.Parameters[parameter.Key].Value = parameter.Value;
else
{
command.Parameters.Add(new SQLiteParameter(parameter.Key));
command.Parameters[parameter.Key].Value = parameter.Value;
}
}
dataAdapter.Fill(dataTable);
}
catch (SqlException)
{
MessageBox.Show("Data Problem, need to display what's wrong later");
}
}//*/
public int RowCount()
{
return dataTable.Rows.Count;
}
public string GetFieldValue(int row_index, string column_name)
{
return dataTable.Rows[row_index][column_name].ToString();
}
public void Save()
{
dataAdapter.Update(dataTable);
}
public void NewRow(Dictionary<string, string> fields)
{
DataRow dataRow = dataTable.NewRow();
foreach (KeyValuePair<string, string> field in fields)
dataRow[field.Key] = field.Value;
dataTable.Rows.Add(dataRow);
}
}
If you want to do it nicely, you should create a data access layer that would expose methods to fetch the data and modify it. This layer would open a connection whenever it's necessary and then close it. You could add a caching layer on top of it. And your GUI would only use the data objects from the lower layers.
It's not a small rewrite, so if your current solution works, and you don't want to put much effort into it, then just leave it like this, it's not that bad. If it's a simple program, then this simple solution is just fine.
I am building an application with c# and I decided to use the Enterprise Library for the DAL (SQL Server).
I don't remember where, but I had read an article about EntLib which said that the connections are closed automatically.
Is it true?
If not, what is the best approach of managing the connections in the middle layer?
Open and close in each method?
The above is a sample method of how I am using the EntLib
public DataSet ReturnSomething
{
var sqlStr = "select something";
DbCommand cmd = db.GetSqlStringCommand(sqlStr);
db.AddInParameter(cmd, "#param1", SqlDbType.BigInt, hotelID);
db.AddInParameter(cmd, "#param2", SqlDbType.NVarChar, date);
return db.ExecuteDataSet(cmd);
}
Thanks in advance.
the ExecuteDataSet method returns a DataSet object that contains all the data. This gives you your own local copy. The call to ExecuteDataSet opens a connection, populates a DataSet, and closes the connection before returning the result
for more info:
http://msdn.microsoft.com/en-us/library/ff648933.aspx
I think you should have something like a static class used as a Façade which would provide the correct connection for your library subsystems.
public static class SystemFacade {
// Used as a subsystem to which the connections are provided.
private static readonly SystemFactory _systemFactory = new SystemFactory();
public static IList<Customer> GetCustomers() {
using (var connection = OpenConnection(nameOfEntLibNamedConnection))
return _systemFactory.GetCustomers(connection);
}
public static DbConnection OpenConnection(string connectionName) {
var connection =
// Read EntLib config and create a new connection here, and assure
// it is opened before you return it.
if (connection.State == ConnectionState.Closed)
connection.Open();
return connection;
}
}
internal class SystemFactory {
internal IList<Customer> GetCustomers(DbConnection connection) {
// Place code to get customers here.
}
}
And using this code:
public class MyPageClass {
private void DisplayCustomers() {
GridView.DataSource = SystemFacade.GetCustomers();
}
}
In this code sample, you have a static class that provides the functionalities and features of a class library. The Façade class is used to provide the user with all possible action, but you don't want to get a headache with what connection to use, etc. All you want is the list of customers out of the underlying datastore. Then, a call to GetCustomers will do it.
The Façade is an "intelligent" class that knows where to get the information from, so creates the connection accordingly and order the customers from the subsystem factory. The factory does what it is asked for, take the available connection and retrieve the customers without asking any further questions.
Does this help?
Yes, EntLib closes connections for you (actually it releases them back into the connection pool). That is the main reason why we originally started to use EntLib.
However, for all new development we have now gone on to use Entity Framework, we find that much more productive.