How can i access abstract class in BLL ? i shouldn't see GenAccessor in BLL it must be private class GenAccessor . i should access Save method over _AccessorForSQL. ok?
MY BLL cs:
public class AccessorForSQL: GenoTip.DAL._AccessorForSQL
{
public bool Save(string Name, string SurName, string Adress)
{
ListDictionary ld = new ListDictionary();
ld.Add("#Name", Name);
ld.Add("#SurName", SurName);
ld.Add("#Adress", Adress);
return **base.Save("sp_InsertCustomers", ld, CommandType.StoredProcedure);**
}
}
i can not access base.Save....????????
it is my DAL Layer:
namespace GenoTip.DAL
{
public abstract class _AccessorForSQL
{
public abstract bool Save(string sp, ListDictionary ld, CommandType cmdType);
public abstract bool Update();
public abstract bool Delete();
public abstract DataSet Select();
}
private class GenAccessor : _AccessorForSQL
{
DataSet ds;
DataTable dt;
public override bool Save(string sp, ListDictionary ld, CommandType cmdType)
{
SqlConnection con = null;
SqlCommand cmd = null;
SqlDataReader dr = null;
try
{
con = GetConnection();
cmd = new SqlCommand(sp, con);
con.Open();
cmd.CommandType = cmdType;
foreach (string ky in ld.Keys)
{
cmd.Parameters.AddWithValue(ky, ld[ky]);
}
dr = cmd.ExecuteReader();
ds = new DataSet();
dt = new DataTable();
ds.Tables.Add(dt);
ds.Load(dr, LoadOption.OverwriteChanges, dt);
}
catch (Exception exp)
{
HttpContext.Current.Trace.Warn("Error in GetCustomerByID()", exp.Message, exp);
}
finally
{
if (dr != null) dr.Close();
if (con != null) con.Close();
}
return (ds.Tables[0].Rows.Count > 0) ? true : false;
}
public override bool Update()
{
return true;
}
public override bool Delete()
{
return true;
}
public override DataSet Select()
{
DataSet dst = new DataSet();
return dst;
}
private static SqlConnection GetConnection()
{
string connStr = WebConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(connStr);
return conn;
}
Scott Ambler: Building Object Applications That Work.
No futher comment - the whole DAL is pretty much a nice demonstration of how NOT to do it. A DAL should have NO (!) reference to the BLL at all. None. The DAL code area should not ahve ANY reference to the BLL layer at all.
In your case I strongly suggest some looks at:
BLToolkit
Nhibernate
If you want to access the save method you simply create an instance of your DAL and call the .Save method like so:
using(GenAccessor g = new GenAccessor)
{
g.save(sproc, param2, param3);
}
Your AccessorForSQL class must also be marked as abstract since you don't provide any implementation for the abstract methods in the _AccessorForSQL.
Clarification: the base keyword cannot be used here because it would indicate that you want to call the specific implementation found in the base class, which obviously doesn't exist since the method is abstract. That said, it doesn't mean you cannot call the method, you just have to drop the base keyword. And again, since you don't implement any of the abstract methods from the base class in the subclass you must mark the subclass as abstract as well. The following should work:
public abstract class AccessorForSQL: GenoTip.DAL._AccessorForSQL
{
public bool Save(string Name, string SurName, string Adress)
{
ListDictionary ld = new ListDictionary();
ld.Add("#Name", Name);
ld.Add("#SurName", SurName);
ld.Add("#Adress", Adress);
return Save("sp_InsertCustomers", ld, CommandType.StoredProcedure);
}
}
You should probably consider using a default implementation in your _AccessorForSQL class. Then you dont need the GenAccessor at all.
By the way, you shouldn't really be using anything web related in your DAL. HttpContext and WebConfigurationManager should be factored out so you can use your DAL layer in other contexts such as WinForms.
namespace GenoTip.DAL
{
public interface IAccessorForSQL
{
bool Delete();
bool Save(string sp, ListDictionary ld, CommandType cmdType);
DataSet Select();
bool Update();
}
public class _AccessorForSQL : IAccessorForSQL
{
private DataSet ds;
private DataTable dt;
public virtual bool Save(string sp, ListDictionary ld, CommandType cmdType)
{
SqlConnection con = null;
SqlCommand cmd = null;
SqlDataReader dr = null;
try
{
con = GetConnection();
cmd = new SqlCommand(sp, con);
con.Open();
cmd.CommandType = cmdType;
foreach (string ky in ld.Keys)
{
cmd.Parameters.AddWithValue(ky, ld[ky]);
}
dr = cmd.ExecuteReader();
ds = new DataSet();
dt = new DataTable();
ds.Tables.Add(dt);
ds.Load(dr, LoadOption.OverwriteChanges, dt);
}
catch (Exception exp)
{
HttpContext.Current.Trace.Warn("Error in GetCustomerByID()", exp.Message, exp);
}
finally
{
if (dr != null)
{
dr.Close();
}
if (con != null)
{
con.Close();
}
}
return (ds.Tables[0].Rows.Count > 0) ? true : false;
}
public abstract bool Update();
public abstract bool Delete();
public abstract DataSet Select();
private static SqlConnection GetConnection()
{
string connStr = WebConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
var conn = new SqlConnection("");
return conn;
}
}
}
You cannot call an abstract method because it does not have an implementation!
Perhaps you want a virtual method?
By the way this line:
return (ds.Tables[0].Rows.Count > 0) ? true : false;
is the same as this:
return (ds.Tables[0].Rows.Count > 0);
Related
I'm using the following class to wrap my CRUD operations:
public class DAL
{
private MySqlConnection conn;
private MySqlCommand cmd = new MySqlCommand();
private MySqlDataAdapter adap = new MySqlDataAdapter();
private MySqlDataReader dr;
public MySqlDataReader Dr
{
get { return dr; }
set { dr = value; }
}
public MySqlDataAdapter Adap
{
get { return adap; }
set { adap = value; }
}
public MySqlCommand Cmd
{
get { return cmd; }
set { cmd = value; }
}
public MySqlConnection Conn
{
get { return conn; }
set { conn = value; }
}
public DAL(IConfiguration configuration)
{
this.conn = new MySqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public DAL(string constr)
{
this.conn = new MySqlConnection(constr);
}
}
Some professionals here say that my DAL Class is terrible because of this
To be honest I didn't understand it very well. But no problem.
You say it's not right to wrap Ado.net. So I don't do it anymore.
There is just one problem. What happens if I have to change my db from MySql to MSSQL?
How can I write code the way I can change my DB with changing just one file and not more.
How should I do that?
You should use interfaces.
By example instead of returning "MySqlDbConnection", you could return a "IDbConnection".
In that case the caller will only know it's a database connection. No matter if it's a MySQL or SQL Server connection.
That way the only file that knows about MySql specifically will be your DAL class.
Those interfaces already exist (IDbConnection, IDbCommand, ...) so don't need to create it yourself.
Edit :
In the following example I modified a few things :
Use interfaces (or abstract classes)
Using naming guidelines + readonly keyword
Use full name for naming things (as suggested by #pinkfloydx33 in a comment) it will help readibility. For example the name of the parameter you used for the connection string was "constr" wich, for me, was meaning "constructor" at first (without reading the following line).
What is not fixed:
The usage of the DbConnection (as you mentionned in your post, you should read again the question you linked because if it's a production code it can/will cause so issues).
Possible improvements:
Using an ORM or a library like Dapper (as suggested by #insane_developer in a comment)
public class DAL
{
private readonly IDbConnection _connection;
private DbCommand _command = new MySqlCommand();
private IDbDataAdapter = new MySqlDataAdapter();
private IDataReader _dataReader;
public IDataReader DataReader
{
get { return _dataReader; }
set { _dataReader = value; }
}
public IDbDataAdapter Adapter
{
get { return _adapter; }
set { _adapter = value; }
}
public DbCommand Cmd
{
get { return _command; }
set { _command = value; }
}
public IDbConnection Connection
{
get { return _connection; }
set { _connection = value; }
}
public DAL(IConfiguration configuration)
{
this._connection = new MySqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public DAL(string connectionString)
{
this._connection = new MySqlConnection(connectionString);
}
}
That way only this class knows about the DBMS type you're using.
i am new with MVC Core, i working on news website Project, how i can display last SQL record on my index?
please describe in details, what should i write in controller and razor page ?
thank you
public class mycontroller:BaseController
{
private readonly IRepo repo;
public mycontroller(IRepo _repo)
{
_repo=repo;
}
[httpGet]
public IActionResult<string> GetLastRecord()
{
return _repo.GetLastRecord();
}
}
public class repo
{
private readonly IDBContextFactory dBContextFactory;
public repo(IDBContextFactory _dbContextFactory)
{
_dbContextFactory=dBContextFactory;
}
public string GetLastRecord()
{
return _dbContextFactory.Select("mydb","select top 1 text from mydb order by autoincreamentedkeyusualyid desc")[0];/* this is bad way of using data table I recommend using this https://stackoverflow.com/questions/33515552/converting-datatable-to-listentity-projectdracula */;
}
}
public interface IRepo
{
public string GetLastRecord();
}
public class DBContextFactory
{
private SqlCommand BuildFactory(string dbName)
{
switch(dbName)
{
case 'mydb':
return CreateMyDB();
}
}
private SqlCommand CreateMyDB()
{
string connectionString = "your connection string";
SqlConnection connection =
new SqlConnection(connectionString));
SqlCommand command = new SqlCommand(connection);
return command.Open();
}
//Private SqlCommand GetMyOpenCommand()
public DataTable Select(string dbName,string query)
{
SqlDataAdapter dataAdapter=new SqlDataAdapter();
dataAdapter.SelectCommand=BuildFactory(dbName);
DataSet dataSet=new DataSet();
dataAdapter.Fill(dataSet);
con.Close();
}
}
public inteface IDBContextFactory
{
SqlCommand BuildFactory(string dbName);
SqlCommand CreateMyDB();
DataTable Select(string dbName,string query)
I am a newbie. Sorry!
My Windows Form App has 3 layer. Presentation has Form_Login with textEdit_Name and textEdit_Pass.
My Connection class:
public class _Connection
{
public OleDbConnection GetConn(string _name, string _pass)
{
OleDbConnection _Conn = new OleDbConnection(String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};User ID={1};Password={2};", #"C:\Test\Test.mdb", _name, _pass));
return _Conn;
}
}
My Data Access Layer:
public class getDAL : IDisposable
{
private _Connection getConn = new _Connection();
OleDbConnection _Conn = new OleDbConnection();
public DataTable getDatatable()
{
_Conn = getConn.GetConn();
//Do something
}
}
How can I get _Conn with:
_name = textEdit_Name.Text and _pass = textEdit_Pass.Text
when user login through Form_Login
Perhaps the simplest method would be to set up the ConnectionString details in a static class in your DAL. Something like this:
public static class ConnectionDetails
{
public static string UserName;
public static string Password;
public static string GetAccessConnectionString(string filepath)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};", filepath);
if (string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Password))
sb.Append("User Id=admin;Password=;");
else
sb.AppendFormat("User Id={0};Password={1};", UserName, Password);
return sb.ToString();
}
}
Your business layer needs to set the UserName and Password fields of the ConnectionDetails static class whenever they change in your presentation layer, and your DAL classes that need connection strings should get them by calling ConnectionDetails.GetAccessConnectionString with the appropriate file path. Or add FilePath as one of the static fields in that class so that you can set all three from wherever.
If you're intending to use more than one database file then a dictionary of connection data keyed off the file name would probably be a good idea.
You can use the ConnectionStringBuilder Class
public static class BuildConnection()
{
public static GetConnectionString(var userName, var Password)
{
OdbcConnectionStringBuilder builder = new OdbcConnectionStringBuilder();
builder.Driver = "Microsoft Access Driver (*.mdb)";
builder.Add("Dbq", #"C:\Test\Test.mdb");
builder.Add("User Id", userName);
builder.Add("Password", Password);
return builder.ConnectionString; // Here is your connection String
}
}
Here is example how you can pass LoginForm TextBox .Text to Business Layer and Data Access Layer.
Data Access Layer:
class DALClass : IDisposable
{
private _Connection getConn;
OleDbConnection _Conn;
public DALClass()
{
getConn = new _Connection();
}
public DataTable getDatatable(string sUserName, string sUserPass)
{
//pass on user name and password
_Conn = getConn.GetConn(sUserName, sUserPass);
//Do something
return default(DataTable); //return datatable
}
}
public class _Connection
{
public OleDbConnection GetConn(string _name, string _pass)
{
OleDbConnection _Conn = new OleDbConnection(String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};User ID={1};Password={2};", #"C:\Test\Test.mdb", _name, _pass));
return _Conn;
}
}
Business Layer:
class BLClass
{
DALClass _dal;
public BLClass()
{
_dal = new DALClass();
}
public DataTable GetDataTable(string sUserName, string sUserPass)
{
return _dal.getDatatable(sUserName, sUserPass);
}
}
Login Form:
public partial class LoginForm : Form
{
BLClass _bl;
public LoginForm()
{
InitializeComponent();
//object of business layer
_bl = new BLClass();
}
private void button1_Click(object sender, EventArgs e)
{
//pass text boxes values to Business Layer
DataTable dt = _bl.GetDataTable(textEdit_Name.Text, textEdit_Pass.Text);
}
}
I've got a base class called DAL_Base for a large project that does most of the SQL lifting.
DAL_Base has fields for SELECT statements, a GetRecords() method, and a virtual FillData(IDataRecord).
public class DAL_Base<T> where T : IDisposable, new() {
private string connStr;
public DAL_Base() {
connStr = ConfigurationManager.ConnectionStrings["CompanyDatabaseConnStr"].ConnectionString;
}
internal string SP_GET { get; set; }
internal SqlConnection m_openConn {
get {
var obj = new SqlConnection(connStr);
obj.Open();
return obj;
}
}
internal virtual T FillDataRecord(IDataRecord record) {
return new T();
}
internal TList<T> Get() {
if (String.IsNullOrEmpty(SP_GET)) {
throw new NotSupportedException(string.Format("Get Procedure does not exist for {0}.", typeof(T)));
}
var list = new TList<T>();
using (var cmd = new SqlCommand(SP_GET, m_openConn)) {
cmd.CommandType = cmd.GetCommandTextType();
using (var r = cmd.ExecuteReader()) {
while (r.Read()) {
list.Add(FillDataRecord(r));
}
}
cmd.Connection.Close();
}
return list;
}
}
There is a lot more, but this should suffice for a single example.
TList is just a List<T> class:
internal class TList<T> : List<T> {
public TList() { }
}
When one of my classes inherits from it, I wanted it to be able to override the base class's FillDataRecord(IDataRecord).
For example, EmployeeDB ** inherits **DAL_BASE.
When I call EmployeeDB.GetEmployeeList(), it uses DAL_BASE to pull the records:
public class EmployeeDB : DAL_Base<Employee> {
private static EmployeeDB one;
static EmployeeDB() {
one = new EmployeeDB() {
SP_GET = "getEmployeeList",
};
}
private EmployeeDB() { }
internal override Employee FillDataRecord(IDataRecord record) {
var item = base.FillDataRecord(record);
item.Emp_Login = record.Str("Emp_Login");
item.Emp_Name = record.Str("Emp_Name");
item.Emp_Email = record.Str("Emp_Email");
item.Emp_Phone = record.Str("Emp_Phone");
item.Emp_Role = record.Str("Emp_Role");
return item;
}
public static EmployeeList GetEmployeeList() {
var list = new EmployeeList();
list.AddRange(one.Get());
return list;
}
}
In the code above, when GetEmployeeList() calls the DAL_Base method Get(), only DAL_Base::FillDataRecord(IDataRecord) is called.
I really need EmployeeDB::FillDataRecord(IDataRecord) to be called, but I can't make DAL_Base::FillDataRecord(IDataRecord) abstract.
What is the way around this?
All I know of right now is to create an EventHandler, which is what I just thought of, so I'm going to work towards that.
If anyone knows of a better route, please chime in!
One solution is to pass a delegate for Derived.FillDataRecord to the base class via the constructor.
public class DAL_Base<T> where T : IDisposable, new() {
private string connStr;
public DAL_Base() {
connStr = ConfigurationManager.ConnectionStrings["CompanyDatabaseConnStr"].ConnectionString;
}
private Func<IDataRecord, T> _fillFunc;
public DAL_Base(Func<IDataRecord, T> fillFunc) : this() {
_fillFunc = fillFunc;
}
// ...
internal TList<T> Get() {
if (String.IsNullOrEmpty(SP_GET)) {
throw new NotSupportedException(string.Format("Get Procedure does not exist for {0}.", typeof(T)));
}
var list = new TList<T>();
using (var cmd = new SqlCommand(SP_GET, m_openConn)) {
cmd.CommandType = cmd.GetCommandTextType();
using (var r = cmd.ExecuteReader()) {
while (r.Read()) {
list.Add(_fullFunc(r));
}
and in the derived class:
public class EmployeeDB : DAL_Base<Employee> {
public EmployeeDB() : base(r => FillDataRecord(r)) { }
private Employee FillDataRecord(IDataRecord record) {
var item = base.FillDataRecord(record);
item.Emp_Login = record.Str("Emp_Login");
item.Emp_Name = record.Str("Emp_Name");
item.Emp_Email = record.Str("Emp_Email");
item.Emp_Phone = record.Str("Emp_Phone");
item.Emp_Role = record.Str("Emp_Role");
return item;
}
}
I have been trying to write an sql connection class for a small project,which will use simple sql commands for insert/update/select/delete ops and sometimes with transactions,is there any guidelines i could use? The class could be instantiated at any point with or without transactions...
Tried:
public class DbConnection
{
public static string srConnectionString = "blablab";
public DbConnection()
{
}
public static DataSet db_Select_Query(string strQuery)
{
DataSet dataSet = new DataSet();
try
{
using (SqlConnection connection = new SqlConnection(srConnectionString))
{
connection.Open();
SqlDataAdapter _SqlDataAdapter = new SqlDataAdapter(strQuery, connection);
DA.Fill(dataSet);
}
return dataSet;
}
catch (Exception)
{
//some error handling.
}
}
public static void db_Update_Delete_Query(string strQuery)
{
try
{
using (SqlConnection connection = new SqlConnection(srConnectionString))
{
connection.Open();
SqlCommand command = new SqlCommand(strQuery, connection);
command.ExecuteNonQuery();
}
}
catch (Exception)
{
//some error handling.
}
}
For example how can i add a parameter that opens or closes transaction as long as this class is used?For example i could be able to call db.commit or db.rollback from outside the class.
PS:tried some micro orms(petapoco for ex.) ,is there a way to run them with pure sql and get return type as datasets or datatables only?
Regards.
Edit:
public class dbconnection : IDisposable
{
public static string strConnectionString = #"Data Source=PC137\PC137_SQL2008;Initial Catalog=BARZO;Integrated Security=True";
#region IDisposable Members
public void Dispose()
{
GC.SuppressFinalize(this);
}
#endregion
private SqlTransaction transaction;
private SqlConnection connection;
private SqlCommand command = new SqlCommand();
public void db_OpenConnection(bool WithTransaction)
{
connection = new SqlConnection(strConnectionString);
connection.Open();
if (WithTransaction)
{
transaction = connection.BeginTransaction();
}
}
public void db_CloseConnection()
{
connection.Close();
connection.Dispose();
transaction.Dispose();
command.Dispose();
}
public void db_Commit()
{
transaction.Commit();
}
public void db_RollBack()
{
transaction.Rollback();
}
public DataSet db_Select_Query(string strQuery)
{
var dataSet = new DataSet();
try
{
SqlDataAdapter SqlDataAdapter = new SqlDataAdapter(strQuery, connection);
SqlDataAdapter.Fill(dataSet);
return dataSet;
}
catch (SqlException sqlError)
{
MessageBox.Show(sqlError,MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
}
public bool db_Update_Delete_Query(string strQuery)
{
try
{
command = new SqlCommand(strQuery, connection);
command.Transaction = transaction;
command.ExecuteNonQuery();
}
catch (SqlException sqlError)
{
MessageBox.Show(sqlError,MessageBoxButtons.OK, MessageBoxIcon.Stop);
return false;
}
return true;
}
}
I would make the db_Update_Delete_Query() method return your own DbCommandToken, which wraps an SqlTransaction object, and can be used to cancel the transaction or report the completion of the command.
This would look like this.
public class DbConnection
{
public static DbCommandToken db_Update_Delete_Query(string strQuery)
{
try
{
using (SqlConnection connection = new SqlConnection(strConnectionString))
{
connection.Open();
var transaction = connection.BeginTransaction();
SqlCommand command = new SqlCommand(strQuery, connection);
return new DbCommandToken(transaction, command);
}
}
catch (Exception)
{
//some error handling.
}
}
}
Note that both your command and your transaction are now owned by your DbCommandToken object, which should implement IDisposable.
If your command is a long-running command, which you may want to run asynchronously, and maybe cancel with a later request, you can add success and failure callbacks to your DbCommandToken: something along these lines.
public class DbCommandToken : IDisposable
{
private readonly SqlTransaction _transaction;
private readonly SqlCommand _command;
public DbCommandToken(SqlTransaction transaction, SqlCommand command)
{
_transaction = transaction;
_command = command;
}
public Action Success { get; set; }
public Action Failure { get; set; }
public Task<int> Execute()
{
return Task.Factory.StartNew(() => _command.ExecuteNonQuery())
.ContinueWith(t =>
{
var rowsAffected = t.Result;
if (rowsAffected >= 0)
{
_transaction.Commit();
Success();
}
...Handle other scenarios here...
return t.Result;
});
}
public void Cancel()
{
_transaction.Rollback();
}
public void Dispose()
{
_command.Dispose();
_transaction.Dispose();
}
}