یک نکتهی تکمیلی: تاثیر فراخوانی متد AsNoTracking بر روی کوئریهای خود ارجاعی
همانطور که در مطلب «مباحث تکمیلی مدلهای خود ارجاع دهنده در EF Code first» هم مشاهده کردید، خود EF، قابلیت تشکیل درخت نهایی خود ارجاع دهنده را دارد و به این ترتیب کوئری گرفتن از نتیجهی آن، بسیار ساده میشود. اما ... اگر در این بین، از متد AsNoTracking برای بهینه سازی، کاهش میزان حافظه و حذف پروکسیهای ردیابی تغییرات EF استفاده شود، دیگر این درخت خودکار، تشکیل نخواهد شد. برای پوشش این حالت میتوان به صورت زیر عمل کرد:
الف) تشکیل یک کلاس پایه برای تعریف سادهتر و مشخص رابطههای خود ارجاعی
public abstract class BaseEntity
{
public int Id { get; set; }
}
public abstract class BaseSelfReferencingEntity<TSelfEntity> : BaseEntity
where TSelfEntity : BaseEntity
{
public virtual TSelfEntity? Reply { set; get; }
public int? ReplyId { get; set; }
public virtual ICollection<TSelfEntity>? Children { get; set; }
}
که ساختار معرفی شدهی در اینجا، با توضیحات موجود در متن، انطباق دارد.
ب) پر کردن درخت نهایی حاصل به صورت دستی:
چون دیگر EF این درخت را برای ما تشکیل نمیدهد، اکنون باید خودمان کار تشکیل آنرا به صورت زیر انجام دهیم:
public static class SelfReferencingExtensions
{
public static List<TEntity> ToSelfReferencingTree<TEntity>(this ICollection<TEntity>? originalList)
where TEntity : BaseSelfReferencingEntity<TEntity>
{
var results = new List<TEntity>();
if (originalList is null || originalList.Count == 0)
{
return results;
}
foreach (var rootItem in originalList.Where(x => !x.ReplyId.HasValue))
{
results.Add(rootItem);
AppendChildren(originalList, rootItem);
}
return results;
}
private static void AppendChildren<TEntity>(ICollection<TEntity> originalList, TEntity parentItem)
where TEntity : BaseSelfReferencingEntity<TEntity>
{
foreach (var kid in originalList.Where(x => x.ReplyId.HasValue && x.ReplyId.Value == parentItem.Id))
{
parentItem.Children ??= new List<TEntity>();
parentItem.Children.Add(kid);
AppendChildren(originalList, kid);
}
}
}
در اینجا کار تشکیل درخت نهایی، با استفاده از یک متد بازگشتی، انجام میشود.
پس از این مقدمات، نحوهی استفاده از آن به صورت زیر است:
var comments = await _comments.AsNoTracking()
.Where(x => x.ParentId == postId)
.OrderBy(x => x.Id)
.Take(count)
.ToListAsync();
var commentsTree = comments.ToSelfReferencingTree();
کوئری نویسی ابتدایی آن، کاملا استاندارد و بدون هیچگونه نکتهی خاصی است. ابتدا تمام نظرات یک مطلب (به صورت AsNoTracking) بازگشت داده میشوند و سپس متد ToSelfReferencingTree کار اتصالات نهایی درخت پاسخها را به صورت خودکار انجام میدهد.