Click here to Skip to main content
15,891,607 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am trying to build a class that interacts with the database in my asp.net web application. I need your opinion on how to design it, here's an example of what I have in mind


C#
public class User
{
    public int UserID { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }    
}


public class UserData
{
    public UserData() { }

    public object Insert(User user)
    {
        object obj = null;
        using (SqlConnection conn = new SqlConnection(Data.GetConn()))
        {           
            using (SqlCommand cmd = conn.CreateCommand())
            {
                cmd.CommandText = "INSERT INTO Users (Name,Surname) VALUES (@Name,@Surname) SELECT SCOPE_IDENTITY() as UserID";

                if (conn.State == ConnectionState.Closed)
                    conn.Open();

                cmd.Parameters.AddWithValue("@Name", user.Name);
                cmd.Parameters.AddWithValue("@Surname", user.Surname);                                                                                                                      
                obj = cmd.ExecuteScalar();                        
            }            
        }

        return obj;
    }   
}

//Onclick button
User user = new User();
user.Name = Name.Text;
user.Surname = Surname.Text;

UserData userD = new UserData();
object obj = userD.Insert(user);
Posted

Personally I wouldn't split this.
I would build a user (or person) class that holds the properties and also has a few methods: Load, Save, Delete eg. You can also foresee a static method returning a list of user objects.

I did something similar and to do that I created an abstract class that handled parts of the code and holds some similar properties (like the unique id and system columns) and all objects derived from that abstract class forcing the way of working for each class into a similar design.

That doesn't mean your design is wrong, I have seen it before, but IMHO it only makes sense if your applications tends to grow very, very large and you can put the Business Objects and Business Logic into seperate assemblies.

Hope this helps.
 
Share this answer
 
Comments
BobJanova 23-May-14 8:57am    
I disagree, I'd always separate domain model classes (User in this example) and database interaction code. Model classes should be creatable and usable anywhere (e.g. unit tests) without needing to know anything about their data source.
By the way your format is right.
you have to your button code in a method or inside your button click event.
public submitData()
{
//Onclick button
User user = new User();
user.Name = Name.Text;
user.Surname = Surname.Text;
 
UserData userD = new UserData();
object obj = userD.Insert(user); 
}
protected button_OnClick(object sender, EventArgs e)
{
 submitData();
}

Otherwise it right to say in your format
 
Share this answer
 
Comments
Davidemazzuuu 23-May-14 8:40am    
Thanks. In class UserData i have method GetList and Login. is right?

<pre lang="c#">public List<user> GetList()
{
List<user> lst = new List<user>();

using (SqlConnection conn = new SqlConnection(Data.GetConn()))
{
using (SqlCommand cmd = conn.CreateCommand())
{
if (conn.State == ConnectionState.Closed)
conn.Open();

cmd.CommandText = "select UserID,Name from Users";
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
User item = new User()
{
UserID = Convert.ToInt32(rdr["UserID"]),
Name = rdr["Name"].ToString(),
};

lst.Add(item);
}
}
}
}

return lst;

public Boolean DoLogin(string email, string password)
{
}
}</pre>


<pre lang="c#">//For write a list
UserData userD = new UserData();

List<user> itemsU = new List<user>();


itemsU = userD.GetList();


foreach (User itemU in itemsU)
{
Response.Write(itemU.Name);
}
//For login
Boolean aut=0;
aut = userD.DoLogin(Email.Text, Password.Text);
if (aut)
{
}

</pre>
I would not use return type as object
C#
public static bool Insert(User user, out int userID)
{
    userID =-1;
    using (SqlConnection conn = new SqlConnection(Data.GetConn()))
    {           
        using (SqlCommand cmd = conn.CreateCommand())
        {
            cmd.CommandText = "INSERT INTO Users (Name,Surname) VALUES (@Name,@Surname) SELECT SCOPE_IDENTITY() as UserID";

            if (conn.State == ConnectionState.Closed)
                conn.Open();

            cmd.Parameters.AddWithValue("@Name", user.Name);
            cmd.Parameters.AddWithValue("@Surname", user.Surname);                                                                                                                      
            var obj = cmd.ExecuteScalar();  
            if(obj!=null && int.TryParse(obj.ToString(), out  userID)
            {
                return true;
            }                  
        }            
    }
    return false;

}


C#
int id;
if(!UserData.Insert(user, out id)) // since i have mark the method as static you don't need to create instance to call the method
{
   // error inserting user data, show alert to user 
}else
{
   //insert success , do something with user id 
}
 
Share this answer
 
This is a reasonably good starting point.

Good things you did:


  • Parameterised query! Thank you for not making me get my SQL injection rant out and for knowing what you're doing :)
  • Separation of data model and database access logic. Although V has suggested he wouldn't do it that way, I definitely encourage it.
  • Checking whether the insert succeeds


Things to consider:


  • Can you use a pre-cooked entity mapping framework (e.g. Entity Framework or NHibernate)? Making a good and robust database access layer can take a reasonable amount of time and effort. It is sometimes the correct decision (I've done it on a project) depending on the database structure but not that often, imo.
  • Your return type shouldn't be object, as DamithSL points out. It should be a bool saying whether the insert succeeds. Alternatively, throw an exception if it didn't.
  • You will currently have to write INSERT and UPDATE functions manually for every data type. Consider using reflection (with PropertyInfos cached) to determine the values and column names for each object (see below for a sketch).
  • You aren't associating the retrieved ID with the object, so it will be difficult to UPDATE (save) it later.


My suggestion for the database access method:

public static bool Insert<T>(string tableName, T user) where T : IIdentifiable
{
    using (SqlConnection conn = new SqlConnection(Data.GetConn()))
    {           
        using (SqlCommand cmd = conn.CreateCommand())
        {
            PropertyInfo[] properties = typeof(T).GetProperties(); // Actually, cache this, when you do it for real
            IList<string> propertyNames = properties.Select(p => p.Name).Where(n => n != "ID").ToList();
            cmd.CommandText = "INSERT INTO " + tableName + " (" + string.Join(",", propertyNames) + ") VALUES (@" + string.Join(",@", propertyNames) + ") SELECT SCOPE_IDENTITY() as NewRecordID";
 
            if (conn.State == ConnectionState.Closed)
                conn.Open();
 
            foreach(PropertyInfo property in properties)
              cmd.Parameters.AddWithValue("@" + property.Name, property.GetValue(user, null));

            var obj = cmd.ExecuteScalar();  
            int newId;
            if(obj!=null && int.TryParse(obj.ToString(), out newId)
            {
                user.ID = newId;
                return true;
            }                  
        }            
    }
    return false;


... and modify User:

public interface IIdentifiable {
 int ID { get; set; }
}

public class User: IIdentifiable {
 public int ID { get; set; }
 public string Name { get; set; }
 public string Surname { get; set; } 
}


IIdentifiable should be in your domain model, not your database layer, which is why it's phrased in domain terms (i.e. "I can identify this object" not "This is the database ID of this object").

This approach does slightly pollute your persistable domain objects, but it isn't too bad. You can easily expand it to allow for attributes to be specified on classes and properties to override the default behaviour (e.g. change the column name associated with a property, exclude properties from persistence, link properties to other object types etc) if you need that.
 
Share this answer
 
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900