PDA

View Full Version : مشکل عدم اجرای اعضای کلاس DbMappingViewCache در EF6.4



SajjadKhati
جمعه 08 اردیبهشت 1402, 13:56 عصر
سلام
من از EF6.4.4 Code First در پروژه ی .Net Framework 4.6.2 استفاده میکنم (Visual Studio 17.5.3) .

برای افزایش سرعت و محاسبه نکردن MappingView برای اولین باری که از دستورات EF در هر بار که برنامه اجرا میشه ، دیدم که از Catch کردن MappingView (مثلا در یک فایل) و بعد خوندن MappingView از اون منبع استفاده میکنند (حداقل یکی از راه های حل مشکل کندی برای اولین بار که دستورات EF در هر بار اجرای برنامه استفاده میشه ، اینه) .

در لینک زیر ، نحوه ی انجام این کار اومده :


https://learn.microsoft.com/en-us/ef/ef6/fundamentals/performance/pre-generated-views#generating-mapping-views-from-code---ef6-onwards

در بخش "Generating Views" ، به درستی اطلاعات MappingView را درون فایل xml ذخیره کردم .

=========================

اما برای قضیه ی لود مشکل دارم (نه اینکه مشکل لود از فایل داشته باشم) . بلکه در همون مقاله گفت که برای لودش ، یک کلاسی بسازید که از کلاس DbMappingViewCache ارث بری کنه (این کلاس هم دو تا عضو abstract در کلاس پدر را که عضوهای MappingHashValue و GetView هستند را باید پیاده سازی کنه) .

بعد هم در انتهای اون مقاله گفت که برای اینکه EF از این کلاس استفاده کنه ، باید از attribute زیر استفاده کنیم :



[assembly: DbMappingViewCacheType(typeof(PhoneBookDbContext), typeof(PhoneBookDbMappingViewCache))]


PhoneBookDbContext ، نام کلاس مشتق DbContext و PhoneBookDbMappingViewCache هم نام کلاس مشتق DbMappingViewCache ام هست .
چون اولش assembly داره ، تا جایی که میدونم ، این نوع attribute ها ، برای یک کلاس یا عضو خاصی نیستند و برای اون assembly بکار برده میشوند (اسمبلی من ، پروژه و لایه ی DataAccess.dll هست) و توصیه میشه که این نوع attribute ها را درون فایل assemblyinfo.cs قرار بدیم (که این فایل درون پوشه ی Properties در اون اسمبلی قرار داره) .
یعنی استفاده از این کلاس ، توسط ما صورت نمیگیره و توسط EF بصورت اتوماتیک صورت میگیره .

کدهای کلاس PhoneBookDbMappingViewCache ام هم اینه :



using DataAccess.ExceptionModule;
using DataAccess.SerializationModule;
using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure.MappingViews;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace DataAccess.EntityModule.Class.PreGeneratedView
{
internal class PhoneBookDbMappingViewCache : DbMappingViewCache
{
private DbMappingViewInfo dbMappingViewInfo;




public PhoneBookDbMappingViewCache()
{
this.dbMappingViewInfo = this.GetDbMappingViewInfo();
}




private DbMappingViewInfo GetDbMappingViewInfo()
{
string xmlFullFileName = "PreGeneratedMappingViews.xml";
string fullFilePath = AppDomain.CurrentDomain.BaseDirectory + xmlFullFileName;
if (File.Exists(fullFilePath) == false)
return null;


DbMappingViewInfo dbMappingViewInfo = XmlDataSerializer.ReadDataFromXmlFile<DbMappingViewInfo>(xmlFullFileName);
return dbMappingViewInfo;
}




public override string MappingHashValue
{
get
{
return this.dbMappingViewInfo?.DbMappingHashValue;
}
}


public override DbMappingView GetView(EntitySetBase extent)
{
Debug.WriteLine("test\ntest\ntest");


if (extent == null)
throw new ArgumentNullException(nameof(extent), ExceptionMessage.argumentNullExceptionMessage);


EntityMappingViewInfoWrapper matchedEntityMappingViewInfoWrapper = this.dbMappingViewInfo?.EntityMappingViews?.Find(
(EntityMappingViewInfoWrapper currentEntityMappingView) =>
{
return currentEntityMappingView.EntityName == extent.Name;
});


if (matchedEntityMappingViewInfoWrapper == null || matchedEntityMappingViewInfoWrapper.EntitySql == null)
return null;


DbMappingView dbMappingView = new DbMappingView(matchedEntityMappingViewInfoWrapper. EntitySql);
return dbMappingView;
}




}
}


کد کلاس assemblyinfo.cs ام که فقط در خط اولش ، اون attribute را بهش اضافه کردم ، اینه :



using DataAccess.EntityModule.Class;
using DataAccess.EntityModule.Class.PreGeneratedView;
using System.Data.Entity.Infrastructure.MappingViews;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;


[assembly: DbMappingViewCacheType(typeof(PhoneBookDbContext), typeof(PhoneBookDbMappingViewCache))]


[assembly: AssemblyTitle("DataAccess")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DataAccess")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]


[assembly: ComVisible(false)]


[assembly: Guid("53e7049a-2c36-46da-b435-1ef77baa5306")]


[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


من هم این کار را کردم و در متد GetView در کلاس PhoneBookDbMappingViewCache ، هم breakpoint و هم دستور Debug.WriteLine نوشتم اما هیچ کدوم شون اجرا نشدن .

میدونید علت چیه و چه کار باید کرد که اعضای این کلاس توسط EF فراخوانی و استفاده بشن؟

==============

من مشکل شبیه این را در دو پست زیر دیدم (هر دو ، یک سئوال هستند) اما جواب خاصی ندیدم که بهش داده باشند :

Can't get DbMappingViewCache to cache my views · Issue #1695 · dotnet/ef6 (github.com) (https://github.com/dotnet/ef6/issues/1695)
و
C#‎‎‎ - How to check if DbMappingViewCache caches the precompiled views - Stack Overflow (https://stackoverflow.com/questions/62541497/how-to-check-if-dbmappingviewcache-caches-the-precompiled-views)

تشکر دوستان .

SajjadKhati
جمعه 08 اردیبهشت 1402, 20:48 عصر
تا اینجا متوجه شدم که اعضای کلاس PhoneBookDbMappingViewCache ، فقط یکبار و اونهم زمانی که فایل اش در حال ساخته شدن برای اولین بار هستند ، اجرا میشه .

هر چند نوشتن فایل اش را در کلاس مشتقِ CreateDatabaseIfNotExists<PhoneBookDbContext> انجام دادم اما خوندن اش طبعا هیچ ربطی به این کلاس نباید داشته باشه و درون این کلاس ، اصلا از کلاسِ PhoneBookDbMappingViewCache استفاده نشد .
نمیدونم علتش چیه!

SajjadKhati
پنج شنبه 14 اردیبهشت 1402, 14:14 عصر
متوجه شدم که اعضای کلاس PhoneBookDbMappingViewCache ، موقع فراخوانی متد Database.Initialize ، فراخوانی نمیشن . بلکه موقعی که درخواست خواندن و یا نوشتن اطلاعات در جدول توسط موجودیت های EF داده میشه ، در این موقع فراخوانی میشن .

هر چند فرق خاصی هم نداره . چون نمیدونم به چه علت ، قبل از فراخوانی اعضای کلاس PhoneBookDbMappingViewCache ، برای اولین باری که برنامه اجرا میشه ، (این درخواست خواندن یا نوشتن توسط EF) ، حدود 2 ثانیه طول میکشه که احتمالا بخاطر محاسبات Mapping View هست . تازه بعد از این 2 ثانیه ، شروع به اجرای اعضای کلاس PhoneBookDbMappingViewCache میکنه!!

در صورتی که اصلا کلاس DbMappingViewCache را طراحی کردن که قبل از اینکه Mapping View را خودش بخواد محاسبه کنه ، اگه اطلاعاتش در جایی از قبل ، ذخیره شده بود ، از اون اطلاعات استفاده کنه . اما نمیدونم این باگ EF6.4.4 هست یا چیز دیگه که بعد از انجام کار ، تازه فراخونی اش میکنه !!