由於很多園友反饋,有的組件不應該缺席、測試復雜度不夠、測試還缺乏一定的公平。
因此考慮在下一個版本中,確保在更加公平的前提下進行更高復雜度的測試 。
同時將分為2組測試,純SQL組件及純ORM組件, 如果純SQL組件不足,就只進行純ORM組件的測試。
待加入測試組件有Dapper、PetaPoco/NPoco、Elinq、FluentData ,有更好的建議,請留言。
--------------------------------------------------------------
“啊!你在用ORM?會不會性能很差啊?”
標題提到的組件“增刪改查”都實現了測試代碼,所以除了測試外,也可以把此項目作為各個組件的入門參考demo。
源碼下載:https://github.com/alifellod/DbAccessLibTest/archive/master.zip
git地址:https://github.com/alifellod/DbAccessLibTest 歡迎園友貢獻改進代碼。
項目使用的是.Net Framework 4.0可以使用2010或2012打開。
默認測試數據庫使用SqlServer
測試前,請先創建數據表Test(使用名為Test的數據庫,如果不用這個數據庫,請更改數據庫連接字符串)
(
[ RowId ] [ int ] IDENTITY( 1, 1) NOT NULL,
[ Guid ] [ varchar ]( 50) primary key NOT NULL,
[ Content ] [ nvarchar ]( 500) NULL,
[ CreateDate ] [ datetime ] NULL default getdate(),
[ EditDate ] [ datetime ] NULL
)
測試前清表:truncatetable Test
默認連接字符串是:Data Source=.;Initial Catalog=Test;Integrated Security=True
此程度測試代碼使用了接口規范,並沒有為了省事耦合在程序里,因此編寫修改測試代碼非常簡單,所以也希望各位園友自己寫上一段測試測試。
由於這些組件基本都是第一次使用,所以可能導致部分組件測試代碼編寫並不合理,敬請指點。
注意:各組件的測試均采用一樣的測試思路,也即ORM對ORM,SQL對SQL,循環也是一樣的循環。
不必拘泥於15和23的區別,而是要區別10和100的區別,也就是在一個數量級別之間比較。
線程我分別寫了Task、ThreadPool和Thread的執行方式,根據自己的喜歡選擇。
先看兩張測試圖。
分別是ClownFish和EF的測試圖,X軸是線程名稱,Y軸是耗時。
特別說明: CYQ的數據是使用更新前的組件,在昨晚測試整理的。今天收到秋天園友的反饋,已經在git提交了更新,測試的數據也回歸正常。
因此,數據也調整過來。(2013-07-27 13:53)
關於XCode的測試數據,請參看大石頭的說明。大石頭建議在大數據的訪問時使用此組件,並開啟緩存。
同時不再接受新的組件更新,除非是在新的測試版本中,以避免針對性的更新。
數據格式:平均值-最高值-最低值
測試順序:增、改、刪
100“線程” 查詢10次 增刪改
|
ClownFish |
Moon |
|
增 |
23-96-2 |
21-76-6 |
8-25-4 |
刪 |
16-49-2 |
15-37-5 |
4-18-2 |
改 |
46-116-3 |
23-56-3 |
7-33-3 |
|
CYQOrm |
EFOrm |
MoonOrm |
MySoftOrm |
NHibernateOrm |
PDFOrm |
XCodeOrm |
增 |
24-79-5 |
23-71-8 |
10-64-3 |
46-102-24 |
21-47-7 |
12-31-4 |
15-60-9 |
刪 |
11-61-4 |
61-137-17 |
10-29-3 |
41-267-15 |
41-103-9 |
20-54-3 |
340-623-173 |
改 |
29-182-18 |
37-113-15 |
5-15-2 |
110-195-52 |
37-128-7 |
17-50-3 |
364-476-246 |
1000“線程” 查詢10次增刪改
|
ClownFish |
Moon |
|
增 |
9-276-2 |
13-49-2 |
7-44-3 |
刪 |
22-125-2 |
7-43-3 |
9-41-3 |
改 |
20-299-2 |
10-123-3 |
8-66-2 |
|
CYQOrm |
EFOrm |
MoonOrm |
MySoftOrm |
NHibernateOrm |
PDFOrm |
XCodeOrm |
增 |
79-961-3 |
29-306-9 |
6-52-3 |
50-386-11 |
12-259-4 |
10-48-3 |
14-231-8 |
刪 |
29-471-4 |
43-321-11 |
14-95-2 |
78-386-13 |
45-237-7 |
22-90-4 |
362-729-177 |
改 |
30-438-3 |
59-334-12 |
27-215-14 |
106-647-18 |
42-294-4 |
17-128-3 |
410-755-199 |
查詢測試,數據行100W
100“線程” 查詢返回Top 100
使用沒有索引的列RowId排序
|
ClownFish |
Moon |
|
查 |
2-19-1 |
1-9-0 |
1-47-0 |
查詢采用的根據組件提供的分頁或者Top查詢功能。(moon及xcode使用的是分頁查詢)
使用沒有索引的列RowId排序
|
CYQOrm |
EFOrm |
MoonOrm |
MySoftOrm |
NHibernateOrm |
PDFOrm |
XCodeOrm |
查 |
1339-2220-281 |
1452-3668-735 |
5230-8032-3241 |
1287-1670-240 |
3372-6264-954 |
2629-3825-836 |
1696-5363-716 |
使用有索引的列Guid排序
|
CYQOrm |
EFOrm |
MoonOrm |
MySoftOrm |
NHibernateOrm |
PDFOrm |
XCodeOrm |
查 |
16-32-13 |
5-8-3 |
5967-14644-1639 |
2-5-1 |
7-127-2 |
1-17-0 |
1-9-0 |
經過測試,發現線程越多,越容易出現問題“超時時間已到,但是尚未從池中獲取連接。出現這種情況可能是因為所有池連接均在使用,並且達到了最大池大小。”導致訪問很不穩定。
一個好的數據訪問層應該是可以優雅的接受並處理大並發的訪問,而不應該僅僅只盯住表面上的測試數據(除非有數量級上的差距)。
整個程序框架怎么才設計出色,更加優雅的面對請求,才是應該花更多心思去考慮的。
從上面也可以看到,ORM性能其實並沒有一般人想象的那么糟糕。
最后看看程序的代碼是怎么樣的。
項目目錄
測試代碼接口

{
bool Insert();
bool Update( string guid, string content);
DataTable Select( int count);
List< string> GetGuidList( int count);
bool Delete( string guid);
}
ClownFish-SQL測試代碼

{
static ClownFishTest()
{
DbContext.RegisterDbConnectionInfo( " sqlserver ", " System.Data.SqlClient ", " @ ", Control.ConnectionStrings);
}
public void TruncateTable()
{
DbHelper.ExecuteNonQuery(SqlString.TruncateTable, null, DbContext, CommandKind.SqlTextNoParams);
}
public bool Insert()
{
var parameter = new { Guid = Guid.NewGuid(), Content = string.Empty };
return (DbHelper.ExecuteNonQuery(SqlString.Insert, parameter, DbContext, CommandKind.SqlTextWithParams) > 0);
}
public bool Update( string guid, string content)
{
var parameter = new { Guid = guid, Content = content };
return (DbHelper.ExecuteNonQuery(SqlString.Update, parameter, DbContext, CommandKind.SqlTextWithParams) > 0);
}
public DataTable Select( int count)
{
return DbHelper.FillDataTable( string.Format(SqlString.Select, count), null, DbContext, CommandKind.SqlTextNoParams);
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
DataTable dtb = Select(count);
if (dtb == null || dtb.Rows.Count == 0)
return result;
result.AddRange( from DataRow row in dtb.Rows select row[ " Guid "].ToString());
return result;
}
public bool Delete( string guid)
{
var parameter = new { Guid = guid };
return (DbHelper.ExecuteNonQuery(SqlString.Delete, parameter, DbContext, CommandKind.SqlTextWithParams) > 0);
}
}

{
private readonly DB _dbHelper = DBFactory.DefaultDB;
public bool Insert()
{
return (_dbHelper.ExecuteOneSql( string.Format(SqlString.InsertFormat, Guid.NewGuid(), string.Empty)) > 0);
}
public bool Update( string guid, string content)
{
return (_dbHelper.ExecuteOneSql( string.Format(SqlString.UpdateFormat, guid, content)) > 0);
}
public DataTable Select( int count)
{
return _dbHelper.GetDataTable( string.Format(SqlString.Select, count));
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
DataTable dtb = Select(count);
if (dtb == null || dtb.Rows.Count == 0)
return result;
result.AddRange( from DataRow row in dtb.Rows select row[ " Guid "].ToString());
return result;
}
public bool Delete( string guid)
{
return (_dbHelper.ExecuteOneSql( string.Format( SqlString.DeleteFormat,guid)) > 0);
}
}
PDF-SQL測試代碼

{
private readonly AdoHelper _dbHelper = MyDB.GetDBHelperByConnectionName( " pdf ");
public bool Insert()
{
IDataParameter[] parameters =
{
new SqlParameter( " @Guid ",SqlDbType.VarChar, 50){Value=Guid.NewGuid().ToString()},
new SqlParameter( " @Content ",SqlDbType.NVarChar, 500){Value= string.Empty}
};
return (_dbHelper.ExecuteNonQuery(SqlString.Insert, CommandType.Text, parameters) > 0);
}
public bool Update( string guid, string content)
{
IDataParameter[] parameters =
{
new SqlParameter( " @Guid ",SqlDbType.VarChar, 50){Value=guid},
new SqlParameter( " @Content ",SqlDbType.NVarChar, 500){Value=content}
};
return (_dbHelper.ExecuteNonQuery(SqlString.Update, CommandType.Text, parameters) > 0);
}
public DataTable Select( int count)
{
return _dbHelper.ExecuteDataSet( string.Format(SqlString.Select, count)).Tables[ 0];
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
DataTable dtb = Select(count);
if (dtb == null || dtb.Rows.Count == 0)
return result;
result.AddRange( from DataRow row in dtb.Rows select row[ " Guid "].ToString());
return result;
}
public bool Delete( string guid)
{
IDataParameter[] parameters =
{
new SqlParameter( " @Guid ",SqlDbType.VarChar, 50){Value=guid}
};
return (_dbHelper.ExecuteNonQuery(SqlString.Delete, CommandType.Text, parameters) > 0);
}
}
CYQ-ORM測試代碼

{
public bool Insert()
{
bool result;
using (MAction actiont = new MAction(TableNames.Test))
{
actiont.Set( " Guid ", Guid.NewGuid());
result = actiont.Insert(InsertOp.None);
}
return result;
}
public bool Update( string guid, string content)
{
bool result;
using (MAction actiont = new MAction(TableNames.Test))
{
actiont.Set( " Content ", content);
result = actiont.Update( " Guid=' " + guid + " ' ");
}
return result;
}
public DataTable Select( int count)
{
DataTable result;
using (MAction actiont = new MAction(TableNames.Test))
{
result = actiont.Select(count, " order by Guid ").ToDataTable();
// actiont.Select(count, "order by RowId");
}
return result;
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
DataTable dtb = Select(count);
if (dtb == null || dtb.Rows.Count == 0)
return result;
result.AddRange( from DataRow row in dtb.Rows select row[ " Guid "].ToString());
return result;
}
public bool Delete( string guid)
{
bool result;
using (MAction actiont = new MAction(TableNames.Test))
{
result = actiont.Delete( " Guid=' " + guid + " ' ");
}
return result;
}
}
EF-ORM測試代碼

{
private readonly EFDbContext _dbContext;
// private static readonly EFDbContext _dbContext = new EFDbContext("Conn");
public EFOrmTest()
{
_dbContext = new EFDbContext( " Conn ");
}
public bool Insert()
{
DbSet model = _dbContext.Set();
model.Add( new TestModel { Guid = Guid.NewGuid().ToString(), EditDate = DateTime.Now });
_dbContext.SaveChanges();
return true;
}
public bool Update( string guid, string content)
{
var model = _dbContext.Set().Find(guid);
model.Content = content;
_dbContext.Entry(model).State = EntityState.Modified;
_dbContext.SaveChanges();
return true;
}
public DataTable Select( int count)
{
GetModelList(count);
return null;
}
public IList GetModelList( int count)
{
DbSet model = _dbContext.Set();
return model.OrderBy(o => o.Guid).Take(count).ToList();
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
var list = GetModelList(count);
if (list == null || list.Count == 0)
return result;
result.AddRange(list.Select(o => o.Guid));
return result;
}
public bool Delete( string guid)
{
var model = _dbContext.Set().Find(guid);
_dbContext.Entry(model).State = EntityState.Deleted;
_dbContext.SaveChanges();
return true;
}
}
Moon-ORM測試代碼

{
public static void Init() { }
public bool Insert()
{
return DBFactory.Add( new MoonTestModel { Guid = Guid.NewGuid().ToString() }) != DBNull.Value;
}
public bool Update( string guid, string content)
{
var model = new MoonTestModel() { Content = content };
model.SetOnlyMark(TestTable.Guid.Equal(guid));
DBFactory.Update(model);
return true;
}
public DataTable Select( int count)
{
GetModelList(count);
return null;
}
public IList GetModelList( int count)
{
// 沒有找到更好的查詢方式
return DBFactory.DefaultDB.GetPagedListDesc( 1, count, TestTable.Guid, TestTable.Guid.NotEqual( " '' "));
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
var list = GetModelList(count);
if (list == null || list.Count == 0)
return result;
result.AddRange(list.Select(moonTestModel => moonTestModel.Guid));
return result;
}
public bool Delete( string guid)
{
return DBFactory.DeleteWhen(TestTable.Guid.Equal(guid)) > 0;
}
}
MySoft-ORM測試代碼

{
private static readonly DbSession DBSession = new DbSession( new MySoft.Data.SqlServer9.SqlServer9Provider(System.Configuration.ConfigurationManager.ConnectionStrings[ " Conn "].ConnectionString));
public bool Insert()
{
return DBSession.Insert( new[] { MySoftTestModel._.Guid }, new object[] { Guid.NewGuid().ToString() }) > 0;
}
public bool Update( string guid, string content)
{
return DBSession.Update( new[] { MySoftTestModel._.Content, MySoftTestModel._.EditDate }, new object[] { content, DateTime.Now }, MySoftTestModel._.Guid == guid) > 0;
}
public DataTable Select( int count)
{
GetModelList(count);
return null;
}
public IList GetModelList( int count)
{
return DBSession.From().OrderBy(MySoftTestModel._.Guid.Asc).GetTop(count).ToList();
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
var list = GetModelList(count);
if (list == null || list.Count == 0)
return result;
result.AddRange(list.Select(o => o.Guid));
return result;
}
public bool Delete( string guid)
{
return DBSession.Delete(MySoftTestModel._.Guid == guid) > 0;
}
}
NHibernate-ORM測試代碼

{
private static readonly ISessionFactory SessionFactory = CreateSessionFactory();
static NHibernateOrmTest()
{
}
private static ISessionFactory CreateSessionFactory()
{
ISessionFactory sessionFactory;
try
{
sessionFactory = Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008
.ConnectionString(s => s.Server( " . ").Database( " Test ")
.TrustedConnection()))
.Mappings(m =>m.FluentMappings.Add())
.BuildSessionFactory();
}
catch (Exception ex)
{
throw;
}
return sessionFactory;
}
public bool Insert()
{
using ( var session = SessionFactory.OpenSession())
{
var model = new TestModel { Guid = Guid.NewGuid().ToString() };
session.Save(model);
session.Flush();
}
return true;
}
public bool Update( string guid, string content)
{
using ( var session = SessionFactory.OpenSession())
{
var model = new TestModel { Guid = guid, Content = content, EditDate = DateTime.Now };
session.Update(model);
session.Flush();
}
return true;
}
public DataTable Select( int count)
{
GetModelList(count);
return null;
}
public IList GetModelList( int count)
{
IList result;
using ( var session = SessionFactory.OpenSession())
{
result = session.QueryOver().OrderBy(o=>o.Guid).Asc.Take(count).List();
}
return result;
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
var list = GetModelList(count);
if (list == null || list.Count == 0)
return result;
result.AddRange(list.Select(o => o.Guid));
return result;
}
public bool Delete( string guid)
{
using ( var session = SessionFactory.OpenSession())
{
var model = new TestModel { Guid = guid};
session.Delete(model);
session.Flush();
}
return true;
}
}

{
public bool Insert()
{
PdfTestModel model = new PdfTestModel
{
Guid = Guid.NewGuid().ToString(),
EditDate = DateTime.Now
};
return EntityQuery<PdfTestModel>.Instance.Insert(model) > 0;
}
public bool Update( string guid, string content)
{
PdfTestModel model = new PdfTestModel
{
Guid = guid,
Content = content,
EditDate = DateTime.Now
};
return EntityQuery<PdfTestModel>.Instance.Update(model) > 0;
}
public DataTable Select( int count)
{
GetModelList(count);
return null;
}
public List<PdfTestModel> GetModelList( int count)
{
PdfTestModel model = new PdfTestModel();
OQL q = OQL.From(model).Limit(count).Select().OrderBy(model.Guid, " ASC ").END;
return EntityQuery<PdfTestModel>.QueryList(q);
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
var list = GetModelList(count);
if (list == null || list.Count == 0)
return result;
result.AddRange(list.Select(o => o.Guid));
return result;
}
public bool Delete( string guid)
{
PdfTestModel model = new PdfTestModel { Guid = guid };
return EntityQuery<PdfTestModel>.Instance.Delete(model) > 0;
}
}
XCode-ORM測試代碼

{
static XCodeOrmTest()
{
XCode.DataAccessLayer.DAL.ShowSQL = false;
// XCode.DataAccessLayer.DAL.SQLPath = AppDomain.CurrentDomain.BaseDirectory;
}
public bool Insert()
{
var model = new XCodeTestModel();
model.Guid = Guid.NewGuid().ToString();
return model.Insert() > 0;
}
public bool Update( string guid, string content)
{
var model = XCodeTestModel.FindByGuid(guid);
model.Content = content;
model.EditDate = DateTime.Now;
return model.Update() > 0;
}
public DataTable Select( int count)
{
GetModelList(count);
return null;
}
public List GetModelList( int count)
{
return XCodeTestModel.Search( string.Empty, " Guid ASC ", 0, count);
}
public List< string> GetGuidList( int count)
{
List< string> result = new List< string>();
var list = GetModelList(count);
if (list == null || list.Count == 0)
return result;
result.AddRange(list.Select(o => o.Guid));
return result;
}
public bool Delete( string guid)
{
// return XCodeTestModel.Delete(new[] { "Guid" }, new object[] { guid }) > 0;
return XCodeTestModel.FindByGuid(guid).Delete() > 0;
}
}
測試耗時監控

/// 執行指定查詢的測試,並記錄測試數據
///
/// 線程序號
/// 執行的查詢
/// 執行測試的組件實體
private object TestWork( object index, Actionint> execute, ITest instance)
{
Stopwatch sw = new Stopwatch();
string threadName = string.Format( " {0:D3} ", index);
if (_showThreadWorkStatus)
UpdateMessage( string.Format( " 線程{0}開始時間:{1} ", threadName, DateTime.Now));
sw.Start();
try
{execute(instance, ( int) index);}
catch (Exception ex)
{UpdateMessage( string.Format( " *線程{0} 執行錯誤,信息:{1} ", threadName, ex.Message));}
sw.Stop();
if (_showThreadWorkStatus)
UpdateMessage( string.Format( " *線程{0} *總耗時:{1} *當前時間:{2} ", threadName, sw.ElapsedMilliseconds, DateTime.Now));
_waitWrite.WaitOne();
_waitWrite.Reset();
_workThreadCount--;
_totalElapsed += sw.ElapsedMilliseconds;
_workerInfo.Add( new Worker { Name = threadName, TotalElapsed = sw.ElapsedMilliseconds });
if (_workThreadCount == 0)
{
UpdateMessage( string.Format( " 全部線程總耗時:{0} *開始時間:{1} *結束時間:{2} ", _totalElapsed, _beginTime, DateTime.Now));
UpdateChart();
EnableExecuteControl();
}
Application.DoEvents();
UpdateThreadCountBack();
_waitWrite.Set();
return 0;
}
測試完整邏輯代碼 ,300多行,慎點。

{
private string _executeType = " Insert "; // 測試查詢類型
private string _testLibClassPath = " DbAccessLibTest.Test.ClownFishTest "; // 測試組件類路徑
private long _totalElapsed; // 總耗時
private int _workThreadCount; // 工作線程數
private int _threadCountCreated, _threadCountBack;
private int _executeCount; // 執行查詢次數(返回行數)
private bool _showThreadWorkStatus;
private Dictionary< int, List< string>> _guidList; // 執行刪除和修改時,預先取回的主鍵guid集合,按照每個線程序號分組分配
private List _workerInfo; // 測試工作線程信息,主要用於圖表數據顯示
private ITest _testInstance; // 用於准備執行刪除、修改的guid集合
private DateTime _beginTime; // 測試開始的時間
private readonly AutoResetEvent _waitWrite = new AutoResetEvent( true); // 編輯總耗時等全局信息的信號燈
private CancellationTokenSource _taskCancelToken;
private readonly Assembly _self = Assembly.Load( " 數據訪問組件測試 ");
public FrmDbAccessTest()
{
InitializeComponent();
}
private void Form1_Load( object sender, EventArgs e)
{
_workerInfo = new List();
chartMain.Series[ 0].XValueMember = " Name ";
chartMain.Series[ 0].YValueMembers = " TotalElapsed ";
chartMain.DataSource = _workerInfo;
}
///
/// 執行測試
///
private void btnExecute_Click( object sender, EventArgs e)
{
// 初始化數據
_workThreadCount = ( int)nuWorkThreadCount.Value;
_executeCount = ( int)nuExecuteCount.Value;
_totalElapsed = 0;
_workerInfo = new List();
_testLibClassPath = " DbAccessLibTest.Test. " + cmbTestLib.Text;
_testInstance = (ITest)_self.CreateInstance(_testLibClassPath);
_beginTime = DateTime.Now;
UpdateMessage( string.Format( " 測試開始時間:{0} ", DateTime.Now));
UpdateMessage( string.Format( " 啟動線程數:{0} ", _workThreadCount));
UpdateMessage( string.Format( " 執行操作數:{0} ", _executeCount));
btnExecute.Enabled = false;
nuThreadCountBack.Value = _threadCountBack = 0;
nuThreadCountCreated.Value = _threadCountCreated = 0;
_showThreadWorkStatus = ckbShowThreadStatus.Checked;
ckbShowThreadStatus.Enabled = false;
new Thread(TestMain).Start();
}
private void TestMain()
{
try
{
UpdateMessage( string.Format( " 數據准備開始:{0} ", DateTime.Now));
Actionint> testExecute = GetExecuteUse();
UpdateMessage( string.Format( " 數據准備結束:{0} ", DateTime.Now));
int createCount = _workThreadCount;
UpdateMessage( string.Format( " 開始創建線程:{0} ", DateTime.Now));
_taskCancelToken = new CancellationTokenSource();
for ( int index = 0; index < createCount; index++)
{
if (IsDisposed)
return; // 防止重啟程序時,仍然不斷創建線程
if (rdbThreadPool.Checked)
UseThreadPool(index, testExecute);
else if (rdbThread.Checked)
UseThread(index, testExecute);
else
{
if (!UseTask(index, testExecute))
return; // 如果已經重啟程序,則跳出
}
Thread.Sleep( 50);
UpdateThreadCountCreated();
}
UpdateMessage( string.Format( " 創建線程完畢:{0} ", DateTime.Now));
}
catch (Exception ex)
{
UpdateMessage(ex.Message);
}
}
private void UseThreadPool( int index, Actionint> testExecute)
{
ThreadPool.QueueUserWorkItem(
threadIndex =>
TestWork(threadIndex, testExecute
, (ITest)_self.CreateInstance(_testLibClassPath)), index);
}
private void UseThread( int index, Actionint> testExecute)
{
new Thread(
threadIndex =>
TestWork(threadIndex, testExecute
, (ITest)_self.CreateInstance(_testLibClassPath)))
.Start(index);
}
private bool UseTask( int index, Actionint> testExecute)
{
var task = new Task< object>
(
threadIndex => TestWork(threadIndex, testExecute, (ITest)_self.CreateInstance(_testLibClassPath))
, index
, _taskCancelToken.Token
);
if (_taskCancelToken.IsCancellationRequested)
return false;
task.Start();
return true;
}
///
/// 執行指定查詢的測試,並記錄測試數據
///
/// 線程序號
/// 執行的查詢
/// 執行測試的組件實體
private object TestWork( object index, Actionint> execute, ITest instance)
{
Stopwatch sw = new Stopwatch();
string threadName = string.Format( " {0:D3} ", index);
if (_showThreadWorkStatus)
UpdateMessage( string.Format( " 線程{0}開始時間:{1} ", threadName, DateTime.Now));
sw.Start();
try
{execute(instance, ( int) index);}
catch (Exception ex)
{UpdateMessage( string.Format( " *線程{0} 執行錯誤,信息:{1} ", threadName, ex.Message));}
sw.Stop();
if (_showThreadWorkStatus)
UpdateMessage( string.Format( " *線程{0} *總耗時:{1} *當前時間:{2} ", threadName, sw.ElapsedMilliseconds, DateTime.Now));
_waitWrite.WaitOne();
_waitWrite.Reset();
_workThreadCount--;
_totalElapsed += sw.ElapsedMilliseconds;
_workerInfo.Add( new Worker { Name = threadName, TotalElapsed = sw.ElapsedMilliseconds });
if (_workThreadCount == 0)
{
UpdateMessage( string.Format( " 全部線程總耗時:{0} *開始時間:{1} *結束時間:{2} ", _totalElapsed, _beginTime, DateTime.Now));
UpdateChart();
EnableExecuteControl();
}
Application.DoEvents();
UpdateThreadCountBack();
_waitWrite.Set();
return 0;
}
#region 執行查詢
private void ExecuteInsert(ITest instance, int threadIndex)
{
for ( int i = 0; i < _executeCount; i++)
instance.Insert();
}
private void ExecuteUpdate(ITest instance, int threadIndex)
{
foreach ( string guid in _guidList[threadIndex])
instance.Update(guid, string.Format( " 測試修改 **線程:{0}**組件:{1}**時間:{2} ", threadIndex, instance.GetType(), DateTime.Now));
}
private void ExecuteDelete(ITest instance, int threadIndex)
{
foreach ( string guid in _guidList[threadIndex])
instance.Delete(guid);
}
private void ExecuteSelect(ITest instance, int threadIndex)
{
instance.Select(_executeCount);
}
#endregion
///
/// 獲取測試的操作,如果是刪除和更新,則先准備數據
///
///
private Actionint> GetExecuteUse()
{
switch (_executeType)
{
case " Insert ":
return ExecuteInsert;
case " Delete ":
AssignGuid(); return ExecuteDelete;
case " Update ":
AssignGuid(); return ExecuteUpdate;
case " Select ":
return ExecuteSelect;
}
throw new Exception( " 程序異常:GetExecute ");
}
///
/// 獲取刪除或者更新需要的guid集合,並分配給各個線程
///
private void AssignGuid()
{
var list = _testInstance.GetGuidList(_workThreadCount * _executeCount);
_guidList = new Dictionary< int, List< string>>(_workThreadCount);
for ( int i = 0; i < _workThreadCount; i++)
_guidList.Add(i, new List< string>());
for ( int i = 0; i < list.Count; i++)
{
int index = i % _workThreadCount;
_guidList[index].Add(list[i]);
}
}
///
/// 更新圖表
///
private void UpdateChart()
{
if (IsDisposed)
return;
if (InvokeRequired)
BeginInvoke( new MethodInvoker(UpdateChart));
else
{
if (_workerInfo.Count > 10)
chartMain.Series[ 0].Label = string.Empty;
_workerInfo.Sort();
chartMain.DataSource = _workerInfo;
chartMain.ResetAutoValues();
}
}
///
/// 更新信息
///
///
private void UpdateMessage( string msg)
{
if (IsDisposed)
{
return;
}
if (InvokeRequired)
BeginInvoke( new MethodInvoker(() => UpdateMessage(msg)));
else
{
rtxtMessage.AppendText(msg + " \n ");
rtxtMessage.Select(rtxtMessage.Text.Length - 1, 1);
rtxtMessage.ScrollToCaret();
}
}
///
/// 更新已創建線程數
///
private void UpdateThreadCountCreated()
{
if (IsDisposed)
return;
if (InvokeRequired)
BeginInvoke( new MethodInvoker(UpdateThreadCountCreated));
else
nuThreadCountCreated.Value = _threadCountCreated = ( int)nuThreadCountCreated.Value + 1;
}
///
/// 更新返回線程數
///
private void UpdateThreadCountBack()
{
if (IsDisposed)
return;
if (InvokeRequired)
BeginInvoke( new MethodInvoker(UpdateThreadCountBack));
else
nuThreadCountBack.Value = _threadCountBack = ( int)nuThreadCountBack.Value + 1;
}
///
/// 啟用執行按鈕
///
private void EnableExecuteControl()
{
if (IsDisposed)
return;
Invoke( new MethodInvoker(() =>
{
btnExecute.Enabled = true;
ckbShowThreadStatus.Enabled = true;
}));
}
private void rdbExecute_CheckedChanged( object sender, EventArgs e)
{
RadioButton rdb = sender as RadioButton;
if (rdb == null)
throw new Exception( " 程序異常:rdbExecute_CheckedChanged ");
if (rdb.Checked)
_executeType = rdb.Text;
}
private void btnAnalysis_Click( object sender, EventArgs e)
{
// 暫時沒有實現
}
private void btnTruncateTable_Click( object sender, EventArgs e)
{
if (MessageBox.Show( " 確定要清空整個表嗎 ", " 提示 ", MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk) == DialogResult.Yes)
{
btnTruncateTable.Enabled = false;
new ClownFishTest().TruncateTable();
UpdateMessage( " 表已清空 - " + DateTime.Now);
btnTruncateTable.Enabled = true;
}
}
private void btnThreadInfo_Click( object sender, EventArgs e)
{
UpdateMessage( string.Format( " 線程數:{0} ", _workThreadCount));
}
private void btnRestartForm_Click( object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
if (rdbTask.Checked)
_taskCancelToken.Cancel( true);
Close();
Dispose();
}
}
到底是選擇ORM而不寫Sql,還是 為了追求性能,而避開ORM,就看自己的情況來取舍了。
選擇一個組件的時候,可以考慮這幾方面:穩定性、性能、易用性、是否保持更新、是否有較好的文檔手冊、使用者社區怎么樣、是否開源。
上面提供的組件測試代碼也可以看到這些組件的代碼風格,喜歡語法糖的不妨好好看看,到底更喜歡哪種風格。
綜合考慮選擇。
ClownFish: http://www.cnblogs.com/fish-li/ 【不開源】僅此一個是非ORM的。
CYQ: http://www.cnblogs.com/cyq1162/ 【逐版本開源】
EF: https://entityframework.codeplex.com/ 【開源】
Moon: http://www.cnblogs.com/humble/ 【不開源】
MySoft: http://www.cnblogs.com/maoyong/archive/2010/03/01/1675730.html 【逐版本開源】
NHibernate: http://nhforge.org/ 【開源】
PDF: http://www.cnblogs.com/bluedoctor/【開源】
XCode: http://xcode.codeplex.com/ 【開源】
希望更多的園友分享或開源自己所能知道的心愛的數據訪問組件。
QQ交流群:9524888 ,如果想提交測試代碼,可以直接發給我,我加上去。
更新修正說明:
由於ClownFish提出測試的時候,使用了匿名對象,因此修改為SQL的直接執行,測試數據如下。
由於1000線程的測試,在500左右,就出現連接超時問題,不能提供測試數據,有興趣的朋友,自己運行測試。對此造成誤解,請諒解。
37-375-5
215-611-29
223-562-40
12-181-3
7-21-4
10-138-3
8-71-4
11-187-4
25-227-3
107-829-3
265-679-15
226-609-19
100-10-delete
10-90-2
24-115-3
33-282-2
37-174-3
111-537-2
23-203-2
8-54-3
100-10-update
11-174-3
16-190-4
21-69-2
21-58-4
75-1155-3