測試 ClownFish、CYQ、Entity Framework、Moon、MySoft、NHibernate、PDF、XCode數據訪問組件性能

下期預告:

因爲不少園友反饋,有的組件不該該缺席、測試複雜度不夠、測試還缺少必定的公平。html

所以考慮在下一個版本中,確保在更加公平的前提下進行更高複雜度的測試 。
git

同時將分爲2組測試,純SQL組件及純ORM組件, 若是純SQL組件不足,就只進行純ORM組件的測試。
github

待加入測試組件有Dapper、PetaPoco/NPoco、Elinq、FluentData ,有更好的建議,請留言。
sql

--------------------------------------------------------------
數據庫

「啊!你在用ORM?會不會性能不好啊?」xcode

用數字來講話,打破模糊的、傳言的印象。

標題提到的組件「增刪改查」都實現了測試代碼,因此除了測試外,也能夠把此項目做爲各個組件的入門參考demo。
緩存

源碼下載:https://github.com/alifellod/DbAccessLibTest/archive/master.zipsession

git地址:https://github.com/alifellod/DbAccessLibTest  歡迎園友貢獻改進代碼。
併發

項目使用的是.Net Framework 4.0可使用2010或2012打開。 app

 

默認測試數據庫使用SqlServer

測試前,請先建立數據表Test(使用名爲Test的數據庫,若是不用這個數據庫,請更改數據庫鏈接字符串)

CREATE  TABLE  [ dbo ]. [ Test ]
(
     [ RowId ]  [ int ]  IDENTITY( 1, 1NOT  NULL,
     [ Guid ]  [ varchar ]( 50primary  key  NOT  NULL,
     [ Content ]  [ nvarchar ]( 500NULL,
     [ 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的執行方式,根據本身的喜歡選擇。

Task能夠很出色的取消建立的線程,ThreadPool測試會較爲穩定,Thread很容易致使SQL鏈接池爆破。
測試以前,可先預熱一下——各個組件進行必定數量查詢。

先看兩張測試圖。

分別是ClownFish和EF的測試圖,X軸是線程名稱,Y軸是耗時。

 

 

特別說明: CYQ的數據是使用更新前的組件,在昨晚測試整理的。今天收到秋天園友的反饋,已經在git提交了更新,測試的數據也迴歸正常。

所以,數據也調整過來。(2013-07-27 13:53)

關於XCode的測試數據,請參看大石頭的說明。大石頭建議在大數據的訪問時使用此組件,並開啓緩存。

同時再也不接受新的組件更新,除非是在新的測試版本中,以免針對性的更新。

數據格式:平均值-最高值-最低值
測試順序:增、改、刪
100「線程」 查詢10次 增刪改
 

 

ClownFish

Moon

PDF

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

PDF

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

PDF

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性能其實並無通常人想象的那麼糟糕。

最後看看程序的代碼是怎麼樣的。

項目目錄

測試代碼接口

public  interface ITest
{
     bool Insert();
     bool Update( string guid,  string content);
    DataTable Select( int count);
    List< string> GetGuidList( int count);
     bool Delete( string guid);
}
View Code

ClownFish-SQL測試代碼

public  class ClownFishTest : DbContextHolderBase, ITest
{
     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);
    }
}
View Code
Moon-SQL測試代碼
public  class MoonTest : ITest
{
     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);
    }
}
View Code

PDF-SQL測試代碼

public  class PdfTest : ITest
{
     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);
    }
}
View Code

CYQ-ORM測試代碼

public  class CyqOrmTest : ITest
{
     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;
    }
}
View Code

EF-ORM測試代碼

public  class EFOrmTest : ITest
{
     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;
    }
}
View Code

Moon-ORM測試代碼

public  class MoonOrmTest : ITest
{
     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;
    }
}
View Code

MySoft-ORM測試代碼

public  class MySoftOrmTest : ITest
{
     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;
    }
}
View Code

NHibernate-ORM測試代碼

public  class NHibernateOrmTest : ITest
{
     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;
    }
}
View Code
PDF-ORM測試代碼
public  class PdfOrmTest : ITest
{
     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;
    }
}
View Code

XCode-ORM測試代碼

public  class XCodeOrmTest : ITest
{
     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;
    }
}
View Code

測試耗時監控

///  

///  執行指定查詢的測試,並記錄測試數據
///  

///  線程序號
///  執行的查詢
///  執行測試的組件實體
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;
}
View Code

測試完整邏輯代碼 ,300多行,慎點。

public  partial  class FrmDbAccessTest : Form
{
     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 -  11);
            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();
    }
}
View Code

 
到底是選擇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左右,就出現鏈接超時問題,不能提供測試數據,有興趣的朋友,本身運行測試。對此形成誤解,請諒解。

 

100-10 insert 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
 
相關文章
相關標籤/搜索