我有一个代码第一个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; }
}
我的问题是:这种行为是对的吗?还是我做错了什么?我构建的几乎所有方法都是异步的,我的代码指标结果现在看起来很好(项目还不是很大)。我需要让事情更快地发挥作用。如果我现在在30秒内加载该页面,少于100行数据,我不想知道1000或10.000行或更多行会是什么样的。 。
PS,我的诊断工具看起来也不错,我估计最多只有3-400 MB内存,处理器通常低于25%,但是罕见的峰值高达75,但就像我说的那样,它们只是尖峰,它和#39;不住在那里。答案 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)。
希望这有助于其他人。所以简短的回答是:投影和优化查询,而不是泛型。