I'm trying to buid a generic SQL executor.
It's working if my DTO class attributes have the same name than my SQL table column.
To increase the generic part of my code, I add an attribute to my DTO in order to separate my DTO attribute to SQL column.
But it's not working
my DTO class :
public class CarModels
{
[DbColumn("ca_id")] //column name
public string Id { get; set; }
[DbColumn("ca_label")] //column name
public string Label { get; set; }
}
my generic method :
public List<T> ExecuteSQLSELECTCommand<T>(string SQLcommand) where T : new()
{
IDictionary<string, string> databaseMappings = new Dictionary<string, string>(); ;
Get_Connection();
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.Connection = connection;
cmd.CommandText = string.Format(SQLcommand);
List<T> res = new List<T>();
try
{
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
T t = new T();
for (int inc = 0; inc < reader.FieldCount; inc++)
{
Type type = t.GetType();
//how to get attribute link to current FiedCount ?
PropertyInfo prop = type.GetProperty(reader.GetName(inc));
prop.SetValue(t, reader.GetValue(inc), null);
}
res.Add(t);
}
reader.Close();
}
catch (Exception e)
{
}
return res;
}
}
and my call :
List<CarModels> carTest = db.ExecuteSQLCommand<CarModels>("SELECT ca_id, ca_label from cartracker.ca_cars");
my question, how can I recover value of the attribute in order to build a PropertyInfo in a MySqlDataReader context?
Thanks.
You can use a class like this. Can you please tell me if it helps:
public class AnEntity<T> where T : class, new()
{
public T AnEntity(string[] token, ref int i, out TokenEnum tokenEnum,
out string tokenException)
{
...
PropertyInfo[] properties = typeof(T).GetProperties();
try
{
foreach (PropertyInfo property in properties)
{
...
if (property.Name == "AName")
{
break;
}
...
Related
I'm currently creating my own ORM for my current employer, because they have restriction when it comes to packages on nuget, installing them on my projects requires approval, and it take weeks or month before they approve it (from the next level to the highest level), that's why I'm creating my own ORM, but not all tables on our database have an Id or primary key with different name (legacy applications), that's why I'm planning to make it flexible.
Let's say I have a model with a primary key
public class Sample
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string FirstName { get; set; }
[Required]
[StringLength(50)]
public string LastName { get; set; }
[Required]
public int Age { get; set; }
[StringLength(255)]
public string Hobby { get; set; }
}
then I create an insert method which adds the data to the database table, now this works if the primary key's name is only "Id", but on the legacy databases that has a different name, like below
[Key]
public int EntryNo { get; set; }
or this
[Key]
public string TransNo { get; set; }
I want the code on my insert to determine if the property is a primary key or not, what my code is doing now is this.
public T InsertData<T>(T model)
{
using (SqlConnection SqlConn = new SqlConnection(_connectionString))
{
T GetResult;
Type t = typeof(T);
Type ObjType = model.GetType();
string SqlParameter = "";
string SqlValue = "";
PropertyInfo[] prop = ObjType.GetProperties();
for(int i = 0; i < prop.Length; i++)
{
if (prop[i].Name != "Id") //I want this to be replace to check if the property is a primary key
{
SqlParameter = (SqlParameter == "") ? $"[{prop[i].Name}]" : $"{SqlParameter},[{prop[i].Name}]";
SqlValue = (SqlValue == "") ? $"'{prop[i].GetValue(model).ToString()}'" : $"{SqlValue},'{prop[i].GetValue(model).ToString()}'";
}
}
string SqlString = $"insert into [{ObjType.Name}]({SqlParameter})values({SqlValue})";
SqlCommand SqlCmd = new SqlCommand(SqlString, SqlConn);
SqlConn.Open();
SqlCmd.ExecuteNonQuery();
SqlConn.Close();
//Get Inserted data
GetResult = GetInsertData(model);
return GetResult;
}
}
now I want to change the if statement to check if the property is a primary key or not, so it does not stick to "Id" only, and it can also work for our legacy applications.
Currently I'm searching on the internet, but I cannot see anything for this.
You could look for the DataAnnotations KeyAttribute.
public T InsertData<T>(T model)
{
using (SqlConnection SqlConn = new SqlConnection(_connectionString))
{
T GetResult;
Type t = typeof(T);
Type ObjType = model.GetType();
string SqlParameter = "";
string SqlValue = "";
PropertyInfo[] props = ObjType.GetProperties();
PropertyInfo prop = null;
for (int i = 0; i < props.Length; i++)
{
if (props[i].Name == "Id") //I want this to be replace to check if the property is a primary key
{
prop = props[i];
break;
}
object[] attrs = props[i].GetCustomAttributes(typeof(KeyAttribute), false);
if (attrs.Length > 0)
{
prop = props[i];
break;
}
}
if (prop == null)
{
throw new Exception("pk not found");
}
SqlParameter = (SqlParameter == "") ? $"[{prop.Name}]" : $"{SqlParameter},[{prop.Name}]";
SqlValue = (SqlValue == "") ? $"'{prop.GetValue(model).ToString()}'" : $"{SqlValue},'{prop.GetValue(model).ToString()}'";
string SqlString = $"insert into [{ObjType.Name}]({SqlParameter})values({SqlValue})";
SqlCommand SqlCmd = new SqlCommand(SqlString, SqlConn);
SqlConn.Open();
SqlCmd.ExecuteNonQuery();
SqlConn.Close();
//Get Inserted data
GetResult = GetInsertData(model);
return GetResult;
}
}
Have a look at the below problem's solution, the premise of your issue is the same.
Similar problem
Nic
Thanks for this, I have modified your code and it's now working, thank you so much, here's my updated code below:
public T InsertData<T>(T model)
{
using (SqlConnection SqlConn = new SqlConnection(_connectionString))
{
T GetResult;
Type t = typeof(T);
Type ObjType = model.GetType();
string SqlParameter = "";
string SqlValue = "";
PropertyInfo[] prop = ObjType.GetProperties();
for(int i = 0; i < prop.Length; i++)
{
//Check if the property is a primay key
object[] attrs = prop[i].GetCustomAttributes(typeof(KeyAttribute), false);
if(attrs.Length == 0)
{
SqlParameter = (SqlParameter == "") ? $"[{prop[i].Name}]" : $"{SqlParameter},[{prop[i].Name}]";
SqlValue = (SqlValue == "") ? $"'{prop[i].GetValue(model).ToString()}'" : $"{SqlValue},'{prop[i].GetValue(model).ToString()}'";
}
}
string SqlString = $"insert into [{ObjType.Name}]({SqlParameter})values({SqlValue})";
SqlCommand SqlCmd = new SqlCommand(SqlString, SqlConn);
SqlConn.Open();
SqlCmd.ExecuteNonQuery();
SqlConn.Close();
//Get Inserted data
GetResult = GetInsertData(model);
return GetResult;
}
}
I am trying to make a generic method where I can insert any object into a sqlite3 database.
User class:
public class Users : IClassModel<Users>
{
public int Id { get; set; }
public string UserName { get; set; }
public string UserAddress { get; set; }
public string OtherUserDetails { get; set; }
public decimal AmountOfFine { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
Interface class:
public interface IClassModel<T>
{
public int Id { get; set; }
}
QueryBuilder class:
public class queryBuilder : IDisposable
{
private SqliteConnection _connection;
public queryBuilder(string connectionString)
{
_connection = new SqliteConnection(connectionString);
_connection.Open();
}
public void Dispose()
{
_connection.Close();
}
public void Create<T>(T obj) where T : IClassModel<T>
{
// insert into tableName values()
Type myType = obj.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
ArrayList valueArray = new ArrayList();
ArrayList nameArray = new ArrayList();
var questionString = "";
var nameString = "";
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(obj, null);
object propName = prop.Name;
valueArray.Add(propValue);
nameArray.Add(propName);
questionString += "?, ";
nameString += $"{propName}, " ;
}
var newNameString = nameString.Trim();
var newerNameString = newNameString.TrimEnd(',');
var newQuestionString = questionString.Trim();
var newerQuestionString = newQuestionString.TrimEnd(',');
SqliteCommand insertSQL = new SqliteCommand($"INSERT INTO {typeof(T).Name} ({newerNameString}) VALUES ({newerQuestionString})", _connection);
foreach (var item in valueArray)
{
insertSQL.Parameters.Add(item);
}
insertSQL.ExecuteNonQuery();
//Console.WriteLine("Successfully added the thing.");
}
}
Driver:
using Microsoft.Data.Sqlite;
using QueryBuilder.Models;
using System.Reflection;
using (var query = new queryBuilder(#"Data Source=C:\path\to\database"))
{
// con
var user = new Users();
user.UserName = "username";
user.UserAddress = "some_address";
user.OtherUserDetails = "details";
user.AmountOfFine = 90;
user.Email = "something#email.com";
user.PhoneNumber = "5555555555";
query.Create<Users>(user);
}
I know my code is bit messy, but the idea is to somehow create an object and then be able to insert it into the already made table, no matter what object it is. I keep getting invalid cast exceptions.
I need to be able to iterate through the values and properties and add them to the sqlite insert command but it doesn't seem to be working. Any help is appreciated.
i have my DTOs that look like this
public class SomeDTO
{
public string last_name{ get; set; }
public string account_number { get; set; }
}
my property name has underscores because i have to map it to the property names of the source.It works fine.
but i am looking for some attribute that help me to name my DTOs properly. So that i could have something like.
public class SomeDTO
{
[Something("last_name")]
public string LastName{ get; set; }
[Something("account_number")]
public string AccountNumber { get; set; }
}
"Column" isn't working because i guess it works with EF Only.
This is the code of generic auto mapping. that maps data-reader to DTO.
public T ExecuteQuerySingle<T>(List<SqlParameter> paramList, string commandString)
{
using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConStr"].ConnectionString))
using (var cmd = new SqlCommand(commandString, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
foreach (var param in paramList)
{
cmd.Parameters.Add(param);
}
conn.Open();
var reader = cmd.ExecuteReader();
if (reader.HasRows)
{
T item;
Mapper.CreateMap<IDataReader, T>();
while (reader.Read())
{
item = Mapper.Map<IDataReader, T>(reader);
return item;
}
conn.Close();
return default(T);
}
conn.Close();
return default(T);
}
}
you could implement INamingConvention to convert name to convenient also you could implement logic for reading custom attribute and fill fields manually
How can I get the results from my data reader into a List<String>?
Here is my code so far:
public List<string> Item_Getall()
{
List<string> data = new List<string>();
SqlCommand cmd = new SqlCommand("c_get_all_item",oo.conn);
cmd.CommandType = CommandType.StoredProcedure;
oo.conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
data.Add(rdr["item_name_id_pk"].ToString());
data.Add(rdr["item_name_arabic"].ToString());
data.Add(rdr["item_componant"].ToString());
data.Add(rdr["item_componant_arabic"].ToString());
data.Add(rdr["item_price"].ToString());
data.Add(rdr["item_image"].ToString());
data.Add(rdr["item_category_name_id_fk"].ToString());
}
oo.conn.Close();
return data;
}
You better use your custom type list instead of string and store your custom type object in list
List<YourCustomType> data = new List<YourCustomType>();
Custom type
public class YourCustom
{
public string ItemName {get; set;}
//Similarly all the properties.
}
Reading values from data Reader and adding in Custom Type List
while (rdr.Read())
{
data.Add(new YourCustom()
{
Id = rdr["item_name_id_pk"].ToString(),
Name = rdr["item_name_arabic"].ToString()
//Similarly all other properties could be set
}
}
I think you will want to create a custom class and return a list of this class:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
Reading would go as:
var data = new List<Item>();
while (rdr.Read())
{
data.Add(new Item()
{
Id = int.Parse(rdr["item_name_id_pk"]),
Name = rdr["item_name_arabic"].ToString()
}
}
return data;
Also, look into the using() statement which will make your code more robust in the eye of exceptions during database calls.
Using a DataAdapter and a custom type:
SqlCommand cmd = new SqlCommand("c_get_all_item",oo.conn);
cmd.CommandType = CommandType.StoredProcedure;
oo.conn.Open();
var adapter = new SqlDataAdapter(cmd);
DataTable dt;
adapter.Fill(dt);
oo.Close()
//Convert DataTable to a list of YourType
List<YourType> data = dt.AsEnumerable().Select(s=>
new YourType {
item_name_id_pk = s.Field<FieldDataType>("item_name_id_pk"),
item_name_arabic = s.Field<FieldDataType>("item_name_arabic"),
...
})
.ToList();
Your custom type would be like;
public class YourType
{
//Replace the DataType with correct datatype to match with FieldDataType
public DataType item_name_id_pk {get; set;}
public DataType item_name_arabic {get; set;}
...
}
Maybe a silly question, I can read all the properties from list parameter but not the value in the fields of <T>.
This is the structure
public class TestRecord {
public string StringTest { get; set; }
public int IntegerTest { get; set; }
public DateTime DateTimeTest { get; set; }
}
The generic method
public void TestOfT<T>(List<T> pList) where T:class, new() {
T xt = (T)Activator.CreateInstance(typeof(T));
foreach (var tp in pList[0].GetType().GetProperties()) {
// System.Reflection.PropertyInfo pi = xt.GetType().GetProperty("StringTest");
// object s = pi.GetValue(tp, null) ; -- failed
Debug.WriteLine(tp.Name);
Debug.WriteLine(tp.PropertyType);
Debug.WriteLine(tp.GetType().Name);
}
}
Test code for generic method
public void TestTCode() {
List<TestRecord> rec = new List<TestRecord>();
rec.Add(new TestRecord() {
StringTest = "string",
IntegerTest = 1,
DateTimeTest = DateTime.Now
});
TestOfT<TestRecord>(rec);
}
Thanks for your help.
the problem is you are reading the value from the new instance (which can be written simply as var xt = new T();.
if you want to get the property of the item you need to pull the value from the instance.
void TestOfT<T>(IEnumerable<T> list) where T: class, new()
{
var properties = typeof(T).GetProperties();
foreach (var item in list)
foreach (var property in properties)
{
var name = property.Name;
var value = property.GetValue(item, null);
Debug.WriteLine("{0} is {1}", name, value);
}
}
public void TestOfT<T>(List<T> pList) where T:class, new() {
var xt = Activator.CreateInstance(typeof(T));
foreach (var tp in pList[0].GetType().GetProperties()) {
Debug.WriteLine(tp.Name);
Debug.WriteLine(tp.PropertyType);
Debug.WriteLine(tp.GetType().Name);
Debug.WriteLine(tp.GetValue(pList[0], null));
}
}