C# - Check if the property on a model is a Key - c#

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;
}
}

Related

C# generic insert method to insert object into Sqlite3 table?

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.

How to used an attribute in a SqlDataReader context ?

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;
}
...

Is there a way i can reuse this code in C#

Is there a way i can reuse this codes in executing SQL transaction, i want to make it a method so i can put parameters to execute other stored procedures,
can you guys help me to design a good coding structure?
try {
using (SqlConnection con = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("InsertUser2Sp", con) {
CommandType = CommandType.StoredProcedure
}) {
cmd.Parameters.AddWithValue("#UserID", useridStr);
cmd.Parameters.AddWithValue("#Firstname", firstnStr);
cmd.Parameters.AddWithValue("#Middlename", middleNstr);
cmd.Parameters.AddWithValue("#Lastname", lastnStr);
cmd.Parameters.AddWithValue("#UserAge", ageInt);
cmd.Parameters.AddWithValue("#HomeAddress", homeaddStr);
con.Open();
cmd.ExecuteNonQuery();
}
}
} catch (Exception ex) {
MessageBox.Show("Could not connect to database. Check settings. " + ex.Message, "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBox.Show(ex.Message);
}
}
I will share a generic method here . All you need to do is to build an object with the same property names (including cases) same with the parameters in your SP.
protected internal string GetSingleValue_String(String spname, Object entity)
{
Object res = new Object();
String conString = String.Empty;
using (SqlConnection con = new SqlConnection(ConnectionString))
{
con.Open();
SqlCommand cmd = new SqlCommand(spname, con);
cmd.CommandType = CommandType.StoredProcedure;
if (entity != null)
{
SqlCommandBuilder.DeriveParameters(cmd);
PropertyInfo entitymember = default(PropertyInfo);
foreach (SqlParameter _param in cmd.Parameters)
{
if (_param.Direction == ParameterDirection.Input)
{
entitymember = entity.GetType().GetProperty(_param.ParameterName.Replace("#", ""));
var entityValue = entitymember.GetValue(entity, null);
String _paramvalue = entityValue != null ? entityValue.ToString() : null;
_param.Value = (string.IsNullOrEmpty(_paramvalue) || _paramvalue == string.Empty ? null : _paramvalue);
}
}
}
res = cmd.ExecuteScalar();
cmd.Connection.Close();
entity = null;
cmd = null;
if(res==null)
res = "";
else if (String.IsNullOrEmpty(res.ToString()))
res = "";
return res.ToString();
}
}
So in your example, create a new class that have the same definition as your SP parameters.
class NewClass()
{
public string UserID { get; set; }
public string Firstname { get; set; }
public string Middlename { get; set; }
public string Lastname { get; set; }
public string UserAge { get; set; }
public string HomeAddress { get; set; }
}
And will call the method like this.
var newClass = new NewClass
{
UserID = "UserId",
Firstname = "Firstname",
Middlename = "Middlename",
Lastname = "Lastname",
UserAge = "UserAge",
HomeAddress = "HomeAddress"
}
var res = GetSingleValue_String("InsertUser2Sp", newClass);
Don'd mind the return type.

How to insert data to the database using a list (List<Object>) of objects and modals?

Hi all I'm trying to insert some values to the database using mvc in asp.net. I heard that it would be good use objects when inserting data. So How can I achieve this using a List of objects through properties designed in a class.
Have a class called,
public class Customer
{
public string Name { get; set; }
public string Company { get; set; }
public int Telephone { get; set; }
public string Email { get; set; }
public int Id { get; set; }
}
So I have a another class which simply do the sql commands
DBAccess.cs
public List<Customer> AddCustomers(string spName)
{
List<Customer> customers = new List<Customer>();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = spName;
}
I know that using Idata Reader using can get the values in a database like this
IDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Customer cus = new Customer();
cus.Name = reader["cus_name"].ToString();
cus.Id = Convert.ToInt32(reader["cid"]);
customers.Add(cus);
}
How do I insert data back to the database using such a scenario? Help would be appreciated.
At the base you have to make an SQL insert command to insert into DB. So, you could try the following. This method will get the list and table name as a parameter. Inside the loop reflection is used to insert the values in key value pair list.
public static bool Insert(List<YourModel> datas, string table)
{
bool result = false;
List<KeyValuePair<string, string>> values = new List<KeyValuePair<string, string>>();
SqlConnection con = new SqlConnection("your connection string");
con.Open();
try
{
foreach (var data in datas)
{
values.Clear();
foreach (var item in data.GetType().GetProperties())
{
values.Add(new KeyValuePair<string, string>(item.Name, item.GetValue(data).ToString()));
}
string xQry = getInsertCommand(table, values);
SqlCommand cmdi = new SqlCommand(xQry, con);
cmdi.ExecuteNonQuery();
}
result = true;
}
catch(Exception ex)
{ throw ex; }
finally { con.Close(); }
return result;
}
In the following method the key value pair list will be passed to another function to make the insert command.
private static string getInsertCommand(string table, List<KeyValuePair<string, string>> values)
{
string query = null;
query += "INSERT INTO " + table + " ( ";
foreach (var item in values)
{
query += item.Key;
query += ", ";
}
query = query.Remove(query.Length - 2, 2);
query += ") VALUES ( ";
foreach (var item in values)
{
if (item.Key.GetType().Name == "System.Int") // or any other numerics
{
query += item.Value;
}
else
{
query += "'";
query += item.Value;
query += "'";
}
query += ", ";
}
query = query.Remove(query.Length - 2, 2);
query += ")";
return query;
}

Using Dapper with Oracle stored procedures which return cursors

How would one go about using Dapper with Oracle stored procedures which return cursors?
var p = new DynamicParameters();
p.Add("foo", "bar");
p.Add("baz_cursor", dbType: DbType.? , direction: ParameterDirection.Output);
Here, the DbType is System.Data.DbType which does not have a Cursor member. I've tried using DbType.Object but that does not work with both OracleClient and OracleDataAcess.
What would be a possible way to use OracleType or OracleDbType instead?
Thanks for the solution here. I achieved the same thing with a little less code using a simple DynamicParameter decorator:
public class OracleDynamicParameters : SqlMapper.IDynamicParameters
{
private readonly DynamicParameters dynamicParameters = new DynamicParameters();
private readonly List<OracleParameter> oracleParameters = new List<OracleParameter>();
public void Add(string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null)
{
dynamicParameters.Add(name, value, dbType, direction, size);
}
public void Add(string name, OracleDbType oracleDbType, ParameterDirection direction)
{
var oracleParameter = new OracleParameter(name, oracleDbType, direction);
oracleParameters.Add(oracleParameter);
}
public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);
var oracleCommand = command as OracleCommand;
if (oracleCommand != null)
{
oracleCommand.Parameters.AddRange(oracleParameters.ToArray());
}
}
}
You would have to implement:
public interface IDynamicParameters
{
void AddParameters(IDbCommand command, Identity identity);
}
Then in the AddParameters callback you would cast the IDbCommand to an OracleCommand and add the DB specific params.
Add this class to your project
and your code should like below :-
var p = new OracleDynamicParameters();
p.Add("param1", pAuditType);
p.Add("param2", pCommnId);
p.Add("outCursor", dbType: OracleDbType.RefCursor, direction: ParameterDirection.Output);
using (var multi = cnn.QueryMultiple("procedure_name", param: p, commandType: CommandType.StoredProcedure))
{
var data = multi.Read();
return data;
}
Just to elaborate on Sams suggestion here's what I came up with. Note that this code is brittle and is now just for Oracle.
Modified Dapper 1.7
void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
if (templates != null)
{
foreach (var template in templates)
{
var newIdent = identity.ForDynamicParameters(template.GetType());
Action<IDbCommand, object> appender;
lock (paramReaderCache)
{
if (!paramReaderCache.TryGetValue(newIdent, out appender))
{
appender = SqlMapper.CreateParamInfoGenerator(newIdent);
paramReaderCache[newIdent] = appender;
}
}
appender(command, template);
}
}
foreach (var param in parameters.Values)
{
string name = Clean(param.Name);
bool add = !((Oracle.DataAccess.Client.OracleCommand)command).Parameters.Contains(name);
Oracle.DataAccess.Client.OracleParameter p;
if(add)
{
p = ((Oracle.DataAccess.Client.OracleCommand)command).CreateParameter();
p.ParameterName = name;
} else
{
p = ((Oracle.DataAccess.Client.OracleCommand)command).Parameters[name];
}
var val = param.Value;
p.Value = val ?? DBNull.Value;
p.Direction = param.ParameterDirection;
var s = val as string;
if (s != null)
{
if (s.Length <= 4000)
{
p.Size = 4000;
}
}
if (param.Size != null)
{
p.Size = param.Size.Value;
}
if (param.DbType != null)
{
p.DbType = param.DbType.Value;
}
if (add)
{
if (param.DbType != null && param.DbType == DbType.Object)
{
p.OracleDbType = Oracle.DataAccess.Client.OracleDbType.RefCursor;
((Oracle.DataAccess.Client.OracleCommand)command).Parameters.Add(p);
}
else
{
((Oracle.DataAccess.Client.OracleCommand)command).Parameters.Add(p);
}
}
param.AttachedParam = p;
}
}
Test code
class Program
{
static void Main(string[] args)
{
OracleConnection conn = null;
try
{
const string connString = "DATA SOURCE=XE;PERSIST SECURITY INFO=True;USER ID=HR;PASSWORD=Adv41722";
conn = new OracleConnection(connString);
conn.Open();
var p = new DynamicParameters();
p.Add(":dep_id", 60);
p.Add(":employees_c", dbType: DbType.Object, direction: ParameterDirection.Output);
p.Add(":departments_c", dbType: DbType.Object, direction: ParameterDirection.Output);
// This will return an IEnumerable<Employee> // How do I return both result?
var results = conn.Query<Employee>("HR_DATA.GETCURSORS", p, commandType: CommandType.StoredProcedure);
}
catch (Exception exception)
{
Console.WriteLine(exception);
throw;
}
finally
{
if (conn != null && conn.State == ConnectionState.Open)
{
conn.Close();
}
}
Console.WriteLine("Fininhed!");
Console.ReadLine();
}
}
class Employee
{
public int Employee_ID { get; set; }
public string FIRST_NAME { get; set; }
public string LAST_NAME { get; set; }
public string EMAIL { get; set; }
public string PHONE_NUMBER { get; set; }
}

Categories