I need to create an generic data access layer to use in my final assignment in Software Engineering, but my data access layer that I have currently created can automatically generate CRUD(Create, Read, Update and Delete) SQL Statement. I still need to define every table in my database and every time I change my database I need to define the changes in my data access layer.
Please look at the sample of my code and tell me how to change my code to improve my access layer:
class sqlConn
{
//Local
private String strConn = #"Data Source=.\SQLEXPRESS;" +
#"AttachDbFilename='D:\JP Stuff\BELGIUM CAMPUS\3de Jaar\SOFTWARE ENGINEERING\ASSIGNMENT\Premier Service Solutions\Premier Service Solutions\DB\db_PSS_1.0.mdf';" +
#"Integrated Security=True;" +
#"User Instance=True";
private SqlConnection conn;
//Properties
public SqlConnection Conn
{
get { return this.conn = new SqlConnection(this.strConn); }
}
//Constructor
public sqlConn()
{
}
}
class sqlFactory : sqlConn
{
//Constructor
public sqlFactory()
: base()
{
}
//Insert Record into database
public void Create(String[] dbData, List<String> strRow)
{
using (SqlConnection sqlCon = this.Conn)
using (SqlCommand com = new SqlCommand("SELECT * FROM " + dbData[0], sqlCon))
{
SqlDataAdapter da = new SqlDataAdapter(com);
SqlCommandBuilder sqlbuilder = new SqlCommandBuilder(da);
DataSet ds = new DataSet();
da.Fill(ds, dbData[0]);
DataRow dr = ds.Tables[dbData[0]].NewRow();
for (int i = 0; i < dbData.Count() - 2; i++)
{
dr[i + 1] = strRow[i];
}
ds.Tables[dbData[0]].Rows.Add(dr);
da.Update(ds, dbData[0]);
}
}
}
class dbDefinitions : sqlFactory
{
public static Dictionary<String, String[]> columns;
static dbDefinitions()
{
columns = new Dictionary<String,String[]>();
//tblCall Definition
#region call
String[] data = new String[]
{
"tblCall", "call_ID_PK", "call_emp_ID_FK",
"call_Description", "call_Notes", "call_Start_Time",
"call_End_Time", "call_Job_FK"
};
columns.Add("call", data);
#endregion
}
}
This may not answer your question fully, but you can improve this code in several ways.
Composition versus Inheritance
First, understand and apply composition over inheritance. Composition is a "has a" relationship, whereas inheritance is a "is a" relationship.
For example, if a Person class has a property of the Phone class type, it's composition.
public class Person
{
public Phone Phone {get; set;}
}
If a Person class descends from the Phone class it's inheritance.
public class Person : Phone
{
}
In your code, sqlFactory should contain a sqlConn instead of inheriting from it.
Composition gives more flexibility, especially since C# doesn't allow for multiple inheritance. Read here for more: Prefer composition over inheritance?
SQL Injection
You should never build sql statements using string concatenation like this.
"SELECT * FROM " + dbData[0]
This creates a potential security hole that allows for SQL Injection attacks. You should always use parameterized queries to prevent against this.
Read Tip/Trick: Guard Against SQL Injection Attacks to understand SQL Injection attacks and how to prevent them.
Coding Convention
The nearly universally accepted convention for class naming amongst C# developers is to use PascalCase, where the first letter of every word in your class name is capitalized. Your classes would be SqlFactory, SqlConn, and DbDefinition.
This guide gives fairly commonly used conventions: C# Coding Standards document
Your DAO should have backing classes that act as models to the tables. These models should all have a common interface. Your DAO should then have instances of the Model interface with XML configuration pointing to the proper tables for the model. This will prevent you from having to define your tables in your code. Your data access layer is the layer that accesses your data, not the layer that defines your data. Your models should define the data.
Try defining the database schema information in an xml file and read it to create the CRUD operations.
Related
i want to run my void method automatically when my project runs but it doesn't. i tried this but i think this is not good way. what is another way or how i can initialize my void method.
string fAccount;
[Key]
[Size(150)]
public string Account
{
get { GroupedAccount(); return fAccount; }
set { SetPropertyValue<string>(nameof(Account), ref fAccount, value); }
}
private void GroupedAccount()
{
SqlConnection conn = new SqlConnection("Connection");
SqlCommand cmd = new SqlCommand("SELECT * FROM Traders", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
conn.Open();
var groupedData = from b in dt.AsEnumerable()
group b by b.Field<string>("Account Name") into g
select new
{
fAccounts = g.Key,
};
foreach (var r in groupedData)
{
SqlCommand cmd2 = new SqlCommand("INSERT INTO dbo.TradersAccount (Account) VALUES (#Account)", conn);
cmd2.Parameters.AddWithValue("#Account", r.fAccounts);
cmd2.ExecuteNonQuery();
}
conn.Close();
}
In an XAF application, you have several places to put startup code, but in your case it looks like you are trying to initialize some objects in the database based on some existing data. The recommended place for this is the ModuleUpdater in the Updater.cs file. See the documentation for more information:
Create and update the application's database
Supply Initial Data for XPO.
Your Account property is part of an XPO object. XPO is an object relational mapper, that is, it abstracts away the complexities of transferring your C# classes to SQL storage. In general you should refrain from putting any additional code in the getters and setters of persisted properties. The DevExpress documentation is good:
Ways to add a business class
Comprehensive XAF Tutorial.
XPO best practices
In particular, XPO is intended to dispense with the need for direct SQL. If you need TradersAccount objects to be created or updated whenever an Trader object is modified, then you would normally create a TraderAccount XPO object and define an association property and create it with new TraderAccount(Session) and add it to the association property collection. Then XPO will automatically generate all of the corresponding SQL for you.
I have been able to interface MySQL with my C# WinForms application with the help of wonderful guides all over the Internet, but I am confused as to which "method" is the standard way of doing so, if ever it does exist. To elaborate, I'll first describe the application that I am developing.
C# Application Overview
My application accepts input from the user, uses DataGridView as the main control for CRUD, and generates Excel reports. Basically, it does the below:
Accept input and insert said data into the database
Retrieve records and display them via DataGridView
Update records using the CellValueChanged event
Delete records
Generate reports using Crystal Reports
Using Objects
What I currently do is to store and retrieve data as objects and use those for all of the above operations. For example:
public class Cat
{
private int id;
public int Id
{
get { return id; }
set { id = value; }
}
private string breed;
public string Breed
{
get { return breed; }
set { breed = value; }
}
public Cat(int id, string breed)
{
this.id = id;
this.breed = breed;
}
}
For me to retrieve data, I do:
public void FillCats()
{
cats = new List<Cat>();
conn.Open();
string query = "SELECT * from cat;";
MySqlCommand cmd = new MySqlCommand(query, conn);
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Cat cat = new Cat(
Convert.ToInt32(reader[0]),
reader[1].ToString(),
);
cats.Add(cat);
}
reader.Close();
conn.Close();
}
Likewise, my create, update, and delete operations are simply variants of the below create function:
public void Insert(DatabaseDriver db)
{
string insert = #"
INSERT INTO cat(id, breed)
VALUES(?id, ?breed);";
db.open();
db.setQuery(insert);
db.setParameter("id", this.id);
db.setParameter("breed", this.breed);
db.executeNonQuery();
db.close();
}
Am I doing this right?
It does work, of course, but I was wondering whether there is a less time-consuming method, perhaps similar to how creating a DBML file would automatically connect your application to your SQL Server database, and even automagically associate every attribute with its corresponding primary key. Before anyone asks, unfortunately, it is not an option for me to use SQL Server.
Check out this answer for a much less-involved way of doing it: Fill Datagridview with MySQL data
As a side note, you can simply call reader.GetInt32(0) and reader.GetString(1) instead of Convert.ToInt32(reader[0]) and reader[1].ToString(). That way you won't get a NullReferenceException when breed is null.
I have a server that hosts 50 databases with identical schemas, and I want to start using Entity Framework in our next version.
I don't need a new connection for each of those databases. The privileges of the one connection can talk to all of the 50 databases, and for data management and speed (this is a WebAPI application) I don't want to instantiate a new EF context every time I talk to each of the databases if I don't have to, unless of course if this occurs each time a request comes to the server then no big deal.
All I really need is the ability to change the USE [databasename] command, which I assume eventually gets sent to the server from EF.
Is there a way to accomplish this in code? Does EF maintain a read/write property in the Context that refers to the database name that could be changed on the fly before calling SaveChanges(), etc.??
Thank you!!!
bob
Don't Work hard, work smart !!!!
MYContext localhostContext = new MYContext();
MYContext LiveContext = new MYContext();
//If your databases in different servers
LiveContext.Database.Connection.ConnectionString = LiveContext.Database.Connection.ConnectionString.Replace("localhost", "Live");
//If your databases have different Names
LiveContext.Database.Connection.ConnectionString = LiveContext.Database.Connection.ConnectionString.Replace("DBName-Localhost", "DBName-Live");
the structure for databases should be the same ;)
You can take a look at:
SO question about passing existing SQL Connection to
EntityFramework Context
and at this article describing how to
change database on existing connection.
Please let me know if any additional help is needed.
Edited
Updated 2nd link to point to SqlConnection.ChangeDatabase method.
So eventually code would look similarly to the following:
MetadataWorkspace workspace = new MetadataWorkspace(
new string[] { "res://*/" },
new Assembly[] { Assembly.GetExecutingAssembly() });
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
using (EntityConnection entityConnection = new EntityConnection(workspace, sqlConnection))
using (NorthwindEntities context = new NorthwindEntities(entityConnection))
{
// do whatever on default database
foreach (var product in context.Products)
{
Console.WriteLine(product.ProductName);
}
// switch database
sqlConnection.ChangeDatabase("Northwind");
Console.WriteLine("Database: {0}", connection.Database);
}
It is very simple
I had
public WMSEntities() : base("name=WMSEntities") //WMSEntities is conection string name in web.config also the name of EntityFramework
{
}
already in autogenerated Model.Context.cs of edmx folder.
To connect to multiple database in runtime, I created another constructor that takes connection string as parameter like below in same file Model.Context.cs
public WMSEntities(string connStringName)
: base("name=" + connStringName)
{
}
Now, I added other connection string in Web.Config for example
<add name="WMSEntities31" connectionString="data source=TESTDBSERVER_NAME;initial catalog=TESTDB;userid=TestUser;password=TestUserPW/>
<add name="WMSEntities" connectionString="data source=TESTDBSERVER_NAME12;initial catalog=TESTDB12;userid=TestUser12;password=TestUserPW12/>
Then, when connecting to database I call below method passing connectionString name as parameter
public static List<v_POVendor> GetPOVendorList(string connectionStringName)
{
using (WMSEntities db = new WMSEntities(connectionStringName))
{
vendorList = db.v_POVendor.ToList();
}
}
Here's my solution for just changing the database name. Simply pull the string from the web or app.config file, modify it, and then instantiate:
string yourConnection = ConfigurationManager.ConnectionStrings["MyEntities"].ConnectionString.Replace("MyDatabase", yourDatabaseName);
dcon = new MyEntities(yourConnection);
I have implemented this in my current project in which we have a common security database and different database for every client in the project. So our security database has a table that contain connection string for every other database. We just pass client id and get the connection string of the client database..
For this add two EDMX one for the common database and other for common schema databases. When user login or what might be your scenario to choose database go to common databse and get the connection string and create object of the needed database. Here is Code sample any, if any quer let me know..
You can keep connection string regarding every other database in a table in a a common database shared by all the other database.
EntityInstance_ReviewEntities.GetContext(GetConnectionString(ClientId));
private string GetConnectionString(int TenantId)
{
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();
ISecurityRepository objSecurity = new SecurityRepository();
string tenantConnectionString = objSecurity.GetClientConnectionString(TenantId);
entityBuilder.ProviderConnectionString = tenantConnectionString;
entityBuilder.Provider = "System.Data.SqlClient";
entityBuilder.Metadata = #"res://*/ClientEntity.YourEntity.csdl|res://*/ClientEntity.ADBClientEntity.ssdl|res://*/ClientEntity.YourEntity.msl";
return entityBuilder.ToString();
}
EntityConnection.ChangeDatabase method is not supported, but SqlConnection.ChangeDatabase works fine.
So you have to use SqlConnection in entity framework database's constructor:
using MvcMyDefaultDatabase.Models;
using System.Data.Metadata.Edm;
using System.Data.SqlClient;
using System.Data.EntityClient;
using System.Configuration;
using System.Reflection;
public ActionResult List(string Schema)
{
SqlConnection sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
MetadataWorkspace workspace = new MetadataWorkspace(new string[] { "res://*/" }, new Assembly[] { Assembly.GetExecutingAssembly() });
EntityConnection entityConnection = new EntityConnection(workspace, sqlConnection);
sqlConnection.Open();
sqlConnection.ChangeDatabase(Schema);
Models.MyEntities db = new MyEntities(entityConnection);
List<MyTableRecords> MyTableRecordsList = db.MyTableRecords.ToList();
return View(MyTableRecordsList);
}
With this code you can read the tables with the same format (same table name and same fields) of several schema passing the database name in the "Schema" string.
For SQL Server, if you want to change only the database, not a connection, try:
public class XXXXDbContext : DbContext
{
public string databaseName
{
set
{
Database.GetDbConnection().Open();
Database.GetDbConnection().ChangeDatabase(value);
}
}
}
I am working on a project using 3 tier architecture.
I want to knw about how to pass datareader value from DAL to Presentation layer
My code is like this.
In DAL layer
public class HomeDAL
{
public SqlDataReader DefaultSearchFriends(long userid)
{
SqlConnection SocialConn = new SqlConnection(connstr);
using (SqlCommand comm = new SqlCommand("proc_FriendsSearch", SocialConn))
{
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.AddWithValue("#userid", userid);
SocialConn.Open();
SqlDataReader dr = comm.ExecuteReader(CommandBehavior.CloseConnection);
return dr;
}
}
}
In BAL layer
public class HomeBAL
{
public SqlDataReader DefaultSearchFriends(long userid)
{
HomeDAL HDAL = new HomeDAL();
SqlDataReader dr = HDAL.DefaultSearchFriends(userid);
return dr;
}
}
On Presentaion Layer I wrote this on Page load
HomeBAL HBAL = new HomeBAL();
SqlDataReader dr = HBAL.DefaultSearchFriends(user_id);
while (dr.Read())
{
//some code
}
Now i want to know two things
1- Is this right to call datareader in this way or there is some better logic.
2- how to close datareader object in BAL layer and in DAL layer.
Well, the basic idea of a layered architecture is to decouple the different components for several reasons.
Some reasons are testability, maintainability, extensibility but there are many more.
To pass the data between these layers - well it depends a bit on the kind of data - but usually you would use some simple classes as data transfer objects (DTO), which would be filled with data in the DAL. E.g.
public class Person
{
public string Name {get; set;}
public string FirstName {get; set;}
...
}
With your approach you are breaking this idea, because you're passing the DataReader to presentation layer which implies, that you cannot switch the DAL technology without touching the other layers. E.g. if you want to use Entity Framework you would have to modify every part in the code, where you're currently using the SqlDataReader.
You can also see, if you'd stick to the idea behind the layered approach, you don't have to think about your second question.
I hope this helps a little bit.
EDIT
Ok I'm a bit curious, that you don't find a proper solution. But anyways, the first and simplest approach could be, don't publish the SqlDataReader. Handle its life cycle in the DAL. Means, assuming that you're using my DTO above
public class HomeDAL
{
public List<Person> DefaultSearchFriends(long userid)
{
SqlConnection SocialConn = new SqlConnection(connstr);
using (SqlCommand comm = new SqlCommand("proc_FriendsSearch", SocialConn))
{
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.AddWithValue("#userid", userid);
SocialConn.Open();
SqlDataReader dr = comm.ExecuteReader(CommandBehavior.CloseConnection);
var persons = new List<Person>();
while (dr.Read())
persons.Add(new Person { Name = dr["Name"], FirstName = dr["FirstName"] });
dr.Close();
return persons;
}
}
}
would be the much better approach.
I think your architecture has problems;
you are using concrete classes to query database; instead you need an abstraction in case you change the backend SQL server or the querying mechanism.
you are passing concrete SqlDataReader into all of your layers. From DAL, you need to return data objects not the db operation context instance.
you need to change only domain objects between layers, not the object doing the actual work.
I suggest you to refer to n layer reference application of microsoft spain
IN DAL File
public class HomeDAL
{
public void DefaultSearchFriends(ref HomeBAL hBAL)
{
SqlConnection SocialConn = new SqlConnection(connstr);
using (SqlCommand comm = new SqlCommand("proc_FriendsSearch", SocialConn))
{
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.AddWithValue("#userid", hBAL.userid);
SocialConn.Open();
hBAL.Search_Reader = comm.ExecuteReader(CommandBehavior.CloseConnection);
}
}
}
In BAL File
public class HomeBAL
{
public SqlDataReader Search_Reader = null;
}
and in Presentaion layer
HomeBAL HBAL = new HomeBAL();
HomeDAL HDAL = new HomeDAL();
HDAL.DefaultSearchFriends(ref HBAL);
SqlDataReader dr = HBAL.Search_Reader;
while (dr.Read())
{
}
I am a beginner and writing code to display data in a Gridview using Asp.Net and c#. I want to know if the approach I followed is correct or not. I want suggestions on standards and architectural issues, best practices with my code so that I can modify my code accordingly. I appreciate your great suggestions and code additions.
Connection Code:
public class DemoProjConnectionClass
{
public SqlConnection DemoProjConnection()
{
SqlConnection con = new SqlConnection("Data Source=Localhost;Initial Catalog=master;Integrated Security=True");
return con;
}
}
Domain Code(gets & sets):
public class DemoProjDomainClass
{
public int EmpId { get; set; }
public string EmpName { get; set; }
public int Salary { get; set; }
}
Class Library Code:
public class DemoProjServiceClass
{
public IList<DemoProjDomainClass> getDemoProjList()
{
string sqlDemoProjList;
sqlDemoProjList = "SELECT EmpId,EmpName,Salary from Employee";
DemoProjConnectionClass x = new DemoProjConnectionClass();
SqlConnection con = x.DemoProjConnection();
con.Open();
SqlCommand cmd = new SqlCommand(sqlDemoProjList, con);
cmd.CommandType = CommandType.Text;
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds, "tempTable1");
IList<DemoProjDomainClass> DemoProjList = new List<DemoProjDomainClass>();
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
DemoProjDomainClass _obj = new DemoProjDomainClass();
_obj.EmpId = Convert.ToInt16(ds.Tables[0].Rows[i][0]);_obj.EmpName = ds.Tables[0].Rows[i][1].ToString();_obj.Salary = Convert.ToInt16(ds.Tables[0].Rows[i][2]);DemoProjList.Add(_obj);
}
return DemoProjList;
}
}
UI Code
protected void Page_Load(object sender, EventArgs e)
{
DemoProjServiceClass ob=new DemoProjServiceClass();
GridView1.DataSource = ob.getDemoProjList();
GridView1.DataBind();
}
I suggest you to :
Use MVC instead asp.net classic
Use linq or EntityFramework instead (cmd,conn,adapter,dataset)
Use repeater instead grid for more performance
Take a look at this tutorial on asp.net site
Connection Code:
I will not hardcode the connectionstring like that. I will keep that in a config file (web.config or so..) and read from there so that i can change my connection string any time if needed with a recompilation.
Class Library Code
Your getDemoProjList method dont have any exception handling. I will wrap that code with using statement so that i dont need to worry abour closing my connection
UI
I dont think you should load data in the PageLoad without checking whether it is a postback or not. So will use a isPostBAck property checkk. I would also do a null check before binding it as the data source of the grid.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DemoProjServiceClass ob=new DemoProjServiceClass();
List<DemoProjDomainClass> objList=ob.getDemoProjList();
if(objList!=null)
{
GridView1.DataSource = objList;
GridView1.DataBind();
}
}
}
Not really.
What you need from this standpoint is data access layer.
http://martinfowler.com/eaaCatalog/
Use Data Source Architectural Patterns item.
Regarding your code connection most of time should not be hardcoded but defined in some kind of configuration file.
Let's assume Domain is fine, but most of time you need to implement Domain pattern which is described more in catalog as well if you have some additional logic of your domain models.
Avoid hardcoding of your sql queries due to possible SQL injection, use Disposable pattern ("using" keyword in terms of C# development) where appropriate. For major cases ORMs work pretty fine to accomplish basic functionality and even more, so there are only one two good reasons to use SqlCommand:
when you gain maximum performance or when you learn the basics. There cons as well. You maintainability decreases and amount of code increases.
From my prospective ASP.Net MVC provides you with highly maintainable and configurable level of code. That's why you can really pay some attention to it. But it's up to you if to do so or not.
For your Connection Code move everything inside of the SqlConnection brackets into your webconfig like this:
<connectionStrings>
<add name="abcConnectionString" connectionString="Data Source=Localhost;Initial Catalog=master;Integrated Security=True providerName="System.Data.SqlClient" />
</connectionStrings>
and then reference it in your code like this:
using System.Configuration;
string connStr = ConfigurationManager.ConnectionStrings["abcConnectionString"].ConnectionString;
SqlConnection Con = new SqlConnection(connStr);
Use Enterprise library to handle your data access code.