I need a method that takes the information of a selected row (with another method, already written (SQL Count)) and puts it in an object called Gebruiker (user).
I already have a method that puts the information from an object in to the database with parameters, but this doesn't work for the other way.
This is my object class:
namespace BurnThatFat
{
class Gebruikerklasse
{
public string Naam;
public string Achternaam;
public int Leeftijd;
public string Geslacht;
public int Huidiggewicht;
public int Streefgewicht;
public string Gebruikersnaam;
public string Wachtwoord;
public override string ToString()
{
return Naam;
}
}
}
and this is the method that puts the information from the object to the database:
public void SignUp(string commandText, Gebruikerklasse gebruiker)
{
// nieuwe connectie maken
// ontvangt de query vanuit 'buttonclick' en voert hem hier uit
// als ExecuteNonQuery niet kan worden uitgevoerd is er iets fout gegaan. D.m.v een bool moet hij dan een bericht tonen
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand(commandText, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#Naam", gebruiker.Naam);
cmd.Parameters.AddWithValue("#Achternaam", gebruiker.Achternaam);
cmd.Parameters.AddWithValue("#Leeftijd", gebruiker.Leeftijd);
cmd.Parameters.AddWithValue("#Geslacht", gebruiker.Geslacht);
cmd.Parameters.AddWithValue("#Huidiggewicht", gebruiker.Huidiggewicht);
cmd.Parameters.AddWithValue("#Streefgewicht", gebruiker.Streefgewicht);
cmd.Parameters.AddWithValue("#Gebruikersnaam", gebruiker.Gebruikersnaam);
cmd.Parameters.AddWithValue("#Wachtwoord", gebruiker.Wachtwoord);
int a = cmd.ExecuteNonQuery();
if (a == 1)
{
Success = true;
}
else if (a == -1)
{
Success = false;
}
conn.Close();
}
}
So how do I have to do this? I don't know how to google this really. I think I'm using the wrong words while googling, because im getting non related things...
Edit: added screenshots
So I need the information in this table: http://prnt.sc/dsg95v
To be stored in this object: http://prnt.sc/dsghl1
I already have code (above) that returns the information from the object to the table. I do that with parameters.
I really don't know where to start with database to object...
Edit again: something like this:
public void DatabaseTransferObject(string commandText, Gebruikerklasse gebruiker)
{
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand(commandText, conn))
{
conn.Open();
gebruiker.Naam = //code to get Naam (name) from table Gebruiker (user);
gebruiker.Leeftijd = //code to get Leeftijd(age) from table Gebruiker (user);
conn.Close();
}
}
This the SQL code to get all the information from the row with the given #username
"Select * from Gebruiker where Gebruikersnaam = #Gebruikersnaam;
It seems pretty clear this is homework, and it is far from clear what you are trying to do, so there will be lacunae for you to fill in. There is a fair amount that should be done differently.
DB Design
For some kind of fitness/weight tracker, it really seems like that should be at least 2 tables. One for user data and one for the current weights. The current weight alone isnt very interesting unless you also know previous weights to see if the trend is Up or Down, no? Such a table would also reveal the rate of loss/gain ("getting in shape" can result in less loss than expected - or even a gain - if fat is being converted to muscle).
Something like {Id, UserId, Date, Weight} which associates a number of weight values on specific dates with a user would work.
Never store something which can be easily calculated (ie Age). Sooner or later such values will be wrong and/or you build in an excessive amount of maintenance to the system.
Never, ever store Passwords as plain text. Hash them. Always.
Gebruikerklasse (User)
As per your title, your class has no Properties, just public fields/members. The difference matters a great deal when it comes to data binding.
The table seems to store both the login and fitness related data. But the login data plays no role in the weight tracking and vice versa, so you probably should have 2 classes: one using the log in columns and the other using the tables related to weight tracking
The db PK Id is not represented which will be critical to updating.
So here is how my User class would look for the fitness related elements:
public class User
{ // critical
public int Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public int Age
{
get { return DateTime.Now.Date.Year - DateOfBirth.Date.Year; }
}
public string Gender { get; set; }
public int CurrentWeight { get; set; }
public int Targetweight { get; set; }
private static string dbConnStr = "server=127.0.0.1;database=...";
public User()
{ }
...
The log in related data is omitted; another class would deal with that (if I even needed a class for it). Also, the DateOfBirth is used to calculate an age. You can add code to also check the month to use an ANB (Age Nearest) age rather than ALB (Age at Last Birthday) method.
As noted, CurrentWeight should probably be some sort of collection of Date and Weight values.
Next, in OOP you generally want the class to do all the work (or as much as possible) related to managing the data related to it. So rather than a DatabaseTransferObject method to "push" data into the class object, use class methods for it to load the data itself.
There are many ways to do it. This this one uses a static method to create a new User object from row data. This uses MySQL, but the way DBProviders works varies very little from one another:
// create a user object from an ID
public static User Load(int id)
{
string sql = "SELECT * FROM gebruiker WHERE Id = #id";
using (var dbCon = new MySqlConnection(dbConnStr))
using (var cmd = new MySqlCommand(sql, dbCon))
{
dbCon.Open();
cmd.Parameters.Add("#id", MySqlDbType.Int32).Value = id;
using (var rdr = cmd.ExecuteReader())
{
if (rdr.HasRows)
{
User U = new User();
rdr.Read();
U.Id = id;
U.Name = rdr.GetString(rdr.GetOrdinal("FirstName"));
U.LastName = rdr.GetString(rdr.GetOrdinal("LastName"));
U.DateOfBirth = rdr.GetDateTime(rdr.GetOrdinal("BirthDate"));
//...
return U;
}
else { return null; }
}
}
}
SQL Server lacks the Getxxxx(string) overrides to get data by column by name. For others that have that override like MySQL, it is slightly simpler:
U.Name = rdr.GetString("FirstName");
U.LastName = rdr.GetString("LastName");
U.DateOfBirth = rdr.GetDateTime("BirthDate");
Usage:
User U = new User(1);
An instance method could be used instead to set the properties (this.Name = rdr.GetString("FirstName");); as I said there are many ways to do it. A complementary Save() method would almost certainly be an instance method to INSERT/UPDATE the db from the local data.
Assuming you want to learn, not just get a grade, a simpler way to do that is with an ORM. These map db data to class objects for you. Dapper is a micro-ORM which maps DB data to a class using connection extensions:
User u = dbCon.Query<User>(SQL, new { id = 1 });
Dapper will create a new User object from the record with the Id 1. It won't quite work that simply with your design because the column and property names do not match.
Related
In making this appointment calendar, I wanted to use access database to save and retrieve my appointments. However I have more than one property type (Strings, Ints, DateTime) and more than one type of boxes (ComboBox, ListBox, DateTimePicker) to display in the windows forms.
I have managed to write my code for the database with the following code (part of it):
foreach(var appointment in listOfAppointments)
{
OleDbCommand DbCommand = new OleDbCommand(
"INSERT INTO NewAppointmentDatabase " +
"([Start], [Length], [DisplayableDescription], [OccursOnDate], [Location], [IsRecurring], [Frequency], [Occurence]) " +
"VALUES(#Start, #Length, #DisplayableDescription, #OccursOnDate, #Location, #IsRecurring, #Frequency, #Occurence)",
SaveAppntAccess);
DbCommand.Connection = SaveAppntAccess;
DbCommand.Parameters.AddWithValue("#Start", appointment.Start); //is a short time in DateTime
DbCommand.Parameters.AddWithValue("#Length", appointment.Length); //is an int
DbCommand.Parameters.AddWithValue("#DisplayableDescription", appointment.DisplayableDescription); //is a long string
DbCommand.Parameters.AddWithValue("#OccursOnDate", appointment.OccursOnDate(date)); //is a boolean with DateTime as argument
DbCommand.Parameters.AddWithValue("#Location", appointment.Location); //is a string
DbCommand.Parameters.AddWithValue("#IsRecurring", appointment.IsRecurring); //is a boolean with yes/no tickbox
DbCommand.Parameters.AddWithValue("#Frequency", appointment.Frequency); //is a string
DbCommand.Parameters.AddWithValue("#Occurence", appointment.Occurence); //is an int
I have to note that the word date in appointment.OccursOnDate(date) is reddened in visual studio which is kind of weird because the boolean parameter is inherited.
And then comes the tricky part: I want to load my data! But I want to take my values from the database and assign them to each property first, and then take those and display them in the ComboBoxes and TextBoxes and DateTimePickers.
The code goes like this (part of it):
if(LoadAppntAccess.State == ConnectionState.Open)
{
OleDbCommand DbCommand = new OleDbCommand(
"SELECT * FROM NewAppointmentDatabase", LoadAppntAccess);
OleDbDataReader reader = null;
DbCommand.Connection = LoadAppntAccess;
reader = DbCommand.ExecuteReader();
foreach (var appointment in listofAppointments)
{
while (reader.Read())
{
//code to complete
}
}
}
How will I assign the values from each field to each property? I was thinking something like this:
appointment.Start.Add(reader["Start"].ToString());
appointment.Length.Add((reader["Length"].ToString());
appointment.DisplayableDescription(reader["DisplayableDescritpion"].ToString());
But I get errors in all of those - what is the right syntax?
EDIT : I forgot to mention that "start" although it's assigned as DateTime, I used as a ShortTime value because I wanted a ComboBox with time and 30 minute intervals. So it's not exactly a Date. For OccursOnDate it was written as:
public bool OccursOnDate(DateTime date)
{
return date.Date == date;
}
and to retrieve a date I used a DateTimePicker.
2nd edit for more info
My class looks like this:
public class Appointment : IAppointment
{
public DateTime Start { get; set; }
public int Length { get; set; }
public string DisplayableDescription { get; set; }
public bool OccursOnDate(DateTime date)
{
return date.Date == date;
}
//custom members
public int ID { get; }
public string Location { get; set; }
public bool IsRecurring { get; set; }
public string Frequency { get; set; }
public int Occurence { get; set; }
public Appointment()
{
}
but unfortunately it inherits the parameters from IAppointment which has this code.
int ID { get; }
DateTime Start { get; }
int Length { get; }
string DisplayableDescription { get; }
bool OccursOnDate(DateTime date);
//custom members
string Location { get; set; }
bool IsRecurring { get; set; }
string Frequency { get; set; }
int Occurence { get; set; }
The custom members are my addition since I had to put some extra stuff according to the specs.
However I managed to find a syntax based on your answers below.
appointment.Start.((DateTime)reader["Start"]);
appointment.Length.((int)reader["Length"]);
appointment.DisplayableDescription.((string)reader["DisplayableDescritpion"]);
appointment.OccursOnDate((DateTime)reader["OccursOnDate"]);
appointment.Location.((string)reader["Location"]);
appointment.IsRecurring.((bool)reader["IsRecurring"]);
appointment.Frequency.((string)reader["Frequency"]);
appointment.Occurence.((int)reader["Occurence"]);
I still get this message:
Any clues?
From the info you have given i would guess something like this:
appointment.Start = (DateTime)reader["Start"];
appointment.Length = (int)reader["Length"];
appointment.DisplayableDescription = (string)reader["DisplayableDescritpion"];
This is just a simple example, we would need more info to give a better answer. If any of the columns can have a null value you need to handle that as well etc..
access reader's columns like this, using column index (column index 0 is first column from your SELECT clause, 1 is second... etc). As you can see, call reader's right method to get appropriate type of data.
appointment.Start = reader.GetDateTime(0);
appointment.Length = reader.GetInt32(1);
appointment.DisplayableDescription= reader.GetString(2);
you can also get data by specifiying column name.
appointment.Start = reader.GetDateTime(reader.GetOrdinal("Start"));
appointment.Length = reader.GetInt32(reader.GetOrdinal("Length"));
appointment.DisplayableDescription = reader.GetString(reader.GetOrdinal("DisplayableDescritpion"));
I posted in a comment but since it wasn't made clear with no picture I am posting now my previous try that didn't work and explain.
This is the code mentioned both by #Nino and #FsDaniel
If you see the Interface's parameters (in my initial post), they only have the get property which makes Start, Length and DisplaybleDescritption readonly. Thus the error. The rest is ok because they are my custom members and I gave them get and set properties. I do not wish to make any alterations in the Interface, that's why I am asking to find if there can be another solution.
I am working on an assignment for school and trying to implement as much features just for learning sake. Hence I've made a generic mapper that maps databse tables to objects to see what's possible. The Db in this case is local. I know I'm making loads and loads of calls and should go around this very differently but....
Everything works as intended except for when a class has a Collection of another class.
Example:
class Student {
public int Id { get; set; }
public string Name { get; set; }
}
My method for filling a list of all the students in the database.
public List<TModel> MapEntitiesFromDb<TModel>(string tablename, string customquery = "") where TModel : class, new()
{
try
{
sql = ValidateSelectSql(tablename, customquery);
}
catch (AccessViolationException ex) { Console.WriteLine(ex.Message); }
command.CommandText = sql;
command.Connection = conn;
List<TModel> list = new List<TModel>();
try
{
using (conn)
{
Type t = new TModel().GetType();
conn.Open();
using (reader = command.ExecuteReader())
{
if (t.GetProperties().Length != reader.FieldCount)
throw new Exception("There is a mismatch between the amount of properties and the database columns. Please check the input code and try again.");
//Possible check is to store each column and property name in arrays and match them to a new boolean array, if there's 1 false throw an exception.
string columnname;
string propertyname;
//Pairing properties with columns
while (reader.Read())
{
TModel obj = new TModel();
for (int i = 0; i < reader.FieldCount; i++)
{
columnname = reader.GetName(i).ToString().ToLower();
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo propertyinfo in properties)
{
propertyname = propertyinfo.Name.ToLower();
if (propertyname == columnname)
{
propertyinfo.SetValue(obj, reader.GetValue(i));
break;
}
}
}
list.Add(obj);
}
}
}
}
catch (Exception ex) { Console.WriteLine(ex.Message); }
return list;
}
My ValidateSelectSql just returns the sql string that needs to be used in the query.
After calling:
List<Student> = MapEntitiesFromDb<Student>("students");
It will return a list with all the students like intended.
Things go wrong when I add a collection for example:
class Student {
public Student()
{
this.Courses = new List<Course>();
string customsqlquery = ::: this works and is tested! :::
Courses = MapEntitiesFromDb<Course>("", customsqlquery);
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Course> Courses;
}
The courses list returned empty and with some help of the debugger tool I found out at the time of creating the object the Id property is 0 of course. In my query I am filtering on student Id but at the time of executing the method to fill the Courses list in the constructor the Id of student will always be 0 becuase it's set at a later stage and the result will be no courses in the list.
I'm wondering if I should put a check for an ICollection property after the other properties are set and if so execute a method on the object that in return executes the method that's now inside the constructor?
I can't call any methods on TModel, else it would be as simple as finding if TModel has a collection property and call obj.FillCollection(); after the Id property has been assigned in the GetEntitiesFromDb method.
I was also thinking about recursion. Again I'd have to find if obj has a collection property and then call GetEntitiesFromDB but it seems undoable because I also need to find out the type in between <> and I Can't send any customquery from the outside...
Maybe tackle it from a whole other perspective?
I can really use some advice on how to tackle this problem.
The most straightforward way to approach this would be to have the collection property lazy load what it needs. I would additionally recommend that you use IEnumerable<T> instead of ICollection<T> because this represents a read-only view of what's currently in the database, nobody should be modifying it in any way.
public class Student
{
private readonly Lazy<IEnumerable<Course>> courses;
public int Id { get; set; }
public IEnumerable<Course> Courses => this.courses.Value;
public Student()
{
this.courses = new Lazy<IEnumerable<Course>>(LoadCourses);
}
private IEnumerable<Course> LoadCourses()
{
var sql = "custom SQL query that uses this.Id after it's loaded";
return MapEntitiesFromDb(sql);
}
}
I'm only recommending this approach because you mentioned that this is just an academic exercise to help you learn about the tools available to you. In an actual production environment this approach would very quickly become unwieldy and I would recommend using Entity Framework instead (which may be something else that you might want to learn about).
I will try to be as Concise as I can be, please.. bear with me, as it should be very simple ...
the goal:
trying to universalize a specific section of a project, that is dealing with the SQL databse transactions .
a side note
to assist you with your answer, I've pasted the folowing (just for Reference)
a Usage-Code : GetTestOfTablTime() returns a DataTable
class : SQLDBInteraction this is another Class - responsible for the final(SQL transaction) stage
in this code below I am constructing what I Call: "Stored Procedure's Meta Data"
that class is the one that holds all of the SQL Db SPs :
HTSPs (HT is the company's aliases)
this class is holding each SP (requierd) parameters
HTSPs class contains another sub Class, for all SPs Names, it only has const strings For Each SP name
public sealed class HTSPs
{
//so for example this is one of the members of this class - a stored procedure
//its mission: get evnents with specified id OF specified userId in spec' month, year..
public sealed class GetTimesWithCustomerNames
{
//if I DO need Constructor for its parameters how do I properly format the constructor?
public GetTimesWithCustomerNames()
{
Userid.ParameterName = ParNameUserid;
Month.ParameterName = ParNameMonth;
Year.ParameterName = ParNameYear;
EventId.ParameterName = ParNameReasonid;
}
const string ParNameUserId = "#userId",
ParNameMonth = "#month",
ParNameYear = "#year",
ParNameEventId = "#eventId";
public static SqlParameter Userid = new SqlParameter();
public static SqlParameter Month = new SqlParameter();
public static SqlParameter Year = new SqlParameter();
public static SqlParameter EventId = new SqlParameter();
}
}
so the issue is: how do I initialize the constractor ?
what is the Proper way to have your simple customised StoredProcedure "MetaData"
Iv'e currently Completed the implementation of the Method below (apart from that issue...)
USAGE
this is a method that returns DataTable while using the HTSPs class / constuctor
using SPTime = HT_DBSchema.HTSPs.GetTimesWithCustomerNames;
private DataTable GetTestOfTablTime()
{
SQLDBInteraction.DataContainer DC_Time = new SQLDBInteraction.DataContainer();
SQLDBInteraction.SqlParamList parmsTime = new SQLDBInteraction.SqlParamList();
Dictionary<SqlParameter, int> SqlCmdParDict = new Dictionary<SqlParameter, int>();
parmsTime.SqlCmd.CommandType = CommandType.StoredProcedure;
parmsTime.SqlCmd.CommandText = AppDb.MetaSqlSProc.Time.Name;
parmsTime.SP_Name = AppDb.MetaSqlSProc.Time.Name;
parmsTime.TableName = AppDb.MetaSqlTable.Time.Name;
//While folowing implementation Does Work I comented it out to try using the SP Struct
//ParmsTTime.SP_Params.Add(new SqlParameter(SPTime.ParNameMonth, 9));
//ParmsTTime.SP_Params.Add(new SqlParameter(SPTime.ParNameReasonid, 1));
//ParmsTTime.SP_Params.Add(new SqlParameter(SPTime.ParNameYear, 2012));
//ParmsTTime.SP_Params.Add(new SqlParameter(SPTime.ParNameUserid, 3571));
//here's where I'm currently stuck, in section below. trying to assign values for the SqlCommand
parmsTime.SqlCmd.Parameters.AddWithValue(SPTime.ParNameMonth, 9);
parmsTime.SqlCmd.Parameters.AddWithValue(SPTime.ParNameYear, 2012);
parmsTime.SqlCmd.Parameters.AddWithValue(SPTime.ParNameReasonid, 1);
SPTime.Userid.Direction = ParameterDirection.Input;
SPTime.Userid.SqlValue = 3571;
return DC_Time.LocalTbl_V3(ParmsTime);
}
UPDATE
the last lines of the code above is trying to implement the parmeters assignment ,
thus it will no longer be required to use :
SQLDBInteraction.SqlParamList.SP_Params (which is List<SqlParameter>)
and instead i would really like to be able to use
SQLDBInteraction.SqlParamList.SqlCmd.Parameters
that way as it is already used for most of the requierd steps to interact with the Database,
so this is how i will drop some unnecessery usage of extra variables
while in same time i wanted to assign SqlCmd ( parmsTime.SqlCmd.Parameters.Add(......))
with the Struct - SPTime Real SqlParameters
... instead of using the strings that reperesnts their name as it is now
E.g parameter.name - (SPTime.ParNameMonth, someValue)
final stage- sql trasaction
the SQLDBInteraction Class that does the transaction
public class SQLDBInteraction
{
public class SqlParamList
{
public SqlCommand SqlCmd = new SqlCommand();
public List<SqlParameter> SP_Params = new List<SqlParameter>();
public String SP_Name;
public string TableName;
public string SelectCommand;
///public SqlCommandType SelectedCmdType;
}
public class DataContainer
{
public DataTable LocalTbl_V3(SqlParamList Params)
{
SqlConnection sqlConnection;
DataTable Usrs = new DataTable(Params.TableName);
SqlDataAdapter sqlDataAdapter;
using (sqlConnection = new SqlConnection(WebConfigurationManager.ConnectionStrings["HTConn"].ConnectionString))
{
sqlConnection.Open();
using (Params.SqlCmd.Connection = sqlConnection)
{
using (sqlDataAdapter = new SqlDataAdapter(Params.SqlCmd.CommandText, sqlConnection))
{
if (sqlDataAdapter.SelectCommand.Parameters.Count > 0 == false)
{
sqlDataAdapter.SelectCommand = Params.SqlCmd;
sqlDataAdapter.Fill(Usrs);
}
}
}
}
return Usrs;
}
I will really appreciate it if someone will find what am I doing wrong with the part of the stored procedure's Parameters Assigned to the SQL Command
so at last ... this is my own answer to the 'problem'.
as there will always be opportunities to fine-tune ...and polish it even more,
though I did find, a way to implement the solution .
for starters :
Summary
connecting between a 3 stages proccess - the data extraction, as follows:
1'st stage : assigning the name and id's for the table and its stored procedure
within the struct
2'nd stage : constructing the sql parameter collection / List <SqlParameter>
for the final sql command .
the 3'rd stage : by using these structs to format / construct the sql command,
will in return, get a System.Data DataTable as requested, via the sql instructions
in the stored procedure... naturally.
prepering a background - for code ReUse
these first 2 blocks of code are sotred in two separated files
the first file is for holding the sql databse schema
table names , tables (custom) IDs , Columns names , and stored procedures names and parameters.
public sealed class HTDB_Tables
{
// this is the class that will hold all Sql server - tables Names
public const string TblTime = "tblTime";
}
// the class for Stored Procedures Paramaters - (for each stored procedure)
public sealed class HTDB_SPs
{
// for example this is one of the stored procedures
public sealed class GetTimesWithCustomerNames
{
public static List<SqlParameter> SqlParlst(string SlctdUserID, string SlctdMonth, string SlctdYear, string SlctdEventID)
{
SqlParameter Userid = new SqlParameter( UserIdParName, SlctdUserID);
SqlParameter Month = new SqlParameter(MonthParName, SlctdMonth);
SqlParameter Year = new SqlParameter(YearParName, SlctdYear);
SqlParameter EventId = new SqlParameter(EventIdarName, SlctdEventId);
List<SqlParameter> RetSqlParLst = new List<SqlParameter>();
retSqlParLst.Add(UserId);
retSqlParLst.Add(Month);
retSqlParLst.Add(Year);
retSqlParLst.Add(EventId);
return retSqlParLst;
}
const string UserIdParName = "#userId",
MonthParName = "#month",
YearParName = "#year",
EventIParName = "#eventId";
}
}
// a numeric value that's used to reference the table
// the id is passed as a parameter to a javascript-Jquery Update function
// through the textbox control - "textchange event" - attribute,
// as reference to which table to send the updated data .
// then it is proccessed in code behind as table id inside the application
// via a switch on the parameter sent by Jquery-ajax function
public sealed class HTDB_tblIDs
{
public const int tblTime = 1;
//this is only an example , for one of tables
//as it requierd that all sql database tables(that i want to work with this project)
// will be added here too
// this could be done via using code smith or through few simple
// steps you could do it via C# method that will list all your
// database tables names etc'
}
the Second file is a general usage Helper nameapace, that stores all Helper classes,
and one of the classes (next block of code) , is the one that hodls the structs
one for the table and another for the stored procedure,
these two, will be assinged, later in the application's code behind.
public class DBMetaDetails
{
public struct DbTable
{
public DbTable(string tableName, int tableId): this()
{
this.Name = tableName;
this.ID = tableId;
}
public string HtmlOutput;
public string Name { get; private set; }
public int ID { get; private set; }
}
public struct SProc
{
public SProc(string SProcName, int SprocID, List<SqlParameter> CurrSpParList)
: this()
{
this.Name = SProcName;
this.ID = SprocID;
this.SpParList = CurrSpParList;
}
public string Name { get; private set; }
public int ID { get; private set; }
public List<SqlParameter> SpParList { get; private set; }
}
}
so both of the codes above are in two separated files ...
meaning codes above could be used in every application i need to create,
so it'll be very easy to implement after this initial background work done.
second part
implementation in current project.
the next codes are used in the "current project" code behind aspx.cs
the implementation of struct for the System.Data DataTable
and its stored procedure, that will be used / assigned to the SqlCommand
public sealed class AppDb
{
public sealed class SqlTableMeta
{
//id -for usage by jquery, name for the DataTable returnd in the next block of code
public static DbTbl Time = new DbTbl(HTDB_Tables.TblTime, HTtIDs.tblTime);
}
public sealed class SqlSProcMeta
{
public static SProc Time = new SProc(HTSPs.SP_GetTimesWithCustomerNames,
HTtIDs.SProcTime,
HTSPs.GetTimesWithCustomerNames.SqlParLst("3571", "9", "2012", "1"));
}
}
the final two steps that could have been in one , though what i want to see is
that the project would have the smallest footprint of extra codes needed to get the data
so i guess the second part that is doing the actual interaction with database
should be soon after tests are complete moved to the Helpers section and out of the code behind section of every application .
so next are codes that will gather all preperations and data setup into action
private DataTable GetDbTable(DbTbl dbTbl, SProc Sprc)
{
SQLDBInteraction.DataContainer DC_Time = new SQLDBInteraction.DataContainer();
//ParmsTime- a class instace, for passing a set of required parameters to final stage
SQLDBInteraction.SqlParamList ParmsTime = new SQLDBInteraction.SqlParamList();
ParmsTime.SqlCmd.CommandType = CommandType.StoredProcedure;
//using the stored procedure struct: assigend to Sqlcommand as its CommandText
ParmsTime.SqlCmd.CommandText = Sprc.Name;
//using stored procedure parameters list - allso via a struct above codes
ParmsTime.SqlCmd.Parameters.AddRange(Sprc.SpParList.ToArray());
//using DataTable struct to assign the data table a name
ParmsTime.TableName = dbTbl.Name;
return DC_Time.LocalTbl_V3(ParmsTime);
}
the final stage: sql database interaction
public class SQLDBInteraction
{
public class SqlParamList
{
public SqlCommand SqlCmd = new SqlCommand();
public List<SqlParameter> SP_Params = new List<SqlParameter>();
public string TableName;
public string SelectCommand;
///public SqlCommandType SelectedCmdType;
}
public class DataContainer
{
public DataTable LocalTbl_V3(SqlParamList Params)
{
SqlConnection sqlConnection;
DataTable retDt = new DataTable(Params.TableName);
SqlDataAdapter sqlDataAdapter;
using (sqlConnection = new SqlConnection(WebConfigurationManager.ConnectionStrings["useyourwebconfigconnection"].ConnectionString))
{
sqlConnection.Open();
using (Params.SqlCmd.Connection = sqlConnection)
{
using (sqlDataAdapter = new SqlDataAdapter(Params.SqlCmd.CommandText, sqlConnection))
{
if (sqlDataAdapter.SelectCommand.Parameters.Count > 0 == false)
{
sqlDataAdapter.SelectCommand = Params.SqlCmd;
sqlDataAdapter.Fill(retDt);
}
}
}
}
return retDt;
}
}
}
this is only a test, yet it works very fast
and very easy to implemet , it will fit perfectly as it is for personal or small buisness use
comments are very welcome ...
thanks
Sorry If I am posting this question in the wrong forum. This is my first time posting at Stackoverflow. And Also I am learning ASP.NET and C# by myself so pardon me if this elementary.
I have created a class file in ASP.NET 4.0 C# using visual studio 2010. My code is like below
namespace My.Customers
{
public class EmailMailingList
{
#region members
private string _emailList;
#endregion
#region properties
/// <summary>
/// gets or sets Customer Email for Mailing List
/// </summary>
public string EmailList
{
get { return _emailList; }
set { _emailList = value; }
}
#endregion
}
}
I have a Database table in SQL 2008 named MailingList with 2 fields namely Id and Email where Id is int with auto increment and Email Varchar(50).
I also created a Stored Procedure based on this table to enter the email address.
where I am getting confused is How can I add this info to my class file so the data can be saved to the database.
I also created a web form called Join-Mailing-List.aspx with a textbox called tbEmail and a Submit Button called Join.
What I am trying to is when someone enters the email address in the textbox I want to pass the textbox value to string EmailList in my class file and save the data to the database.
I know how to pass the textbox value to my class file. I just dont know how to save the info to the DB without using the DB code in the aspx page
Thanks and really appreciate for any advice or examples
There are a number of different ways to save information to a database using C#. Some of the more common:
ADO.NET
Linq-To-SQL
Entity Framework
You will probably need to read up on those to find the best one for you. If you want something quick and relatively easy, I would probably go with ADO.NET but others may disagree.
As far as how to include the code for the update in your class you could just add an Update() or Save() function.
There are several ways you can approach saving into database
Here is one: (simpler in my opinion)
public class EmailMailingList
{
private string _emailList;
public string EmailList
{
get { return _emailList; }
set { _emailList = value; }
}
#endregion
public void Save()
{
//Insert (this.EmailList); to database
}
}
//Use:
EmailMailingList ml = new EmailMailingList();
ml.EmailList = "blah";
ml.Save();
Some school of thought frown at that.
Another is creating a special class to do that
public class MailingListSaver
{
public static void Save(EmailMailingList ml)
{
//insert (ml.EmailList) into database
}
}
//Use:
EmailMailingList ml = new EmailMailingList();
ml.EmailList = "blah";
MailingListSaver.Save(ml);
Look into (google)
Active Record Pattern
Repository Pattern
N-tier application c#
You can simplify your class a little for clarity:
public class EmailMailingList
{
public string EmailList { get; set; }
}
Then you have MANY options for getting this info to your db. If you're just starting out, I think plain ol' ADO.Net is a get choice for getting accustomed to things. Something like:
// A method in your code-behind:
public void Save(EmailMailingList list)
{
using(var connection = new SqlConnection("your connection string"))
{
var command = connection.CreateCommand();
command.CommandText = "name of your stored procedure";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter( ... ));
command.ExecuteNonQuery();
}
}
Of course, eventually you'll want to look into the powerful ORMs available to you in the .Net world (Entity Framework, NHibernate, LinqToSql)
I would recommend using the entity data model. It is the quickest, easiest way and it is a "best practice." You should get started bu walking through this tutorial:
http://www.asp.net/web-forms/tutorials/getting-started-with-ef/the-entity-framework-and-aspnet-getting-started-part-1
Once you have your datamodel set up using the wizard, then it is really quick and simple to save your data from a form on the code side:
dataModel.dataEntities de;
dataModel.dataTable tbl;
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
//Create new entity object and table object
de = new dataModel.dataEntities();
de.Connection.Open();
tbl = new dataModel.DataEntities();
tbl.Field1 = ((TextBox)tbField1).Text.ToString(); ;
tbl.Field2 = ((TextBox)tbField2).Text.ToString(); ;
//Insert the row and save the change to the table
de.AddToDataTable(tbl);
de.SaveChanges();
de.Connection.Close();
}
I was looking to map my database query results to strongly type objects in my c# code. So i wrote a quick and dirty helper method on the SqlConnection class which runs the query on the database and uses reflection to map the record columns to the object properties. The code is below:
public static T Query<T>(this SqlConnection conn, string query) where T : new()
{
T obj = default(T);
using (SqlCommand command = new SqlCommand(query, conn))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
obj = new T();
PropertyInfo[] propertyInfos;
propertyInfos = typeof(T).GetProperties();
for (int i = 0; i < reader.FieldCount; i++)
{
var name = reader.GetName(i);
foreach (var item in propertyInfos)
{
if (item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase) && item.CanWrite)
{
item.SetValue(obj, reader[i], null);
}
}
}
}
}
}
return obj;
}
public class User
{
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public DateTime signupDate { get; set; }
public int age { get; set; }
public string gender { get; set; }
}
var user = conn.Query<User>("select id,firstname,lastname from users");
I just wanted a second opinion on my approach above of using reflection to tie the values together, if there's anything i can do better in the code above. Or if there's some other totally different approach i can take to get the same result?
I think i can probably improve the code in the helper method by removing the loop for propertyInfos and using a dictionary instead. Is there anything else that needs to be tweaked?
P.S: i'm aware of Dapper, i just wanted to implement something similar on my own to help me learn better.
What you've done is basically what linq-to-sql or other OR-mappers do under the hood. To learn the details of how it works it's always a good idea to write something from scratch.
If you want more inspiration or want to have something that's ready for production use out-of-the-box I'd recommend reading up on linq-to-sql. It is lightweight, yet competent.
There are a few of things I can think of:
I think that in order to skip the loop you can use:
reader[item.Name]
I've done something similar myself, but I never ran into dapper. I'm not sure if it uses reflection, but it's always a good idea to read someone else's code to sharpen your skill (Scott Hanselman frequently recommends doing so).
You can also look at:
http://www.codeproject.com/KB/database/metaquery_part1.aspx
You can implement an attribute that maps a field to a database column, but that's just for fun.
Edit:
5: You can also skip the while loop over the reader and just take the first row, and document the fact that your query only returns one object, so it doesn't pull a thousand rows if the query returns a thousand rows.