نحوه کاهش مصرف حافظه EF Code first حین گزارشگیری از اطلاعات
نویسنده: وحید نصیری
تاریخ: ۱۳۹۱/۰۵/۰۶ ۱۹:۶
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Diagnostics;
using System.Linq;
namespace EFGeneral
{
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
}
public class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(MyContext context)
{
for (int i = 0; i < 21000; i++)
{
context.Users.Add(new User { Name = "name " + i });
if (i % 1000 == 0)
context.SaveChanges();
}
base.Seed(context);
}
}
public class PerformanceHelper
{
public static string RunActionMeasurePerformance(Action action)
{
GC.Collect();
long initMemUsage = Process.GetCurrentProcess().WorkingSet64;
var stopwatch = new Stopwatch();
stopwatch.Start();
action();
stopwatch.Stop();
var currentMemUsage = Process.GetCurrentProcess().WorkingSet64;
var memUsage = currentMemUsage - initMemUsage;
if (memUsage < 0) memUsage = 0;
return string.Format("Elapsed time: {0}, Memory Usage: {1:N2} KB", stopwatch.Elapsed, memUsage / 1024);
}
}
public static class Test
{
public static void RunTests()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());
StartDb();
for (int i = 0; i < 3; i++)
{
Console.WriteLine("\nRun {0}", i + 1);
var memUsage = PerformanceHelper.RunActionMeasurePerformance(() => LoadWithTracking());
Console.WriteLine("LoadWithTracking:\n{0}", memUsage);
memUsage = PerformanceHelper.RunActionMeasurePerformance(() => LoadWithoutTracking());
Console.WriteLine("LoadWithoutTracking:\n{0}", memUsage);
}
}
private static void StartDb()
{
using (var ctx = new MyContext())
{
var user = ctx.Users.Find(1);
if (user != null)
{
// keep the object in memory
}
}
}
private static void LoadWithTracking()
{
using (var ctx = new MyContext())
{
var list = ctx.Users.ToList();
if (list.Any())
{
// keep the list in memory
}
}
}
private static void LoadWithoutTracking()
{
using (var ctx = new MyContext())
{
var list = ctx.Users.AsNoTracking().ToList();
if (list.Any())
{
// keep the list in memory
}
}
}
}
}
// Query for all users without tracking them
var users = context.Users.AsNoTracking();
// Query for some users without tracking them
var someUsers = context.Users
.Where(u => u.Name.EndsWith("st"))
.AsNoTracking()
.ToList();
//Or ...
var items = context.Users.AsNoTracking().Where(...);
var context = new NorthwindDataContext(); context.tblCities.MergeOption = MergeOption.NoTracking;
public virtual IInternalQuery<TElement> AsNoTracking()
{
return (IInternalQuery<TElement>) new InternalQuery<TElement>(this._internalContext, (ObjectQuery) DbHelpers.CreateNoTrackingQuery((ObjectQuery) this._objectQuery));
}
public static IQueryable CreateNoTrackingQuery(ObjectQuery query)
{
IQueryable queryable = (IQueryable) query;
ObjectQuery objectQuery = (ObjectQuery) queryable.Provider.CreateQuery(queryable.Expression);
objectQuery.MergeOption = MergeOption.NoTracking; // اینجا کار خاموش سازی ردیابی انجام شده
return (IQueryable) objectQuery;
} private static void LoadWithoutTracking()
{
using (var ctx = new MyContext())
{
ctx.Configuration.AutoDetectChangesEnabled = false;
var list = ctx.Users.ToList();
if (list.Any())
{
// keep the list in memory
}
}
}
سلام؛ من اینجا و اینجا و اینجا را ... مطالعه کردم. اما متوجه نشدم در زمان گزارش گیری با هر ابزاری (Stmulsoft, FastReport, ..) اطلاعات باید به صورت یک BusinesObject و یا هر عنوان دیگه ای به ابزار مورد نظر در قالب یک IEnumeable ارسال شود. در حالی که اگر ما IQueable را با .ToList() به یک IEnumerable تبدیل شود، ممکن است در برگیرنده کل اطلاعات باشد. در این موارد راهی برای کاهش حافظه و سربار کم وجود دارد؟ متد ToList را نمیتوان به صورت Lazy پیاده سازی کرد یعنی اگر ابزار گزارش ساز فرضا صفحه 1 را نمایش دهد اطلاعات تا صفحه یک از بانک واکشی بشود. اگر گزارش ما 200 صفحه باشد در حالت عادی کل اطلاعات در سرور لود شده و برنامههای گزارش ساز صرفا پس از تهیه گزارش اطلاعات را به صورت صفحه بندی نمایش میدهند.
سلام؛ ممنون از پاسختون. من تمام برنامههای گزارش سازی را که میشناختم (Telerik, Fastreport, ReportViewer, DevExpress) همه را بررسی کردم . اما هیچ کدام چنین پراپرتی و یا مشابه اون و نداشتند. یعنی هرکسی میخواد گزارش بگیرد یک دفعه کل اطلاعات و از بانک میخونه! هرچقدر هم با فیلترهای مختلف گزارش و با فیلترهای مختلف مثلا تاریخ یا شماره رکورد محدود کنیم اما در کل کاربر در هر لحظه یک صفحه را که بیشتر نمیتواند ببیند. مباحث مذکور به خوبی در انواع گرید و کنترلهای مختلف پیاده سازی شده و شرکتهای مختلف راه حلهای مختلفی همانند مواردی که شما ذکر کردید ارائه کرده اند اما برای گزارش خیر !