The point is to present the use of reflection in an SQL framework. The developer can create his/her own entity with following requirements:
  • Constructor of MyEntityFramework class requires a valid connection string!
  • Each class name must mach with the table name in the database!
  • Each class must contain a parameter less constructor!
  • Every property names must must be public with accessible get,set!
  • These properties must have valid DataMapping: [DataMapping(columnName,propertyName)]!
  • For Insert, Delete or Update must mark the primary key property with Pkey attribute!
  • The framework doesn't maintain the auto-increment fields! To avoid any issue don't define them!
  • Must use LINQ for compound selection!
  • Please pay attention to nullable fields when define the properties!
The framework doesn't contain methods for stored procedures and any complicated SQL transactions!
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Data;
  7. using System.Reflection;
  8. using System.Data.SqlClient;
  9. namespace MyEntityFramework
  10. {
  11. //Base class, definiates the basic database methods
  12. public abstract class DBase
  13. {
  14. public SqlConnection con { get; private set; }
  15. public Type ReflectionType { get; set; }
  16. public ICollection<PropertyInfo> Props { get; set; }
  17. public DBase(string connectionstring)
  18. {
  19. con = new SqlConnection();
  20. con.ConnectionString = connectionstring;
  21. try
  22. {
  23. con.Open();
  24. }
  25. catch (Exception ex)
  26. {
  27. throw new Exception(ex.Message);
  28. }
  29. finally
  30. {
  31. if (con != null) con.Close();
  32. }
  33. }
  34. abstract public void Delete(ICollection<object> Objects);
  35. abstract public void Delete(object Object);
  36. abstract public void Insert(ICollection<object> Objects);
  37. abstract public void Insert(object Object);
  38. abstract public void Update(ICollection<object> Objects);
  39. abstract public void Update(object Object);
  40. abstract protected void LoadClassFromSQLReader(object Object, IDataRecord row, object[] dataMappingAttributes);
  41. abstract protected void FillProperties(object Object);
  42. }
  43. //Simple Entity FrameWork for Select, Insert, Delete, Update
  44. public class MyEntityFrameWork : DBase
  45. {
  46. public MyEntityFrameWork(string connectionstring)
  47. : base(connectionstring)
  48. {
  49. }
  50. /// <summary>
  51. /// Delete a record from the database table
  52. /// </summary>
  53. /// <param name="Objects">A class object</param>
  54. public override void Delete(object Object)
  55. {
  56. ICollection<object> collection = new List<object>();
  57. collection.Add(Object);
  58. try
  59. {
  60. Delete(collection);
  61. }
  62. catch
  63. {
  64. throw;
  65. }
  66. }
  67. /// <summary>
  68. /// Delete records from the database table
  69. /// </summary>
  70. /// <param name="Objects">Collection of class objects</param>
  71. public override void Delete(ICollection<object> Objects)
  72. {
  73. foreach (object o in Objects)
  74. {
  75. FillProperties(o);
  76. object property = null;
  77. string colname = "";
  78. foreach (PropertyInfo pi in Props)
  79. {
  80. object[] attributes = pi.GetCustomAttributes(true);
  81. foreach (Attribute attr in attributes)
  82. {
  83. Pkey pkeyattr = attr as Pkey;
  84. if (pkeyattr != null)
  85. {
  86. property = pi.GetValue(o);
  87. colname = pi.Name;
  88. break;
  89. }
  90. }
  91. }
  92. if (colname == "") throw new KeyNotFoundException(String.Format("Couldn't find property with primary key attribute in the class {0}", ReflectionType.Name));
  93. string sqlCommandLine = String.Format("DELETE FROM {0} WHERE({1}={2})", ReflectionType.Name, colname, property);
  94. SqlCommand cmd = new SqlCommand(sqlCommandLine, con);
  95. try
  96. {
  97. if (RunSqlCommand(cmd) == 0) throw new KeyNotFoundException(String.Format("Couldn't find any columns or values with name of {0} and value {1}!", colname, property));
  98. }
  99. catch
  100. {
  101. throw;
  102. }
  103. }
  104. }
  105. /// <summary>
  106. /// Insert a record into the database table
  107. /// </summary>
  108. /// <param name="Objects">A class object</param>
  109. public override void Insert(object Object)
  110. {
  111. ICollection<object> collection = new List<object>();
  112. collection.Add(Object);
  113. try
  114. {
  115. Insert(collection);
  116. }
  117. catch
  118. {
  119. throw;
  120. }
  121. }
  122. /// <summary>
  123. /// Insert records into the database table
  124. /// </summary>
  125. /// <param name="Objects">Collection of class objects</param
  126. public override void Insert(ICollection<object> Objects)
  127. {
  128. foreach (object o in Objects)
  129. {
  130. FillProperties(o);
  131. IList<string> columnList = new List<string>();
  132. IList<object> valueList = new List<object>();
  133. foreach (PropertyInfo pi in Props)
  134. {
  135. object property = pi.GetValue(o);
  136. valueList.Add(property);
  137. columnList.Add(pi.Name);
  138. }
  139. string columns = string.Join(",", columnList);
  140. string values = string.Join(",@", columnList);
  141. string sqlCommandLine = String.Format("INSERT INTO {0} ({1}) VALUES(@{2})", ReflectionType.Name, columns, values);
  142. SqlCommand cmd = new SqlCommand(sqlCommandLine, con);
  143. for (int i = 0; i < columnList.Count; i++)
  144. {
  145. cmd.Parameters.AddWithValue(columnList[i], string.IsNullOrEmpty(valueList[i].ToString())?DBNull.Value:valueList[i]);
  146. }
  147. try
  148. {
  149. RunSqlCommand(cmd);
  150. }
  151. catch
  152. {
  153. throw;
  154. }
  155. }
  156. }
  157. /// <summary>
  158. /// Update a record in the database table
  159. /// </summary>
  160. /// <param name="Objects">A class object</param
  161. public override void Update(object Object)
  162. {
  163. ICollection<object> collection = new List<object>();
  164. collection.Add(Object);
  165. try
  166. {
  167. Update(collection);
  168. }
  169. catch
  170. {
  171. throw;
  172. }
  173. }
  174. /// <summary>
  175. /// Update records in the database table
  176. /// </summary>
  177. /// <param name="Objects">Collection of class objects</param
  178. public override void Update(ICollection<object> Objects)
  179. {
  180. foreach (object o in Objects)
  181. {
  182. FillProperties(o);
  183. PropertyInfo pkeypi = getPrimaryKey(Props);
  184. object pkeyproperty = null;
  185. string pkeycolname = "";
  186. if (pkeypi != null)
  187. {
  188. pkeyproperty = pkeypi.GetValue(o);
  189. pkeycolname = pkeypi.Name;
  190. }
  191. if (pkeycolname == "") throw new KeyNotFoundException(String.Format("Couldn't find property with primary key attribute in the class {0}", ReflectionType.Name));
  192. Dictionary<string, object> updateitems = new Dictionary<string, object>();
  193. foreach (PropertyInfo pi in Props)
  194. {
  195. //Must skip the primary key
  196. if (pi.Name != pkeypi.Name)
  197. {
  198. object property = pi.GetValue(o);
  199. updateitems.Add(pi.Name, property);
  200. }
  201. }
  202. string updatelinepart = string.Join(",", updateitems.Select(x => x.Key + "=@" + x.Key).ToArray());
  203. string sqlCommandLine = String.Format("UPDATE {0} SET {1} WHERE({2}={3})", ReflectionType.Name, updatelinepart, pkeycolname, pkeyproperty);
  204. SqlCommand cmd = new SqlCommand(sqlCommandLine, con);
  205. foreach (KeyValuePair<string, object> item in updateitems)
  206. {
  207. cmd.Parameters.AddWithValue("@" + item.Key, string.IsNullOrEmpty(item.Value.ToString()) ? DBNull.Value : item.Value);
  208. }
  209. try
  210. {
  211. if (RunSqlCommand(cmd) == 0) throw new KeyNotFoundException(String.Format("Couldn't find any columns or values with name of {0} and value {1}!", pkeycolname, pkeyproperty));
  212. }
  213. catch
  214. {
  215. throw;
  216. }
  217. }
  218. }
  219. /// <summary>
  220. /// Returns with a collection of the required objects
  221. /// </summary>
  222. /// <typeparam name="T">Generic type</typeparam>
  223. /// <returns></returns>
  224. public ICollection<T> GetList<T>()
  225. {
  226. object item = null;
  227. try
  228. {
  229. item = Activator.CreateInstance<T>();
  230. FillProperties(item);
  231. }
  232. catch (Exception ex)
  233. {
  234. throw new Exception(ex.Message);
  235. }
  236. string sqlSelect = String.Format("SELECT * FROM {0}", ReflectionType.Name);
  237. SqlCommand cmd = new SqlCommand();
  238. cmd.Connection = con;
  239. cmd.CommandType = CommandType.Text;
  240. cmd.CommandText = sqlSelect;
  241. SqlDataReader dr = null;
  242. try
  243. {
  244. con.Open();
  245. dr = cmd.ExecuteReader();
  246. }
  247. catch (Exception ex)
  248. {
  249. throw new Exception(ex.Message);
  250. }
  251. if (dr.HasRows)
  252. {
  253. ICollection<T> list = new List<T>();
  254. object[] dataMappingAttributes = null;
  255. while (dr.Read())
  256. {
  257. try
  258. {
  259. item = Activator.CreateInstance<T>();
  260. Type ClassType = item.GetType();
  261. if (dataMappingAttributes == null) dataMappingAttributes = ClassType.GetCustomAttributes(typeof(DataMappingAttribute), false);
  262. if (dataMappingAttributes.Count() < 1) throw new KeyNotFoundException(String.Format("Couldn't find DataMapping attribute(s) for the class {0}!", ClassType.Name));
  263. LoadClassFromSQLReader(item, (IDataRecord)dr, dataMappingAttributes);
  264. list.Add((T)item);
  265. }
  266. catch
  267. {
  268. dr.Close();
  269. con.Close();
  270. throw;
  271. }
  272. }
  273. dr.Close();
  274. con.Close();
  275. return list;
  276. }
  277. return null;
  278. }
  279. /// <summary>
  280. /// Fills a class from an IDataRecord of SqlDataReader matching columns with public properties
  281. /// </summary>
  282. /// <param name="Object">class</param>
  283. /// <param name="dr">IDataRecord</param>
  284. protected override void LoadClassFromSQLReader(object Object, IDataRecord row, object[] dataMappingAttributes)
  285. {
  286. Type ClassType = Object.GetType();
  287. for (int colIndex = 0; colIndex < row.FieldCount; colIndex++)
  288. {
  289. string colName = row.GetName(colIndex);
  290. PropertyInfo propertyInfo = null;
  291. foreach (DataMappingAttribute dma in dataMappingAttributes)
  292. {
  293. if (dma.ColumnName == colName)
  294. {
  295. propertyInfo = ClassType.GetProperty(dma.PropertyName);
  296. break;
  297. }
  298. }
  299. if (propertyInfo != null)
  300. {
  301. try
  302. {
  303. propertyInfo.SetValue(Object, string.IsNullOrEmpty(row.GetValue(colIndex).ToString())?null:row.GetValue(colIndex));
  304. }
  305. catch (ArgumentException ex)
  306. {
  307. throw new ArgumentException(String.Format("Type mismatch error at the {0} property! {1}", colName, ex.Message));
  308. }
  309. }
  310. else
  311. {
  312. throw new KeyNotFoundException(String.Format("Couldn't find any property with the name {0}! It can be caused by: Invalid mapping attribute or Invalid property. A property should be public with proper accessors and valid DataMapping attribute!", colName));
  313. }
  314. }
  315. }
  316. /// <summary>
  317. /// Gets the type of the object and it's public properties by reflection
  318. /// </summary>
  319. /// <param name="SomeClass"></param>
  320. protected override void FillProperties(object SomeClass)
  321. {
  322. try
  323. {
  324. ReflectionType = SomeClass.GetType();
  325. Props = ReflectionType.GetProperties(BindingFlags.Public |
  326. BindingFlags.Instance);
  327. }
  328. catch (Exception ex)
  329. {
  330. throw new Exception(ex.Message);
  331. }
  332. }
  333. /// <summary>
  334. /// Runs an SqlCommand on the database
  335. /// </summary>
  336. /// <param name="cmd">SqlCommand</param>
  337. /// <returns>Counts of affected rows</returns>
  338. private int RunSqlCommand(SqlCommand cmd)
  339. {
  340. int rowCount = 0;
  341. try
  342. {
  343. con.Open();
  344. rowCount = cmd.ExecuteNonQuery();
  345. }
  346. catch (Exception ex)
  347. {
  348. throw new Exception(ex.Message);
  349. }
  350. finally
  351. {
  352. if (con.State == ConnectionState.Open) con.Close();
  353. }
  354. return rowCount;
  355. }
  356. /// <summary>
  357. /// Returns with a Pkey PropertyInfo
  358. /// </summary>
  359. /// <param name="pis">Collection of PropertyInfos</param>
  360. /// <returns></returns>
  361. private PropertyInfo getPrimaryKey(ICollection<PropertyInfo> pis)
  362. {
  363. foreach (PropertyInfo pi in pis)
  364. {
  365. object[] attributes = pi.GetCustomAttributes(true);
  366. foreach (Attribute attr in attributes)
  367. {
  368. Pkey pkeyattr = attr as Pkey;
  369. if (pkeyattr != null)
  370. {
  371. return pi;
  372. }
  373. }
  374. }
  375. return null;
  376. }
  377. }
  378. //DataMapping for ColumnName=PropertyName
  379. [System.AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
  380. public class DataMappingAttribute : System.Attribute
  381. {
  382. public string ColumnName { get; set; }
  383. public string PropertyName { get; set; }
  384. public DataMappingAttribute(string columname, string propertyname)
  385. {
  386. ColumnName = columname;
  387. PropertyName = propertyname;
  388. }
  389. }
  390. //DataMapping for primary key property
  391. [System.AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
  392. public class Pkey : System.Attribute
  393. {
  394. }
  395. }