• 测试 ClownFish、CYQ、Entity Framework、Moon、MySoft、NHibernate、PDF、XCode数据访问组件性能


    下期预告:

    由于很多园友反馈,有的组件不应该缺席、测试复杂度不够、测试还缺乏一定的公平。

    因此考虑在下一个版本中,确保在更加公平的前提下进行更高复杂度的测试 。

    同时将分为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的数据库,如果不用这个数据库,请更改数据库连接字符串)

    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 + " ");
                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
     
  • 相关阅读:
    冲刺计划第五天
    冲刺计划第四天
    冲刺计划第三天
    冲刺计划第二天
    冲刺计划第一天
    本周总结(9)
    统计文章中得单词百分比、以及字母百分比
    梦断代码阅读笔记03
    maven仓库、jar包兼容问题
    SpringMVC异常映射
  • 原文地址:https://www.cnblogs.com/yelaiju/p/3209506.html
Copyright © 2020-2023  润新知