Table of Contents
The Factory Pattern is a widely used design pattern in software development that provides a way to create objects without specifying the exact class of object that will be created. In the context of .NET Core, this pattern is particularly useful for abstracting data access layers, making applications more flexible and easier to maintain.
Understanding the Factory Pattern
The Factory Pattern involves creating a factory class responsible for instantiating objects. This approach encapsulates the creation logic and allows for dynamic object creation based on configuration or runtime conditions. It promotes loose coupling between components and enhances testability.
Applying the Factory Pattern in .NET Core
In .NET Core, the Factory Pattern can be used to abstract different data access implementations, such as Entity Framework, Dapper, or even in-memory data providers. This abstraction enables switching between data sources with minimal code changes.
Defining the Data Access Interface
First, define a common interface for data operations. For example:
public interface IDataRepository
{
Task GetByIdAsync(int id);
}
Implementing Data Access Classes
Next, create concrete classes that implement this interface for different data sources:
public class EfDataRepository : IDataRepository
{
private readonly AppDbContext _context;
public EfDataRepository(AppDbContext context)
{
_context = context;
}
public async Task<IEnumerable<DataItem>> GetAllAsync()
{
return await _context.DataItems.ToListAsync();
}
public async Task<DataItem> GetByIdAsync(int id)
{
return await _context.DataItems.FindAsync(id);
}
}
public class DapperDataRepository : IDataRepository
{
private readonly IDbConnection _connection;
public DapperDataRepository(IDbConnection connection)
{
_connection = connection;
}
public async Task<IEnumerable<DataItem>> GetAllAsync()
{
var sql = "SELECT * FROM DataItems";
return await _connection.QueryAsync(sql);
}
public async Task<DataItem> GetByIdAsync(int id)
{
var sql = "SELECT * FROM DataItems WHERE Id = @Id";
return await _connection.QueryFirstOrDefaultAsync(sql, new { Id = id });
}
}
Creating the Factory
The factory class decides which data access implementation to instantiate based on configuration or context:
public static class DataRepositoryFactory
{
public static IDataRepository CreateRepository(string provider)
{
if (provider == "EntityFramework")
{
// Assume dependency injection provides context
var context = new AppDbContext();
return new EfDataRepository(context);
}
else if (provider == "Dapper")
{
var connection = new SqlConnection("YourConnectionString");
return new DapperDataRepository(connection);
}
else
{
throw new NotSupportedException("Provider not supported");
}
}
}
Benefits of Using the Factory Pattern
- Decouples data access implementation from business logic
- Facilitates switching data sources with minimal code changes
- Improves testability by allowing mock implementations
- Enhances maintainability and scalability of the application
By implementing the Factory Pattern in .NET Core, developers can create flexible, maintainable, and scalable data access layers that adapt to changing requirements and technologies.