实体框架搞乱了调用和页面加载速度极慢

时间:2015-12-31 12:20:07

标签: c# asp.net-mvc entity-framework-6

我有一个代码第一个asp.net mvc5项目,在本地和生产系统上只用几行数据加载页面需要30多秒,这不仅仅是第一次(第一次实际上大约10秒钟,但我理解为什么)。我禁用了调试,并做了一些我可以在网上找到的其他技巧,但我能做的最好的事情是将时间缩短到大约25秒。

问题是,例如,对于其中一个页面,我从数据库中获取一个表,但是当我检查数据库实际执行的操作时,它会独立地查询每一行。得到整个桌子。

我通过通用接口访问上下文。以下是进行实际调用的方法。

        public async Task<IQueryable<TEntity>> Load<TEntity>() where TEntity : class
    {
        var set = await Task.FromResult(Set<TEntity>());

        return set;
    }

    public async Task Create<TEntity>(TEntity entity) where TEntity : class
    {
        if (Entry(entity).State == EntityState.Detached)
            Set<TEntity>().Add(entity);
        await this.SaveChangesAsync();
    }

    public async Task Update<TEntity>(TEntity entity) where TEntity : class
    {
        if (Entry(entity).State == EntityState.Detached)
            Entry(entity).State = EntityState.Modified;
        await this.SaveChangesAsync();
    }

    public async Task Delete<TEntity>(TEntity entity) where TEntity : class
    {
        if (Entry(entity).State == EntityState.Detached)
            Set<TEntity>().Remove(entity);
        await this.SaveChangesAsync();
    }

    public async Task<IQueryable<TEntity>> Query<TEntity>() where TEntity : class
    {
        return await Task.FromResult(Set<TEntity>().AsNoTracking()); // detach results from context
    }

我正在谈论的页面我正在使用Load方法。此外,这些方法位于上下文类中。另一件可能有用的事情是:我正在构建一个多层应用程序。我有我的域图层,我有我的上下文。用于访问该上下文的通用接口,商店调用接口并将所有内容转换为DTO,管理员继承商店并在需要时对数据进行更多工作(现在我正在使用只生成id和那种东西),在管理器上,我构建了另一组接口,由Application Services层调用,由MVC层依次调用(通过接口)再次)。所有内容都由Autofac注入.InstancePerLifetimeScope()。

这是这一个类的例子:

上下文:(代码优先于现有数据库)

  public partial class PurchasingContext : DbContext, IWritePurchasingEntities
{
    public PurchasingContext()
        : base("name=PurchasingContext")
    {
    }

    public virtual DbSet<AttachedFile> AttachedFiles { get; set; }
    public virtual DbSet<BuyingList> BuyingLists { get; set; }
    public virtual DbSet<BuyingListItem> BuyingListItems { get; set; }
    public virtual DbSet<DeliveryPoint> DeliveryPoints { get; set; }
    public virtual DbSet<Item> Items { get; set; }
    public virtual DbSet<Order> Orders { get; set; }
    public virtual DbSet<OrderCategory> OrderCategories { get; set; }
    public virtual DbSet<OrderItem> OrderItems { get; set; }
    public virtual DbSet<OrderValueLimit> OrderValueLimits { get; set; }
    public virtual DbSet<QtyPending> QtyPendings { get; set; }
    public virtual DbSet<ReceivedItem> ReceivedItems { get; set; }
    public virtual DbSet<tempVendor> tempVendors { get; set; }
    public virtual DbSet<UserFavouriteVendor> UserFavouriteVendors { get; set; }
    public virtual DbSet<Vendor> Vendors { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BuyingList>()
            .Property(e => e.TotalPrice)
            .HasPrecision(19, 4);

        modelBuilder.Entity<BuyingList>()
            .HasMany(e => e.BuyingListItems)
            .WithRequired(e => e.BuyingList)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<BuyingListItem>()
            .Property(e => e.Total)
            .HasPrecision(19, 4);

        modelBuilder.Entity<DeliveryPoint>()
            .HasMany(e => e.Orders)
            .WithRequired(e => e.DeliveryPoint)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Item>()
            .Property(e => e.UnitPrice)
            .HasPrecision(19, 4);

        modelBuilder.Entity<Item>()
            .HasMany(e => e.BuyingListItems)
            .WithRequired(e => e.Item)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Item>()
            .HasMany(e => e.OrderItems)
            .WithRequired(e => e.Item)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Item>()
            .HasMany(e => e.QtyPendings)
            .WithRequired(e => e.Item)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Item>()
            .HasMany(e => e.ReceivedItems)
            .WithRequired(e => e.Item)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Order>()
            .Property(e => e.Value)
            .HasPrecision(19, 4);

        modelBuilder.Entity<Order>()
            .Property(e => e.DeliveryCost)
            .HasPrecision(19, 4);

        modelBuilder.Entity<Order>()
            .HasMany(e => e.AttachedFiles)
            .WithRequired(e => e.Order)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Order>()
            .HasMany(e => e.OrderItems)
            .WithRequired(e => e.Order)
            .HasForeignKey(e => e.OrderId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Order>()
            .HasMany(e => e.QtyPendings)
            .WithRequired(e => e.Order)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Order>()
            .HasMany(e => e.ReceivedItems)
            .WithRequired(e => e.Order)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<OrderCategory>()
            .HasMany(e => e.BuyingLists)
            .WithRequired(e => e.OrderCategory)
            .HasForeignKey(e => e.CategoryId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<OrderItem>()
            .Property(e => e.TotalPrice)
            .HasPrecision(19, 4);

        modelBuilder.Entity<OrderItem>()
            .HasMany(e => e.Orders)
            .WithOptional(e => e.OrderItem)
            .HasForeignKey(e => e.OrderItemsId);

        modelBuilder.Entity<OrderValueLimit>()
            .Property(e => e.ValueLimit)
            .HasPrecision(19, 4);

        modelBuilder.Entity<Vendor>()
            .HasMany(e => e.BuyingLists)
            .WithRequired(e => e.Vendor)
            .HasForeignKey(e => e.VendorId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Vendor>()
            .HasMany(e => e.Items)
            .WithRequired(e => e.Vendor)
            .HasForeignKey(e => e.VendorID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Vendor>()
            .HasMany(e => e.Orders)
            .WithRequired(e => e.Vendor)
            .HasForeignKey(e => e.VendorID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Vendor>()
            .HasMany(e => e.UserFavouriteVendors)
            .WithRequired(e => e.Vendor)
            .HasForeignKey(e => e.VendorId)
            .WillCascadeOnDelete(false);
    }


    public async Task<IQueryable<TEntity>> Load<TEntity>() where TEntity : class
    {
        var set = await Task.FromResult(Set<TEntity>());

        return set;
    }

    public async Task Create<TEntity>(TEntity entity) where TEntity : class
    {
        if (Entry(entity).State == EntityState.Detached)
            Set<TEntity>().Add(entity);
        await this.SaveChangesAsync();
    }

    public async Task Update<TEntity>(TEntity entity) where TEntity : class
    {
        if (Entry(entity).State == EntityState.Detached)
            Entry(entity).State = EntityState.Modified;
        await this.SaveChangesAsync();
    }

    public async Task Delete<TEntity>(TEntity entity) where TEntity : class
    {
        if (Entry(entity).State == EntityState.Detached)
            Set<TEntity>().Remove(entity);
        await this.SaveChangesAsync();
    }

    public async Task<IQueryable<TEntity>> Query<TEntity>() where TEntity : class
    {
        return await Task.FromResult(Set<TEntity>().AsNoTracking()); // detach results from context
    }
}

商店:

    public class OrderStore<TOrder>
{
    IWritePurchasingEntities _iWrite;

    public OrderStore(IWritePurchasingEntities iWrite)
    {
        _iWrite = iWrite;
    }
    public async Task<List<OrderDTO>> GetAll()
    {
        var orders = await _iWrite.Load<Order>();
        return orders.ToList().ToDto();
    }
    public async Task<List<OrderDTO>> GetAll(string userId)
    {
        var orders = await _iWrite.Query<Order>();
        return orders.Where(p => p.UserId == userId).ToList().ToDto();
    }
    public async Task<OrderDTO> GetOne(int id)
    {
        var order = await _iWrite.Query<Order>();
        return order.FirstOrDefault(p => p.Id == id).ToDto();
    }
    public void Create(OrderDTO order)
    {
        _iWrite.Create<Order>(order.FromDto());
    }
    public void Update(OrderDTO order)
    {
        _iWrite.Update<Order>(order.FromDto());
    }
    public void Delete(int orderId)
    {
        var order = _iWrite.Query<Order>().Result.FirstOrDefault(p => p.Id == orderId);
        _iWrite.Delete<Order>(order);
    }
}

管理器:

    public class OrderManager : OrderStore<Order>, IOrderManager
{
    IWritePurchasingEntities _iWrite;

    Random random = new Random();

    public OrderManager(IWritePurchasingEntities iWrite)
        :base(iWrite)
    {
        _iWrite = iWrite;
    }

    public async Task ReceivedItemsOnOrder(ReceivedItemDTO receivedDto, QtyPendingDTO pendingDto)
    {
        var order = await GetOne(receivedDto.OrderId);
        if (order.ReceivedItems == null)
        {
            order.ReceivedItems = new List<ReceivedItemDTO>();
        }
        if (order.QtyPendings == null)
        {
            order.QtyPendings = new List<QtyPendingDTO>();
        }
        if (order.QtyPendings.FirstOrDefault(p => p.ItemId == receivedDto.ItemId) == null)
        {
            pendingDto.Id = random.Next(1000000, 9999999);
            pendingDto.OrderId = receivedDto.OrderId;
            pendingDto.ItemId = receivedDto.ItemId;
            pendingDto.QtyOrdered = order.OrderItems.FirstOrDefault(p => p.ItemID == receivedDto.ItemId).Qty;
            pendingDto.QtyPending1 = (int)(pendingDto.QtyOrdered - receivedDto.QtyReceived);
            order.QtyPendings.Add(pendingDto);
        }
        else
        {

            order.QtyPendings.FirstOrDefault(p => p.ItemId == receivedDto.ItemId).QtyPending1 -= (int)receivedDto.QtyReceived;
        }
        receivedDto.Id = random.Next(1000000, 9999999);
        order.ReceivedItems.Add(receivedDto);
        Update(order);
    }

    public async Task<int> GetOrderId()
    {
        var orders = await _iWrite.Query<Order>();
        int orderId;
        if (orders == null || orders.ToList().Count == 0)
            orderId = 100000;
        else
            orderId = orders.OrderBy(q => q.Id).Select(p => p.Id).Last() + 1;
        return orderId;
    }

服务:

    public class OrderService : IOrderService
{
    IOrderManager _orderManager;

    public OrderService(IOrderManager orderManager)
    {
        _orderManager = orderManager;
    }

    public async Task<List<OrderDTO>> GetAll()
    {
        return await _orderManager.GetAll();
    }

    public async Task<List<OrderDTO>> GetAll(string userId)
    {
        return await _orderManager.GetAll(userId);
    }

    public async Task<OrderDTO> GetOne(int id)
    {
        return await _orderManager.GetOne(id);
    }

    public void Create(OrderDTO order)
    {
        _orderManager.Create(order);
    }

    public void Update(OrderDTO order)
    {
        _orderManager.Update(order);
    }
    public void Delete(int orderId)
    {
        _orderManager.Delete(orderId);
    }
    public async Task<int> GetOrderId()
    {
        var orderId = await _orderManager.GetOrderId();
        return orderId;
    }
    public async Task ReceivedItemsOnOrder(ReceivedItemDTO received, QtyPendingDTO pending)
    {
        await _orderManager.ReceivedItemsOnOrder(received, pending);
    }
}

最后控制器(这有点长,1500行,但我只复制在我加载表的索引页面上使用的动作):

    public class OrderController : Controller
{
    IOrderService _orderService;
    IVendorService _vendorService;
    IItemService _itemService;
    IDeliveryPointService _deliveryPointService;
    IOrderCategoryService _orderCategoryService;
    IOrderValueLimitService _orderValueLimitService;
    IBuyingListService _buyingListService;
    NotificationService _notificationService;
    IAttachedFilesService _attachedFileServices;

    protected ApplicationDbContext ApplicationDbContext { get; set; }
    protected UserManager<ApplicationUser> UserManager { get; set; }


    public OrderController(IOrderService orderService,
                            IVendorService vendorService,
                            IItemService itemService,
                            IDeliveryPointService deliveryPointService,
                            IOrderCategoryService orderCategoryService,
                            IOrderValueLimitService orderValueLimitService,
                            IBuyingListService buyingListService,
                            IWriteNotifications iWrite,
                            IAttachedFilesService attachedFileServices)
    {
        _orderService = orderService;
        _vendorService = vendorService;
        _itemService = itemService;
        _deliveryPointService = deliveryPointService;
        _orderCategoryService = orderCategoryService;
        _orderValueLimitService = orderValueLimitService;
        _buyingListService = buyingListService;
        _notificationService = new NotificationService(iWrite);
        _attachedFileServices = attachedFileServices;
        ApplicationDbContext _ctx = new ApplicationDbContext();
        this.ApplicationDbContext = new ApplicationDbContext();
        this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.ApplicationDbContext));
    }

    //Getting the models
    OrderModel om = new OrderModel();

    //i need this for generating id's.
    Random random = new Random();

    //This is the main view for the index page, it is empty because i will use partial views from child actions to populate it with content.
    [HttpGet]
    public ActionResult Index()
    {
        return View();
    }

    //Get a list of orders and sort them by the specified property
    [HttpGet]
    public async Task<ActionResult> List(string sortOrder)
    {
        //set up the appropriate services for order listing
        om.FavouriteVendors = await _vendorService.GetFavourites(User.Identity.GetUserId());
        List<OrderDTO> orders;
        //Get the appropriate order list depending on the user access
        if (User.Identity.HasEdit("Authoriser"))
        {
            orders = await _orderService.GetAll();
        }
        else
        {
            orders = await _orderService.GetAll(User.Identity.GetUserId());
        }

        //Get a list of users with pending orders
        om.Users = new List<ApplicationUser>();
        var userIds = new List<string>();
        foreach (var order in orders)
            if (userIds.Contains(order.UserId) == false)
                userIds.Add(order.UserId);
        foreach (var id in userIds)
            om.Users.Add(UserManager.Users.FirstOrDefault(p => p.Id == id));

        //Get a list of vendors with pending orders
        var orderVendors = new List<VendorDTO>();
        var orderVendorsId = _orderService.GetAll().Result.Select(p => p.VendorID).ToList();
        foreach (var vendorId in orderVendorsId)
        {

            var vendor = await _vendorService.GetOne(vendorId);
            if (orderVendors.Select(p => p.Vendor1).Contains(vendor.Vendor1) == false)
                orderVendors.Add(vendor);
        }

        //Sort the orderList
        ViewBag.CreatedByParam = sortOrder == "by" ? "by_desc" : "by";
        ViewBag.CreatedOnParam = sortOrder == "on" ? "on_desc" : "on";
        ViewBag.VendorParam = sortOrder == "vendor" ? "vendor_desc" : "vendor";
        ViewBag.POParam = sortOrder == "po" ? "po_desc" : "po";
        switch (sortOrder)
        {
            case "by":
                om.Orders = orders.OrderBy(q => om.Users.FirstOrDefault(p => p.Id == q.UserId).UserName).ToList();
                break;
            case "by_desc":
                om.Orders = orders.OrderByDescending(q => om.Users.FirstOrDefault(p => p.Id == q.UserId).UserName).ToList();
                break;
            case "on":
                om.Orders = orders.OrderBy(p => p.OrderDate).ToList();
                break;
            case "on_desc":
                om.Orders = orders.OrderByDescending(p => p.OrderDate).ToList();
                break;
            case "vendor":
                om.Orders = orders.OrderBy(p => orderVendors.FirstOrDefault(q => q.Vendor1 == p.VendorID).VendorName).ToList();
                break;
            case "vendor_desc":
                om.Orders = orders.OrderByDescending(p => orderVendors.FirstOrDefault(q => q.Vendor1 == p.VendorID).VendorName).ToList();
                break;
            case "po":
                om.Orders = orders.OrderBy(p => p.Id).ToList();
                break;
            case "po_desc":
                om.Orders = orders.OrderByDescending(p => p.Id).ToList();
                break;
            default:
                om.Orders = orders.OrderByDescending(p => p.OrderDate).ToList();
                break;
        }

        //Set up the dropdown list of vendors which will be used to filter the list.
        IEnumerable<SelectListItem> orderVendorsList = orderVendors.Select(b => new SelectListItem { Value = b.Vendor1, Text = b.VendorName }).OrderBy(q => q.Text);
        ViewBag.OrderVendors = orderVendorsList;

        return PartialView(om);
    }

    //Applying the filters
    [HttpGet]
    public async Task<ActionResult> FilterList(string FilterByVendor, bool IsAuthorised, bool GoodsReceived, bool PassedForPayment)
    {
        //set up the appropriate services for order listing
        om.FavouriteVendors = await _vendorService.GetFavourites(User.Identity.GetUserId());
        om.Users = UserManager.Users.ToList();


        //Get the appropriate order list depending on the user access
        var orders = new List<OrderDTO>();
        if (User.Identity.HasEdit("Authoriser"))
        {
            om.Orders = await _orderService.GetAll();
        }
        else
        {
            om.Orders = await _orderService.GetAll(User.Identity.GetUserId());
        }

        //Apply the filters

        if (FilterByVendor != null && FilterByVendor != "")
            om.Orders = om.Orders.Where(p => p.VendorID == FilterByVendor).ToList();
        if (FilterByVendor == "")
            om.Orders = om.Orders.ToList();
        if (IsAuthorised)
            om.Orders = om.Orders.Where(p => p.IsAuthorized == IsAuthorised).ToList();
        if (GoodsReceived)
            om.Orders = om.Orders.Where(p => p.GoodsReceived == GoodsReceived).ToList();
        if (PassedForPayment)
            om.Orders = om.Orders.Where(p => p.PassedForPayment == PassedForPayment).ToList();

        return PartialView(om);
    }

}

和订单页面控制器的模型

    public class OrderModel
{
    public ICollection<OrderDTO> Orders { get; set; }
    public ICollection<VendorDTO> Vendors { get; set; }
    public ICollection<VendorDTO> FavouriteVendors { get; set; }
    public ICollection<ApplicationUser> Users { get; set; }
    public ICollection<ItemDTO> Items { get; set; }
    public ICollection<OrderItemsDTO> ItemsList { get; set; }
    public ICollection<DeliveryPointDTO> DeliveryPoints { get; set; }
    public ICollection<OrderCategoryDTO> OrderCategories { get; set; }
    public ICollection<OrderItemsDTO> OrderItems { get; set; }
    public ICollection<HttpPostedFileBase> AttachedFiles { get; set; }
    public ICollection<AttachedFilesDTO> Files { get; set; }
    public ICollection<IdentityRole> Roles { get; set; }
    public ICollection<BuyingListDTO> BuyingLists { get; set; }
    public ICollection<BuyingListItemDTO> BuyingListItems { get; set; }

    public ApplicationUser User { get; set; }
    public VendorDTO Vendor { get; set; }
    public VendorDTO FavouriteVendor { get; set; }
    public DeliveryPointDTO DeliveryPoint { get; set; }
    public OrderCategoryDTO OrderCategory { get; set; }
    public OrderDTO Order { get; set; }
    public OrderValueLimitDTO Limit {get; set;}
    public AttachedFilesDTO File { get; set; }
    public BuyingListDTO BuyingList { get; set; }
    public BuyingListItemDTO BuyingListItem { get; set; }
    public ItemDTO Item { get; set; }
}

在调试时我也得到了这个: enter image description here

我的问题是:这种行为是对的吗?还是我做错了什么?我构建的几乎所有方法都是异步的,我的代码指标结果现在看起来很好(项目还不是很大)。我需要让事情更快地发挥作用。如果我现在在30秒内加载该页面,少于100行数据,我不想知道1000或10.000行或更多行会是什么样的。 。

PS,我的诊断工具看起来也不错,我估计最多只有3-400 MB内存,处理器通常低于25%,但是罕见的峰值高达75,但就像我说的那样,它们只是尖峰,它和#39;不住在那里。

1 个答案:

答案 0 :(得分:0)

所以,由于没有其他人因此而受到赞誉,这就是问题所在。该页面在30秒内加载?现在它加载大约30ms。所以0.03秒。该行为的主要问题是使用通用接口。它看起来很酷,维护起来很容易,而且缺点是特别在SQL服务器上的缺点。所以,不要使用那个泛型(不能说我完全删除它,至少在某些地方仍然使用它,至少现在)我已经构建了一个存储库层(因为我不想要我的域名)依赖于任何具体的类)并为我拥有的每个对象编写新的优化查询。令人惊讶的是var y = context.Objects.ToList()var y = context.Objects.Select(p => new { x = p.name}).ToList()之间存在多大差异,即使该对象只有一个属性。投射为我节省了一天。感谢大家的评论和特别是@shoeb siddique的视频(http://youtube.com/watch?v=GmyVMSf4DtE&feature=youtu.be),这正是我所需要的,而且VahidN也是他的探查器(http://github.com/VahidN/DNTProfiler)。

希望这有助于其他人。所以简短的回答是:投影和优化查询,而不是泛型。