مشكلة في استخدام LINQ إلى SQL مع DataContext واحد لكل إجراء ذري

StackOverflow https://stackoverflow.com/questions/259524

سؤال

لقد بدأت باستخدام Linq to SQL في نظام (مثل DDD قليلاً) والذي يبدو (مبسطًا للغاية) مثل هذا:

public class SomeEntity // Imagine this is a fully mapped linq2sql class.
{
    public Guid SomeEntityId { get; set; }
    public AnotherEntity Relation { get; set; }
}

public class AnotherEntity // Imagine this is a fully mapped linq2sql class.
{
    public Guid AnotherEntityId { get; set; }
}

public interface IRepository<TId, TEntity>
{
    Entity Get(TId id);
}

public class SomeEntityRepository : IRepository<Guid, SomeEntity>
{
    public SomeEntity Get(Guid id)
    {
        SomeEntity someEntity = null;
        using (DataContext context = new DataContext())
        {
            someEntity = (
                from e in context.SomeEntity
                where e.SomeEntityId == id
                select e).SingleOrDefault<SomeEntity>();
        }

        return someEntity;
    }
}

الآن، لدي مشكلة.عندما أحاول استخدام SomeEntityRepository مثل هذا

public static class Program
{
    public static void Main(string[] args)
    {
        IRepository<Guid, SomeEntity> someEntityRepository = new SomeEntityRepository();
        SomeEntity someEntity = someEntityRepository.Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"));
        Console.WriteLine(someEntity.SomeEntityId);
        Console.WriteLine(someEntity.Relation.AnotherEntityId);
    }
 }

كل شيء يعمل بشكل جيد حتى يصل البرنامج إلى خط الكتابة الأخير، لأنه يلقي ObjectDisposedException, لأن DataContext لم يعد موجودًا.

أرى المشكلة الفعلية، ولكن كيف يمكنني حلها؟أعتقد أن هناك العديد من الحلول، لكن أياً من الحلول التي فكرت بها حتى الآن لن يكون جيداً في حالتي.

  • ابتعد عن نمط المستودع واستخدم DataContext الجديد لكل جزء ذري من العمل.
    • أنا حقا لا أريد أن أفعل هذا.والسبب هو أنني لا أريد أن تكون التطبيقات على علم بالمستودع.سبب آخر هو أنني لا أعتقد أن جعل عناصر linq2sql COM مرئية سيكون أمرًا جيدًا.
    • أيضا، أعتقد أن القيام بذلك context.SubmitChanges() ربما سأرتكب أكثر بكثير مما كنت أنوي فعله.
  • تحديد DataLoadOptions لجلب العناصر ذات الصلة.
    • نظرًا لأنني أريد أن تقوم طبقة منطق الأعمال الخاصة بي بالرد مع بعض الكيانات في بعض الحالات، فلا أعرف الخصائص الفرعية التي يتعين عليهم استخدامها.
  • تعطيل التحميل البطيء/التحميل المتأخر لجميع الخصائص.
    • ليس خيارًا، نظرًا لوجود عدد لا بأس به من الجداول وهي مرتبطة بشكل كبير.قد يتسبب هذا في حدوث الكثير من حركة المرور غير الضرورية وتحميل قاعدة البيانات.
  • ذكرت بعض المنشورات على الإنترنت أن استخدام .Single() من شأنه أن يساعد.
    • على ما يبدو أنه لا ...

هل هناك طريقة لحل هذا البؤس؟

بالمناسبة:قررنا استخدام Linq t0 SQL لأنه حل ORM خفيف الوزن نسبيًا ومضمن في إطار عمل .NET وVisual Studio.إذا كان .NET Entity Framework مناسبًا بشكل أفضل لهذا النمط، فقد يكون هناك خيار للتبديل إليه.(نحن لم نصل إلى هذا الحد في التنفيذ بعد).

هل كانت مفيدة؟

المحلول

لدى ريك ستراهل مقالة لطيفة حول إدارة دورة حياة DataContext هنا: http://www.west-wind.com/weblog/posts/246222.aspx.

في الأساس، يعد نهج العمل الذري جيدًا من الناحية النظرية ولكنك ستحتاج إلى الاحتفاظ بـ DataContext الخاص بك لتتمكن من تتبع التغييرات (وجلب الأطفال) في كائنات البيانات الخاصة بك.

أنظر أيضا: مثيل متعدد/مفرد من Linq إلى SQL DataContext و LINQ إلى SQL - أين يعيش DataContext الخاص بك؟.

نصائح أخرى

عليك إما:

1) اترك السياق مفتوحًا لأنك لم تقرر بشكل كامل البيانات التي سيتم استخدامها (المعروف أيضًا باسم التحميل البطيء).

أو 2) اسحب المزيد من البيانات عند التحميل الأولي إذا كنت تعلم أنك ستحتاج إلى تلك الخاصية الأخرى.

شرح الأخير: هنا

لست متأكدًا من أنه يتعين عليك التخلي عن المستودع إذا اخترت وحدات العمل الذرية.أستخدم كليهما، على الرغم من أنني أعترف بتجاهل عمليات التحقق من التزامن المتفائلة نظرًا لأنها لا تعمل في طبقات على أي حال (بدون استخدام طابع زمني أو أي اتفاقية أخرى مطلوبة).ما انتهى بي الأمر هو مستودع يستخدم DataContext ويرميه بعيدًا عند الانتهاء.

يعد هذا جزءًا من مثال Silverlight غير ذي صلة، لكن الأجزاء الثلاثة الأولى توضح كيف أستخدم نمط المستودع مع LINQ إلى سياق SQL، FWIW: http://www.dimebrain.com/2008/09/linq-wcf-silver.html

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

إذا تم منح المتصل الاقتران اللازم لاستخدام خاصية .Relation، فقد يحدد المتصل أيضًا DataLoadOptions.

DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Entity>(e => e.Relation);
SomeEntity someEntity = someEntityRepository
  .Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"),
  loadOptions);

//

using (DataContext context = new DataContext())
{
  context.LoadOptions = loadOptions;

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

1) اجعل DataContext متغير عضو في مستودعك.نعم، هذا يعني أنه يجب على مستودعك الآن تنفيذ IDisposable وعدم تركه مفتوحًا...ربما شيء تريد تجنب الاضطرار إلى القيام به، لكنني لم أجده غير مريح.

2) أضف بعض الطرق إلى مستودعك مثل هذا:

public SomeEntityRepository WithSomethingElseTheCallerMightNeed()
{
 dlo.LoadWith<SomeEntity>(se => se.RelatedEntities);
 return this; //so you can do method chaining
}

ثم يبدو المتصل الخاص بك كما يلي:

SomeEntity someEntity = someEntityRepository.WithSomethingElseTheCallerMightNeed().Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"));

تحتاج فقط إلى التأكد من أنه عندما يصل المستودع الخاص بك إلى قاعدة البيانات، فإنه يستخدم خيارات تحميل البيانات المحددة في تلك الطرق المساعدة...في حالتي، يتم الاحتفاظ بـ "dlo" كمتغير عضو، ثم يتم ضبطه قبل الوصول إلى قاعدة البيانات.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top