实体框架(6)性能优化建议

时间:2013-06-20 14:35:01

标签: c# .net performance entity-framework entity-framework-6

我的应用程序中有一个ADO.Net数据访问层,它使用基本的ADO.Net和CRUD存储过程(每个操作一个,例如Select_myTable,Insert_myTable)。可以想象,在大型系统(如我们的系统)中,DA层所需的DB对象数量非常大。

我一直在研究将层类重构为EF POCO类的可能性。我已经设法做到这一点,但是当我尝试进行性能测试时,它变得非常可怕。使用下面的类(创建对象,将Key设置为所需的值,调用dataselect),100000次数据加载仅需要大约47秒(数据库中只有少数记录)。而Stored Proc方法大约需要7秒钟。

我正在寻找关于如何优化这一点的建议 - 作为一个注意事项,我不能改变层的暴露功能 - 只是它如何实现方法(即我不能将上下文所有权的责任转交给BO层)

由于

public class DAContext : DbContext
{
    public DAContext(DbConnection connection, DbTransaction trans)
        : base(connection, false)
    {
        this.Database.UseTransaction(trans);

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        //Stop Pluralising the Object names for table names.
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        //Set any property ending in "Key" as a key type.
        modelBuilder.Properties().Where(prop => prop.Name.ToLower().EndsWith("key")).Configure(config => config.IsKey());

    }

    public DbSet<MyTable> MyTable{ get; set; }
}

public class MyTable : DataAccessBase
{

    #region Properties

    public int MyTableKey { get; set; }

    public string Name { get; set; }
    public string Description { get; set; }
    public bool Active { get; set; }
    public int CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public int ModifiedBy { get; set; }
    public DateTime ModifiedDate { get; set; }

    #endregion

    #region constructors

    public MyTable()
    {
        //Set Default Values.
        Active = true;
        Name = string.Empty;
        CreatedDate = DateTime.MinValue;
        ModifiedDate = DateTime.MinValue;
    }

    #endregion

    #region Methods

    public override void DataSelect(System.Data.SqlClient.SqlConnection connection, System.Data.SqlClient.SqlTransaction transaction)
    {
        using (DAContext ctxt = new DAContext(connection, transaction))
        {

            var limitquery = from C in ctxt.MyTable
                             select C;


            //TODO: Sort the Query
            limitquery = FilterQuery(limitquery);

            var limit = limitquery.FirstOrDefault();

            if (limit != null)
            {
                this.Name = limit.Name;
                this.Description = limit.Description;
                this.Active = limit.Active;
                this.CreatedBy = limit.CreatedBy;
                this.CreatedDate = limit.CreatedDate;
                this.ModifiedBy = limit.ModifiedBy;
                this.ModifiedDate = limit.ModifiedDate;
            }
            else
            {
                throw new ObjectNotFoundException(string.Format("No MyTable with the specified Key ({0}) exists", this.MyTableKey));
            }
        }
    }

    private IQueryable<MyTable1> FilterQuery(IQueryable<MyTable1> limitQuery)
    {
        if (MyTableKey > 0) limitQuery = limitQuery.Where(C => C.MyTableKey == MyTableKey);
        if (!string.IsNullOrEmpty(Name)) limitQuery = limitQuery.Where(C => C.Name == Name);
        if (!string.IsNullOrEmpty(Description)) limitQuery = limitQuery.Where(C => C.Description == Description);
        if (Active) limitQuery = limitQuery.Where(C => C.Active == true);
        if (CreatedBy > 0) limitQuery = limitQuery.Where(C => C.CreatedBy == CreatedBy);
        if (ModifiedBy > 0) limitQuery = limitQuery.Where(C => C.ModifiedBy == ModifiedBy);
        if (CreatedDate > DateTime.MinValue) limitQuery = limitQuery.Where(C => C.CreatedDate == CreatedDate);
        if (ModifiedDate > DateTime.MinValue) limitQuery = limitQuery.Where(C => C.ModifiedDate == ModifiedDate);

        return limitQuery;
    }

    #endregion


}

2 个答案:

答案 0 :(得分:1)

跟踪时选择很慢。你绝对应该关闭跟踪并再次测量。

看看我的基准

http://netpl.blogspot.com/2013/05/yet-another-orm-micro-benchmark-part-23_15.html

答案 1 :(得分:0)

这可能只是一种预感,但是......在您的存储过程中,过滤器定义良好,SP处于编译状态,具有良好的执行计划。您的EF查询从头开始构建,并在每次使用时重新编译。因此,现在的任务就是设计一种在使用之间编译和保存EF查询的方法。一种方法是重写您的FilterQuery以不依赖于流畅的条件方法链。每次参数集更改时,不是追加或不附加新条件,而是将其转换为一个,其中过滤器在满足条件时应用,或者在没有时被1.Equals(1)覆盖。这样您的查询就可以被编译并可以重复使用。支持SQL看起来很时髦,但执行时间应该会有所改善。或者,您可以设计面向方面编程方法,其中编译查询将根据参数值重新使用。如果我有时间,我会在Code Project上发布一个样本。