A lightweight ORM framework written by myself is released. This framework is designed based on a three-tier architecture at the beginning. Therefore, from the perspective of naming, it is easy to understand the three-tier architecture.
The purpose of designing this framework is to add, delete, modify, and query data without repeated writes, and focus on function implementation.
Reasons for publishing and changing the framework: I hope to give a reference to beginners, give good suggestions, and give myself a presentation opportunity.
Before I start, let me explain that I have almost no idea about the concept of software engineering. The highest level of education is the second day.
Start with my orm design bottom layer
The bottom layer is a DalBase, Which is abstract and implements the basic operations of addition, deletion, modification, and query.
Since it is abstract, it should not have any specific members.
Its core objects include: database access objects, generating abstract definitions of SQL objects, creating SQL parameter abstraction methods, and where parameterized query object abstraction definitions.
/// <Summary >/// DBHelper object for data access /// </summary> public abstract DbHelperBase DBHelper {get ;} // subclass implementation /// <summary> /// obtain the object that generates the SQL text /// </summary> protected internal abstract BuildSQL BuildSQLTextObj {get ;} /// <summary> /// obtain the data parameter object /// </summary> protected internal abstract DbParameter GetDbParam (string paramName, object paramValue ); /// <summary> /// create a WhereHelper object // </summary> internal abstract WhereHelper CreateWhereHelper ();
To expand the supported database types, you only need to implement specific code for these abstract objects.
The following code adds, deletes, and modifies a query (the query is relatively complex and is released separately)
/// <Summary> /// Number of affected rows returned by the SQL statement // </summary> public virtual int ExecuteBySQL (string sqlText, Dictionary <string, object> dbParams) {// The operations for executing SQL statements are implemented by DbParameter [] parameters = GetDbParam (dbParams); int rows = DBHelper. execNonQuery (sqlText, parameters); return rows ;} /// <summary> /// Add one or more items /// </summary> public virtual int Add <TModel> (params TModel [] models) where TModel: modelBase, new () {ThrowModelIsNullException (models); string sqlText; Dictionary <string, object> dbParams;
// In fact, the internal implementation accesses the InsertSQLTExtAndParam of BuildSQLObj. It generates SQL statements and SQL parameter objects and writes them as a method so that the subclass can rewrite them, you may have thought too much about this. You can rewrite Add directly. generateInsertSQLTextAndParam (out sqlText, out dbParams, models); int rows = ExecuteBySQL (sqlText, dbParams); return rows ;} /// <summary> /// update one or more records. Specify the update field based on the SQL condition. (If sqlWhere is empty, update by primary key) /// </summary> public virtual int Update <TModel> (string [] fields, string sqlWhere, Dictionary <stri Ng, object> dbParams, params TModel [] models) where TModel: ModelBase, new () {ThrowModelIsNullException (models); string sqlText; GenerateUpdateSQLTextAndParam (out sqlText, ref dbParams, sqlWhere, fields, models); int rows = ExecuteBySQL (sqlText, dbParams); return rows;} // <summary> // update one or more records based on SQL conditions, ignore the updated field // </summary> public virtual int UpdateByIgnoreField <TModel> (string [] ignoreFie Lds, string sqlWhere, Dictionary <string, object> dbParams, params TModel [] models) where TModel: ModelBase, new () {string [] allFields = BuildSQLTextObj. getAllFields <TModel> (); string [] updateFields = BuildSQLTextObj. removeFields (allFields, ignoreFields); return Update (updateFields, sqlWhere, dbParams, models );} /// <summary> /// Delete a record based on the primary key /// </summary> public virtual int Delete <TModel> (params o Bject [] pkValues) where TModel: ModelBase, new () {string sqlText; Dictionary <string, object> dbParams; if (pkValues = null | pkValues. length <0) {throw new DbDataException ("the primary key is blank during the delete operation! ");} GenerateDeleteSQLTextAndParam <TModel> (out sqlText, out dbParams, pkValues); int rows = ExecuteBySQL (sqlText, dbParams); return rows ;}
The method provided by the query is "3". It returns DataSet, List, DataReader, and paging.
public virtual DataTable GetDataTable<TModel>(string sqlWhere, Dictionary<string, object> dbParams, string[] orderFields, bool isDesc, params string[] selectFields) where TModel : ModelBase, new(){ string sqlText; GenerateSearchSQLTextAndParam<TModel>(sqlWhere, dbParams, orderFields, isDesc, out sqlText, selectFields); return GetDataTableBySQL(sqlText, dbParams);}public virtual DbDataReader GetDataReader<TModel>(string sqlWhere, Dictionary<string, object> dbParams, string[] orderFields, bool isDesc, params string[] selectFields) where TModel : ModelBase, new(){ string sqlText; GenerateSearchSQLTextAndParam<TModel>(sqlWhere, dbParams, orderFields, isDesc, out sqlText, selectFields); return GetDataReaderBySQL(sqlText, dbParams);}public virtual List<TModel> GetList<TModel>(string sqlWhere, Dictionary<string, object> dbParams, string[] orderFields, bool isDesc, params string[] selectFields) where TModel : ModelBase, new(){ DbDataReader dbReader = GetDataReader<TModel>(sqlWhere, dbParams, orderFields, isDesc, selectFields); List<TModel> list = GetListByDataReader<TModel>(dbReader); return list;}
Why is there a DataReader method? It is useful to return it. 1 is to convert it to List, 2 is because the DataReader method is also called internally for obtaining DataSet. (This can be seen through decompilation)
Because DataReader converts a List more efficiently than DataTable to List;
The basic functions of DalBase are almost the same. The following describes the implementation of BLLbase. BLLBase mainly implements some overloading of the dal method,
Literally speaking, the basic class of business logic, here I define the rules of business logic, such as the pre-Insert (post) events, the pre-query events, and so on ..
Addition, deletion, and modification in BLLBase are actually no different from those in DalBase.
Let's talk about the Get method.
public virtual DataTable GetDataTable<TModel>(string[] selectFields, string[] orderFields, bool isDesc, WhereHelper dbWhereModel) where TModel : ModelBase, new() { StringBuilder sqlWhere = null; Dictionary<string, object> dbParam = null; GetSqlWhereParam(ref sqlWhere, ref dbParam, dbWhereModel); return GetDataTable<TModel>(sqlWhere.ToString(), dbParam, orderFields, isDesc, selectFields); }
This is a query with the where condition. The datatable is returned, and the other methods used to obtain the List and DataReader are the same. I have been proud of the creation of the WhereHelper class for a long time, the following example shows how to use and implement the function.
For example:
1. Create a WinForm program and reference ZhCun. Framework. Common and ZhCunFramework. DataAccess
2. Create the Models folder and create Test1.cs and Test2.cs respectively. The two are table ing:
namespace ZhCun.Framework.WinTest.Models{ public class Test1 : ModelBase { [ModelAttribute(IsPrimaryKey = true, IsIdentity = true)] public int Id { set; get; } public string Name { set; get; } public string Age { set; get; } public string Remark { set; get; } }}
The ing Model must inherit ModelBase, Which is why generic constraints are added to DalBase. In fact, I didn't want to use ModelBase to implement anything, so I should be a constraint.
In addition, the ModelAttribute attribute specifies the type and other rules of the ing database table for this attribute. The Id indicates an auto-increment primary key.
3. Create a BLLCommon class named XXXXServer, which may be better. It inherits BLLBase and specifies the connection string.
The two tables can be created manually. The connection string can be directly specified and written to the dead table.
public class BLLCommon : BLLBase { static string ConnStr { get { // "Data Source=192.168.0.55;Initial Catalog=aa;uid=sa;pwd=123"; return Configurations.GetConnectString("Test"); } } public BLLCommon() : base(DatabaseTypeEnum.SQLServer, ConnStr) { }}
BLLCommon specifies the connection string and database type. If a project uses multiple (or multiple) databases, you can create multiple BLLCommon,
All BLLBase Methods define virtual methods, so here you can rewrite the things you want to modify to complete the restrictions or constraints of business logic.
For example, when I want to add data in the Test1 table, the Remark value is 'A'
public override int Add<TModel>(params TModel[] models) { foreach (var item in models) { Test1 m = item as Test1; if (m != null) { m.Remark = "aa"; } } return base.Add<TModel>(models); }
The following is the call code:
This code implements, adds, updates, and how to use transactions.
BLLCommon _ BllObj = new BLLCommon (); private void btnAdd_Click (object sender, EventArgs e) {Test1 t1 = new Test1 (); Test2 t2 = new Test2 (); t1.Name = txtName. text; t1.Age = txtAge. text; t1.Remark = txtRemark. text; t2.Name = txtName. text; t2.Age = txtAge. text; t2.Remark = txtRemark. text; try {_ BllObj. transStart (); _ BllObj. add (t2); _ BllObj. add (t1); var model = _ BllObj. getModel <Test1> (1); if (model! = Null) {model. remark = "updated at:" + DateTime. now. toString ("yyyy-MM-dd HH: mm: ss"); _ BllObj. update (new string [] {"Remark"}, model) ;}_ BllObj. transCommit (); MessageBox. show ("submitted successfully! ");} Catch (Exception ex) {_ BllObj. TransRollback (); MessageBox. Show (ex. Message );}}
Method call with where query:
WhereHelper wh1 = _ BllObj. createWhereHelper (); wh1.Add ("Name "). equal ("Zhang San"); List <Test1> list1 = _ BllObj. getList <Test1> (wh1 );
Why should we use BLLObj to create a WhereHelper object? In this case, considering that different database queries should have different syntaxes, we abstracted WhereHelper for extension.
The database is specified when the BLLBase is referenced. Therefore, BLL knows which database is created for WhereHelper. Therefore, it is most appropriate to use the BLL object to create WhereHelper, in this way, database switching will not be affected.
Wh1.Add ("Field 1"). Equal (1). And ("Field 2"). Does not (2 );
Inspired by jquery, this class is designed. Every operation returns the current object (that is, WhereHelper). That is to say, it can be infinitely "clicked.
The biggest advantage of using it is that you don't need to consider the duplicate parameter names, you don't need to consider the compatibility of the Database Change, the operation is so simple.
For how to use and design WHereHelper, see WhereHelper In the lightweight ORM framework (II)
I wrote so much with great effort. If someone asks for something or has any suggestions, please leave a message.
I read an article about programmers yesterday. One of the six characteristics of a good programmer is: Understanding and sharing.
Share the source code of this framework and download it.
Download instructions:
This framework has not been used for any commercial purposes (of course it will not be necessary in the future)
Welcome to correction, criticism, optimization, suggestions, plagiarism and commercial use
The purpose of sharing is to optimize the framework, receive suggestions, and understand the shortcomings.