نصائح للعمل مع EntityFramework Core

مرحبا.



اكتشفت مؤخرًا أنه ليس كل من يعمل مع EF يعرف كيف يطبخها. علاوة على ذلك ، فهم ليسوا متحمسين للفهم. مواجهة المشكلات في المراحل المبكرة جدًا - الإعداد.

حتى بعد التكوين الناجح ، هناك مشاكل في طلبات البيانات. ليس لأن الناس لا يعرفون LINQ ، ولكن لأنه لا يمكن تعيين كل شيء من الكائنات إلى النماذج العلائقية. لأنه عند العمل باستخدام ارتباط ، يفكر الأشخاص في الجداول. ارسم استعلامات SQL وحاول ترجمتها إلى LINQ.



هذا وربما شيء آخر أريد التحدث عنه في المقال.



التخصيص



جاء أحد الأشخاص إلى المشروع ، ورأى EF هناك لأول مرة ، وتعجب من هذه المعجزة ، وتعلم كيفية استخدامها ، وقرر استخدامها لأغراض شخصية. أنشأت مشروعا جديدا ... ثم ماذا تفعل؟



تبدأ في googling ، التجربة ، الخطأ. من أجل توصيل EF بمشروع ما ، يواجه المطور أنواعًا غير مفهومة من المشكلات.



1. وكيف يتم تكوينه للعمل مع نظام إدارة قواعد البيانات المطلوب؟

2. وكيف تهيئ عمل الهجرات؟



أنا متأكد من أن هناك المزيد من المشاكل ، ولكن ربما تكون هذه هي الأكثر شيوعًا. دعنا نتحدث عن كل شيء بالترتيب.



1. حسنا هنا لجوجل. :) مهمتك هي العثور على مزود EntityFramework Core لنظام إدارة قواعد البيانات لديك.



ستجد أيضًا في وصف المزود تعليمات حول الإعداد.



بالنسبة إلى PostgreSQL ، على سبيل المثال ، تحتاج إلى تثبيت حزمة Nuget Npgsql.EntityFrameworkCore.PostgreSQL

قم بإنشاء السياق الخاص بك بقبول DbContextOptions وإنشاء كل شيء مثل هذا



var opts = new DbContextOptionsBuilder<MyDbContext>()
        .UseNpgsql(constring);

var ctx = new MyDbContext(opts.Options);




إذا كان لديك تطبيق ASP .NET Core ، فسيتم تسجيل السياق في الحاوية بشكل مختلف. ولكن يمكنك بالفعل رؤية هذا في مشروع العمل الخاص بك. أو جوجل.



2. إذا لم تكن هناك مربيات في شركتك ، فأنت على الأرجح تعرف كيفية تثبيت الأدوات اللازمة للعمل مع عمليات الترحيل. على الأقل لمدير الحزم ، على الأقل NET Core CLI.



ولكن ما قد لا تكون على دراية به هو أن مشروع بدء التشغيل المحدد (- مشروع بدء التشغيل) يبدأ عند العمل مع عمليات الترحيل. هذا يعني أنه إذا كان مشروع بدء عمليات الترحيل هو نفس المشروع الذي يبدأ تشغيل التطبيق الخاص بك وعندما تبدأ لسبب ما ، تقوم بتدوير عمليات الترحيل من خلال ctx.Database.Migrate () ، ثم عندما تحاول إنشاء حذف ترحيل تم إنشاؤه مسبقًا أو إنشاء ترحيل آخر ، سيتم ترحيل آخر ترحيل تم إنشاؤه إلى القاعدة. مفاجأة!



ولكن ، ربما ، عند محاولة إنشاء الترحيل الأول ، ستلتقط شيئًا كهذا



لم يتم تكوين موفر قاعدة بيانات لـ DbContext هذا. يمكن تكوين الموفر عن طريق تجاوز طريقة DbContext.OnConfiguring أو باستخدام AddDbContext على موفر خدمة التطبيق. إذا تم استخدام AddDbContext ، فتأكد أيضًا من أن نوع DbContext الخاص بك يقبل كائن DbContextOptions <TContext> في منشئه ويمرره إلى المُنشئ الأساسي لـ DbContext.


هذا لأن الأداة التي تعمل مع عمليات الترحيل تقوم بإنشائها استنادًا إلى السياق الخاص بك ، ولكنها تحتاج إلى مثيل لهذا الغرض. ولتوفيره ، تحتاج إلى تنفيذ واجهة IDesignTimeDbContextFactory في مشروع البداية. هذا ليس بالأمر الصعب ، هناك طريقة واحدة فقط يجب أن ترجع مثيلاً لسياقك.



إعداد النماذج



لقد قمت بالفعل بإنشاء ترحيلك الأول ، ولكنه لسبب ما فارغ. على الرغم من أن لديك نماذج.



النقطة المهمة هي أنك لم تدرس السياق الخاص بك لترجمة النماذج الخاصة بك إلى قاعدة البيانات الخاصة بك.



لكي تقوم EF بإنشاء ترحيل لإنشاء جدول للنموذج الخاص بك في قاعدة البيانات ، يجب على الأقل إنشاء خاصية لنوع DbSet <MyEntity> في السياق الخاص بك.



على سبيل المثال ، بواسطة هذه الخاصية

DbSet <MyEntity> MyEntities {get؛ جلس؛ }



سيتم إنشاء جدول MyEntities مع الحقول المقابلة لخصائص كيان MyEntity.



إذا كنت لا تريد أن يكون لديك DbSet أو تريد التأثير على قواعد إنشاء الجدول الافتراضية لكيان ما ، فأنت بحاجة إلى تجاوز طريقة

protected override void OnModelCreating(ModelBuilder modelBuilder)

السياق الخاص بك.



كيف تفعل هذا ، على الأرجح تعلم. علاوة على ذلك ، ربما تكون على دراية بالسمات التي تسمح لك بالتحكم في قواعد التعيين. لكن هذا هو الشيء. لا تمنح السمات تحكمًا كاملاً في التعيين ، مما يعني أنه سيكون لديك تصفيات في OnModelCreating. بمعنى ، سيكون لديك قواعد لتعيين الكيانات في شكل تعليقات توضيحية وفي شكل واجهة برمجة تطبيقات بطلاقة. اذهب ثم ابحث عن سبب وجود اسم خاطئ للحقل ، والذي توقعته ، أو القيود الخاطئة.



- حسنًا ، كل شيء بسيط - كما تقول - سأقوم بتهيئة كل شيء من خلال OnModelCreating



وبعد ذلك ، بعد إنشاء الكيان العشرين من 10 حقول ، تبدأ عيناك في التموج. أنت تحاول العثور على الإعداد الميداني لكيان ما ، ولكن كل شيء يطفو أمام عينيك من كتلة موحدة بطول 200-500 سطر.



نعم ، يمكنك تقسيم هذه الكتلة إلى 20 طريقة وستصبح أسهل قليلاً. ولكن من المفيد معرفة أن هناك واجهة IEntityTypeConfiguration <TEntity> ، من خلال تنفيذ أي كيان معين ، يمكنك وصف قواعد التعيين الخاصة بكيان معين في هذا التطبيق. حسنًا ، لكي يلتقطها السياق ، في OnModelCreating ، تحتاج إلى كتابة

modelBuilder.ApplyConfigurationsFromAssembly (assemblyWithConfigurations) ؛



وبالطبع ، من الممكن نقل السلوك العام إلى نفس OnModelCreating. على سبيل المثال ، إذا كانت لديك قاعدة عامة لمعرفات جميع الكيانات أو العديد منها ، فيمكنك تكوينها على هذا النحو



foreach (var idEntity in modelBuilder.Model.GetEntityTypes()
    .Where(x => typeof(BaseIdEntity).IsAssignableFrom(x.ClrType))
    .Select(x => modelBuilder.Entity(x.ClrType)))
{
    idEntity.HasKey(nameof(BaseIdEntity.Id));
}


استفسارات البناء



حسنًا ، لقد رتبنا الأشياء ، وأصبح الأمر أكثر متعة.



الآن يبقى إعادة استعلامات مشروعنا المنزلي من SQL إلى Linq. لقد كتبت بالفعل طلبات في العمل ، الأمر سهل مثل

قصف الكمثرى ctx.MyEntities.Where (الشرط). حدد (الخريطة). GroupBy (التعبير). OrderBy (التعبير) ... الخفة.



إذن ما هو طلبنا؟



SELECT bla bla bla FROM table
RIGHT JOIN....... 


بلى



ctx.LeftEntities.RightJoin(.... f@#$


لقد كنت أستخدم Linq وطرق الامتداد الخاصة به قبل فترة طويلة من معرفتي بـ EF. ولم يكن لدي أي سؤال مطلقًا ، ولكن أين هو RIGHT JOIN ، LEFT JOIN فيه ...



ما هو LEFT JOIN من حيث الكائنات؟



عليه




class LeftEntity 
{
    public List<RightEntity> RightEntities { get; set; }
}


إنه ليس سحرًا. لدينا فقط كيان معين يشير إلى قائمة الكيانات الأخرى ، ولكن قد لا يكون لدينا أي منها.



ان هذا



ctx.LeftEntities.Include(x => x.RightEntities)


ما هو RIGHT JOIN؟ هذا هو LEFT JOIN المقلوب ، مما يعني أننا نبدأ للتو بكيان مختلف.



ولكن يحدث أي شيء ، فأنت بحاجة أحيانًا إلى التحكم في كل حزمة ككيان منفصل ، حتى لو لم يكن الكيان الأيسر مرتبطًا بأي كيان صحيح (الكيان الأيمن هو NULL). لذلك ، يمكن إجراء LEFT JOIN بشكل صريح على هذا النحو



ctx.LeftEntities.SelectMany(x => x.RightEntities.DefaultIfEmpty(), (l, r) => new { Left = l, Right = r })


يمنحنا هذا التحكم في الربط ، كما في النموذج العلائقي. لماذا قد تحتاجه؟ لنفترض أن العميل يحتاج إلى عرض البيانات في شكل جدول ، ويلزم الفرز و / أو ترقيم الصفحات.



لا أحب هذه الفكرة أيضًا ، لكن لكل شخص مراوغاته وحب لا نهاية له في Excel. لذلك سوف نغطي السجادة بالسعال ، والسب بقبضة اليد ، ونواصل العمل. ما التالي بالنسبة لنا؟



FULL JOIN


حسنًا ، لقد فهمت بالفعل ، نحن لا نفكر باستخدام SQL ، بل نفكر في أشياء مثل هذه ، والآن مثل هذا ... اللعنة. كيف تصور هذا؟



بدون tuples ، لا مكان.



بشكل عام ، عندما يتعين علينا العمل مع tuple ، نفقد فهم نوع الاتصال (1-1 ، 1-n ، nn) وبشكل عام ، من الناحية النظرية ، يمكننا عبور الذباب والفيلة. هذا هو السحر الأسود.

دعنا نقوم به!



لنأخذ كثير إلى كثير كأساس.



وبالتالي ، لدينا 3 أنواع من الكيانات



LeftEntity

RightEntity

LeftRight (لتنظيم الاتصال)



LeftRight سأقوم بإنشائها باستخدام مفتاح مركب. لست بحاجة إلى الوصول المباشر إلى هذا الكيان ، فالمراجع الخارجية غير مطلوبة ، لذلك لن أقوم بإنتاج حقول ومؤشرات غير ضرورية.



كنتيجة للاستعلام ، أرغب في الحصول على مجموعة من المجموعات ، والتي ستحتوي على الكيان الأيسر والكيان الأيمن. علاوة على ذلك ، إذا لم يكن للكيان العذراء علاقة بالكيان الصحيح ، فسيكون الكائن الصحيح لاغياً. الشيء نفسه ينطبق على الكيانات اليمنى.



اتضح أن LeftRight لا يناسبنا كمخرج ، ولن تسمح لنا حدوده بإنشاء حزمة بدون أحد الكيانات. إذا كنت تمارس DDD ، فسوف تلاحظ عدم الاتساق.

حتى لو كنت لا تمارس ، فلا يجب عليك ذلك. لنقم بإنشاء نوع جديد لإخراج

LeftRightFull ، والذي لا يزال يحتوي على مرجع للكيانات اليمنى واليسرى.



لذلك، لدينا



اليسار

L1

L2



حق

R1

R2



LeftRight

L2 R2



نريد في الإخراج

L1 ن

L2 R2

n R1



لنبدأ بالاتصال الأيسر



var query = ctx.LeftEntities
    .SelectMany(x => x.RightLinks.DefaultIfEmpty(), (l, lr) => new LeftRightFull
    {
        LeftEntity = l,
        RightEntity = lr.RightEntity
    })




الآن ، لدينا بالفعل

L1 n

L2 R2



ماذا بعد؟ التمسك RightEntity عبر SelectMany؟ حسنًا ، لذلك نحصل على

L1 n R1 n

L1 n R2 L2

L2 R2 R1 n

L2 R2 R2 L2



يمكننا تصفية غير الضرورية (L2 R2 R1 n و L1 n R2 L2) حسب الحالة ، ومع ذلك ، لا يزال من غير الواضح كيفية تحويل L1 n R1 n في

L1 n

n R1



ربما أخذتنا الحفريات إلى السهوب الخطأ. دعنا فقط نربط هذه الاستعلامات المتشابهة بوصلات اليسار للكيانات اليمنى واليسرى عبر الاتحاد.

L1 n

L2 R2

UNION

L2 R2

n R1



هنا قد يكون لديك أسئلة حول الأداء. لن أجيب عليهم ، لأن كل شيء سيعتمد على DBMS المحدد ودقة محسنه.



بدلاً من ذلك ، يمكنك إنشاء عرض باستخدام الاستعلام الذي تريده. لا يعمل Union على EF Core 2 ، لذلك كان علي إنشاء طريقة عرض. ومع ذلك ، أوصي بعدم نسيان ذلك. فجأة ، قررت تنفيذ حذف بسيط للكيانات من خلال علامة IsDeleted. في الرأي ، تحتاج إلى دعم هذا. إذا نسيت ، فمن غير المعروف متى ستتلقى تقرير خطأ.



بالمناسبة ، كيف يتم تنفيذ الحذف الناعم دون إعادة كتابة الكود بالكامل؟ سأتحدث عن هذا في المرة القادمة. ربما شيء آخر.



وداعا للجميع.



All Articles