Table of Contents

مقدمة: تحدي الوصول إلى البيانات

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

فهم نمط المصانع: ما بعد اختلاق الأجسام البسيطة

وفي جوهرها، فإن نمط التصميم الإبداعي يلقي مسؤولية تأجيل الأشياء إلى فئة مصنعية مخصصة، ويقع هذا النمط تحت ثلاثة تغييرات مشتركة: المصانع البسيطة، وطريقة المصانع، والمصانع الخلاصية، وكمية اختزال مستويات الوصول إلى البيانات، فإن مصنع بسيط [FLT:] [المنتج الافتراضي] هي في أغلب الأحيان أكثر الحالات التي تتطور فيها

The primary motive for using a factory in.NET Core data access is to uphold the Open/Closed Principle: classes should be open for extension but closed for modification. By channeling all data access object creation through a factory, you can introduce new implementations (e.g., shifting from Entity Framework Core to Dapper) without touching the business logical.

The Factory Pattern is not a silver bullet, it is most effective when you have a well-defined set of interchangeable data access strategies and need to isolate the creation logical from the rest of the application. In simple monolithic applications with a single data source, the overhead of a factory may not be justified.

]

تصميم المكوث: العقد المشترك بين الوجوه

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

مثال: مستودع عام

public interface IDataRepository<TKey, TEntity> where TEntity : class
{
 Task<IEnumerable<TEntity>> GetAllAsync();
 Task<TEntity?> GetByIdAsync(TKey id);
 Task AddAsync(TEntity entity);
 Task UpdateAsync(TEntity entity);
 Task DeleteAsync(TKey id);
}

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

Interfaces Specialized for Advanced Scenarios

في مشاريع العالم الحقيقي، قد تحتاج إلى أساليب مستودع تتجاوز نطاق الوحدويات الأساسية للوحدة، مثل الاستفسارات الممهدة، أو التصفير، أو التجميع، والنظر في تحديد وصلات بينية منفصلة لعمليات القراءة فقط وكتابة فقط لمتابعة مبدأ الفصل بين الجوانب، على سبيل المثال:

public interface IDataReader<TEntity>
{
 Task<IEnumerable<TEntity>> QueryAsync(Expression<Func<TEntity, bool>> predicate);
 Task<TEntity?> GetByIdAsync(int id);
}

public interface IDataWriter<TEntity>
{
 Task InsertAsync(TEntity entity);
 Task UpdateAsync(TEntity entity);
 Task DeleteAsync(int id);
}

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

تطبيق صفات الوصول إلى البيانات

وبعد تحديد الوصلات البينية، تُنشئ عمليات تنفيذ ملموسة لكل تكنولوجيا من تكنولوجيا الوصول إلى البيانات، وفيما يلي أمثلة تستخدم إطار عمل للكيانات الأساسية ] و]Dapper]، أثنان من أكثر الأطر شيوعاً للحصول على البيانات الأساسية.

الإطار الأساسي للهيئة

public class EfDataRepository : IDataRepository
{
 private readonly AppDbContext _context;

 public EfDataRepository(AppDbContext context)
 {
 _context = context;
 }

 public async Task<IEnumerable<DataItem>> GetAllAsync()
 {
 return await _context.Set<DataItem>().AsNoTracking().ToListAsync();
 }

 public async Task<DataItem?> GetByIdAsync(int id)
 {
 return await _context.Set<DataItem>().FindAsync(id);
 }

 // Additional methods omitted for brevity
}

Notice that ] expects an ] instance, which in a.NET Core application is typically injected via the DI container. This aligns with the Factory Pattern: the factory will need access to the DI container to resolve such dependencies.

دال - تنفيذ خطة

public class DapperDataRepository : IDataRepository
{
 private readonly IDbConnection _connection;
 private readonly string _connectionString;

 public DapperDataRepository(IConfiguration configuration)
 {
 _connectionString = configuration.GetConnectionString("DefaultConnection");
 _connection = new SqlConnection(_connectionString);
 }

 public async Task<IEnumerable<DataItem>> GetAllAsync()
 {
 var sql = "SELECT * FROM DataItems";
 return await _connection.QueryAsync<DataItem>(sql);
 }

 public async Task<DataItem?> GetByIdAsync(int id)
 {
 var sql = "SELECT * FROM DataItems WHERE Id = @Id";
 return await _connection.QueryFirstOrDefaultAsync<DataItem>(sql, new { Id = id });
 }
}

وينفذ كلا التنفيذين نفس العقد ولكنهما يستخدمان ميكانيكيين مختلفين تماماً، وسيقرر المصنع أيهما يقوم على الفور على أساس ظروف العمل.

بناء المصنع: من بسيط إلى خلاصة

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

مصنع بسيط ثابت (المصادرة - الجريفن)

public static class DataRepositoryFactory
{
 public static IDataRepository Create(IServiceProvider serviceProvider, string provider)
 {
 return provider switch
 {
 "EntityFramework" => serviceProvider.GetRequiredService<EfDataRepository>(),
 "Dapper" => ActivatorUtilities.CreateInstance<DapperDataRepository>(serviceProvider),
 _ => throw new NotSupportedException($"Data provider '{provider}' is not supported.")
 };
 }
}

This factory uses the to immediateiate types that have dependencies registered in the DI container. is resolved directly because it is already registered (along with ). is created using which handles constructor injection without requiring manual registration of the reposit

مصنع خلاص للأسر المتعددة المنتجات

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

public interface IDataAccessFactory
{
 IDataRepository CreateOrderRepository();
 IDataRepository CreateInventoryRepository();
 // etc.
}

public class EfDataAccessFactory : IDataAccessFactory
{
 private readonly AppDbContext _context;
 public EfDataAccessFactory(AppDbContext context) => _context = context;

 public IDataRepository CreateOrderRepository() => new EfOrderRepository(_context);
 public IDataRepository CreateInventoryRepository() => new EfInventoryRepository(_context);
}

public class DapperDataAccessFactory : IDataAccessFactory
{
 private readonly string _connectionString;
 public DapperDataAccessFactory(IConfiguration configuration) => _connectionString = configuration.GetConnectionString("DefaultConnection");

 public IDataRepository CreateOrderRepository() => new DapperOrderRepository(_connectionString);
 public IDataRepository CreateInventoryRepository() => new DapperInventoryRepository(_connectionString);
}

ومصنع الخلايا أكثر قوة، ولكنه أكثر كثافة أيضا، وتحفظه على التطبيقات التي تحتاج إلى تبادل مجموعات كاملة من البيانات (مثل استبدال جميع مستودعات إطار الكيانين بمخازن دابر) في آن واحد، بدلا من تصوير عمليات التنفيذ الفردية للزهور.

إدماج المصنع في حقن الإعالة الأساسية

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

التسجيل في البرنامج (أو بدء التشغيل)

builder.Services.AddTransient<EfDataRepository>();
builder.Services.AddTransient<IDataRepository>(sp =>
{
 var config = sp.GetRequiredService<IConfiguration>();
 var provider = config.GetValue<string>("DataProvider");
 return DataRepositoryFactory.Create(sp, provider);
});

وفي هذا التسجيل، يسجل الخرسانة ] بشكل عابر (حتى يمكن لحاوية الاستنشاق أن تحلها داخل المصنع) ويستخدم مندوب مصنع يقرأ ] القيمة من والمندوبون إلى المصنع الثابت، والآن أي مستهلك يُحقن [ي التنفيذ الصحيح].

استخدام الخدمات المسمومة للدعم المتعدد التوريدات

If your application needs multiple repositories using different providers concur (e.g., one for historical data using Dapper, and one for real-time data using Entity Framework), you can register named factory methods or use a dictionary pattern.

builder.Services.AddSingleton<IDataRepositoryFactory>(sp =>
{
 var config = sp.GetRequiredService<IConfiguration>();
 var providers = config.GetSection("DataProviders").Get<Dictionary<string, string>>();
 return new DataRepositoryFactory(sp, providers);
});

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

الاستحقاقات العالمية الحقيقية والسيناريوهات

فلنبحث الحالات الملموسة التي تشرق فيها أنماط المصانع في طبقات الوصول الأساسية للبيانات.

1- الاختبار والاختبار

(ب) أن يُصبح منطق عمل الوحدة ثلاثياً عندما يمكن أن تحل محل مستودع للجرائم، ويمكن تشكيل المصنع في اختبارات الإعداد لإعادة رصيف أو تنفيذ داخلي، مثلاً أثناء اختبارات الاندماج، وضع مفتاح [للعملية] ، وله مصنع يعيد مدعوم بـ [27]

2- التطبيقات المتعددة الأطقم

وقد يحتاج كل مستأجر إلى تكنولوجيا مختلفة لمخزون البيانات بسبب الترخيص أو القيود التي تولدها أو التوزيع الجغرافي، ويمكن للمصنع أن يدرس البيانات الوصفية للمستأجر في وقت غير مناسب وأن يعجل باستخدام مستودع مناسب - أقل من مستأجر واحد لشبكة SQL Server عبر إطار الكيان، واستخدامات أخرى لمحطة بريدجستير فيل عن طريق دابر، وثالث استخدامات لـه لـ Azure Cosmos DB.

3- نظارات المرأة والهجرة التدريجية

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

اختبار مركز الوصول إلى البيانات المستخرجة

مصنع مصمم جيداً يُجري اختبارات مباشرة يمكنك إنشاء مصنع اختبار يُعيد تنفيذات أو نسخ خفية من الوزن في مستودعات بياناتك

public class TestDataRepositoryFactory
{
 public static IDataRepository CreateInMemory()
 {
 return new InMemoryDataRepository();
 }
}

(ب) ) ببساطة يحمل وينفذ الواجهة باستخدام العمليات داخل الذاكرة، ويمكن أن تقوم اختبارات الوحدة بعد ذلك بتأهيل دوائر الأعمال مع هذا المصنع الاختبار دون اشتراط وجود وصلة لقاعدة البيانات أو أطر السخرة، ولا تزال اختبارات التكامل تستخدم المصنع الحقيقي للتحقق من قاعدة بيانات الاختبار.

أفضل الممارسات والخيوط المشتركة

لا تبالغ في الخلاص

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

Avoid Stringly-Typed Providers

(أ) استخدام الخيوط السحرية لأسماء مقدمي الخدمات (مثل ] أو ) هو قذف، بدلاً من ذلك، تعريف تعداد أو استخدام جسم تشكيلي ذي أنواع قوية.

public enum DataProvider
{
 EntityFramework,
 Dapper,
 InMemory
}

العمر الوظيفي بحذر

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

النظر في استخدام مصنع مع سجل

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

مقارنة مع أنماط بديلة

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

  • Strategy Pattern ] — similar to Factory, but the focus is on encapsulating algorithms (e.g., different sorting or filtering strategies) rather than object creation. The Factory Pattern is a creational pattern; the Strategy Pattern is behavioral, they can complement each other: a factory might return a strategy.
  • Decorator Pattern ] – Useful for added cross-cutting concerns (caching, logging, retry) to a repository without modifying its code. The Factory can decorate the repository it creates, combining both patterns.
  • Repository Pattern (without factory) - Directly injecting a concrete repository via DI works for simple applications. The Factory adds flexibility when the concrete type varies.

الموارد الخارجية والقراءة الإضافية

To deepen your understanding of the Factory Pattern and.NET Core data access, refer to the following authoritative sources:

الاستنتاج: بناء التغيير

(ب) توفر خطة المصانع طريقة مُنضبطة لإدارة التباين في طبقات الوصول إلى البيانات، مما يتيح لك تبادل المستودعات، واعتماد تكنولوجيات جديدة، واختبار منطق الأعمال في عزلة، وفي إطار نظام المعلومات الخاص بك، تجمع بين نمط المصنع و الحاوية الموجودة في نظام المعلومات التصميمية، وتنتج بنية نظيفة وقابلة للاستمرار تحترم مبادئ SOLID، وتبدأ في تحديد واجهة، وتطبق صفين محددين، وتنشئ سجلاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاًاً.