PowerCSharp.Core serves as the foundational core of the PowerCSharp ecosystem, providing centralized interfaces, models, and base functionality for all PowerCSharp packages. This package has been refactored to provide a clean, dependency-free foundation with improved architectural separation and better maintainability.
Recent Improvements (v0.3.0):
- Centralized Interface Design: All interfaces now centralized for better consistency
- Dependency-Free Core: Removed external dependencies for maximum compatibility
- Enhanced Architecture: Improved namespace organization and separation of concerns
- Better Documentation: Comprehensive API documentation and usage examples
All interfaces in PowerCSharp are centralized in the Core package to maintain:
- Single source of truth for contracts and abstractions
- Consistent namespace organization across packages
- Easy dependency management across the ecosystem
- Clear architectural boundaries between packages
PowerCSharp.Core/
├── Interfaces/
│ ├── Extensions/
│ │ ├── Configuration/
│ │ │ └── IAppOptions.cs
│ │ └── Linq/
│ │ ├── IDynamicFilterProvider.cs
│ │ └── IDynamicOrderProvider.cs
│ └── Models/
│ (reserved for future model classes)
Namespace: PowerCSharp.Core.Interfaces.Extensions.Configuration
Represents the interface for application options, providing a standardized way to define configuration sections.
public interface IAppOptions
{
/// <summary>
/// The configuration section path.
/// </summary>
string ConfigSectionPath { get; }
}When implementing IAppOptions:
- Return a meaningful configuration path that matches your appsettings.json structure
- Use consistent naming for configuration sections
- Include validation for required configuration properties
public class DatabaseOptions : IAppOptions
{
public string ConfigSectionPath => "Database";
public string ConnectionString { get; set; }
public int Timeout { get; set; }
public bool EnableRetry { get; set; }
public void Validate()
{
if (string.IsNullOrEmpty(ConnectionString))
throw new InvalidOperationException("Database connection string is required");
if (Timeout <= 0)
throw new InvalidOperationException("Database timeout must be greater than 0");
}
}using Microsoft.Extensions.Configuration;
using PowerCSharp.Extensions.Configuration;
public class Startup
{
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
var dbOptions = configuration.GetOptions<DatabaseOptions>("Database");
dbOptions.Validate();
services.Configure<DatabaseOptions>(configuration.GetSection("Database"));
}
}Namespace: PowerCSharp.Core.Interfaces.Extensions.Linq
Provides a flexible way to define dynamic filtering criteria for collections using compiled predicates.
public interface IDynamicFilterProvider<T>
{
/// <summary>
/// Sets delegate to use to filter
/// </summary>
/// <param name="filter">A predicate that takes an object of type T and returns a bool.</param>
void SetFilter(Func<T, bool> filter);
/// <summary>
/// Gets the delegate to be used to filter the T
/// </summary>
/// <returns>A predicate that takes an object of type T and returns a bool.</returns>
Func<T, bool> GetFilter();
}When implementing IDynamicFilterProvider<T>:
- Handle null filters by providing a default predicate that returns true
- Consider thread safety if the provider will be used in multi-threaded scenarios
- Provide clear error messages for invalid filter expressions
public class DynamicFilterProvider<T> : IDynamicFilterProvider<T>
{
private Func<T, bool> _filter;
private readonly object _lock = new object();
public void SetFilter(Func<T, bool> filter)
{
lock (_lock)
{
_filter = filter ?? (entity => true);
}
}
public Func<T, bool> GetFilter()
{
lock (_lock)
{
return _filter ?? (entity => true);
}
}
}public class ExpressionFilterProvider<T> : IDynamicFilterProvider<T>
{
private Func<T, bool> _filter;
public void SetFilter(Func<T, bool> filter)
{
_filter = filter ?? (entity => true);
}
public Func<T, bool> GetFilter()
{
return _filter ?? (entity => true);
}
public void SetFilterFromString(string expression)
{
try
{
var lambdaExpression = DynamicExpressionParser.ParseLambda(
typeof(T),
typeof(bool),
expression);
_filter = (Func<T, bool>)lambdaExpression.Compile();
}
catch
{
// If parsing fails, use a filter that includes all entities
_filter = entity => true;
}
}
}Namespace: PowerCSharp.Core.Interfaces.Extensions.Linq
Enables dynamic ordering of collections using multiple sorting criteria with ascending/descending options.
public interface IDynamicOrderProvider<T>
{
/// <summary>
/// Sets delegate to use for ordering
/// </summary>
/// <param name="delegates">A list of predicates that takes an object of type T and returns an object.</param>
void SetOrderDelegates(List<(Func<T, object>, bool)> delegates);
/// <summary>
/// Gets the list of delegates to be used to order the T
/// </summary>
/// <returns>A list of predicates that takes an object of type T and returns an object.</returns>
List<(Func<T, object>, bool)> GetOrderDelegates();
}When implementing IDynamicOrderProvider<T>:
- Handle null delegate lists by returning an empty list
- Validate delegate functions before adding them to the list
- Consider thread safety for concurrent access
- Provide clear error handling for invalid property access
public class DynamicOrderProvider<T> : IDynamicOrderProvider<T>
{
private List<(Func<T, object>, bool)> _orderDelegates;
private readonly object _lock = new object();
public void SetOrderDelegates(List<(Func<T, object>, bool)> delegates)
{
lock (_lock)
{
_orderDelegates = delegates ?? new List<(Func<T, object>, bool)>();
}
}
public List<(Func<T, object>, bool)> GetOrderDelegates()
{
lock (_lock)
{
return _orderDelegates ?? new List<(Func<T, object>, bool)>();
}
}
public void AddOrderDelegate(Func<T, object> keySelector, bool descending = false)
{
if (keySelector == null) return;
lock (_lock)
{
_orderDelegates ??= new List<(Func<T, object>, bool)>();
_orderDelegates.Add((keySelector, descending));
}
}
public void ClearOrderDelegates()
{
lock (_lock)
{
_orderDelegates?.Clear();
}
}
}public class ExpressionOrderProvider<T> : IDynamicOrderProvider<T>
{
private List<(Func<T, object>, bool)> _orderDelegates;
public void SetOrderDelegates(List<(Func<T, object>, bool)> delegates)
{
_orderDelegates = delegates ?? new List<(Func<T, object>, bool)>();
}
public List<(Func<T, object>, bool)> GetOrderDelegates()
{
return _orderDelegates ?? new List<(Func<T, object), bool)>();
}
public void SetOrderFromString(string orderExpression)
{
var delegates = new List<(Func<T, object>, bool)>();
if (string.IsNullOrEmpty(orderExpression))
{
SetOrderDelegates(delegates);
return;
}
var clauses = orderExpression.Split(',');
foreach (var clause in clauses)
{
var parts = clause.Trim().Split(' ');
if (parts.Length == 0) continue;
string propertyName = parts[0];
bool descending = parts.Length > 1 && parts[1].Equals("DESC", StringComparison.OrdinalIgnoreCase);
try
{
var keySelector = CreateKeySelector(propertyName);
delegates.Add((keySelector, descending));
}
catch
{
// Skip invalid property names
continue;
}
}
SetOrderDelegates(delegates);
}
private Func<T, object> CreateKeySelector(string propertyName)
{
var param = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(param, propertyName);
var converted = Expression.Convert(property, typeof(object));
var lambda = Expression.Lambda<Func<T, object>>(converted, param);
return lambda.Compile();
}
}public class QueryBuilder<T>
{
private readonly IDynamicFilterProvider<T> _filterProvider;
private readonly IDynamicOrderProvider<T> _orderProvider;
public QueryBuilder(
IDynamicFilterProvider<T> filterProvider,
IDynamicOrderProvider<T> orderProvider)
{
_filterProvider = filterProvider;
_orderProvider = orderProvider;
}
public QueryBuilder<T> Where(string filterExpression)
{
if (_filterProvider is ExpressionFilterProvider<T> expressionFilter)
{
expressionFilter.SetFilterFromString(filterExpression);
}
return this;
}
public QueryBuilder<T> OrderBy(string orderExpression)
{
if (_orderProvider is ExpressionOrderProvider<T> expressionOrder)
{
expressionOrder.SetOrderFromString(orderExpression);
}
return this;
}
public IEnumerable<T> Execute(IEnumerable<T> source)
{
var filtered = source.Where(_filterProvider.GetFilter());
var ordered = ApplyOrdering(filtered);
return ordered;
}
private IEnumerable<T> ApplyOrdering(IEnumerable<T> source)
{
var orderDelegates = _orderProvider.GetOrderDelegates();
if (orderDelegates == null || orderDelegates.Count == 0)
return source;
IOrderedEnumerable<T> ordered = null;
foreach (var (keySelector, descending) in orderDelegates)
{
ordered = ordered == null
? descending ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector)
: descending ? ordered.ThenByDescending(keySelector) : ordered.ThenBy(keySelector);
}
return ordered ?? source;
}
}public class ConfigurationManager
{
private readonly IConfiguration _configuration;
public ConfigurationManager(IConfiguration configuration)
{
_configuration = configuration;
}
public T GetOptions<T>() where T : class, IAppOptions
{
var options = _configuration.GetOptions<T>(typeof(T).Name);
// Validate if the options type has a Validate method
if (options is IValidatableOptions validatable)
{
validatable.Validate();
}
return options;
}
}
public interface IValidatableOptions
{
void Validate();
}
public class DatabaseOptions : IAppOptions, IValidatableOptions
{
public string ConfigSectionPath => "Database";
public string ConnectionString { get; set; }
public int Timeout { get; set; }
public void Validate()
{
if (string.IsNullOrWhiteSpace(ConnectionString))
throw new InvalidOperationException("Database connection string is required");
if (Timeout <= 0)
throw new InvalidOperationException("Database timeout must be greater than 0");
}
}- Single Responsibility: Each interface should have one clear purpose
- Extensibility: Design interfaces that can be extended without breaking changes
- Testability: Interfaces should be easy to mock and test
- Documentation: Provide comprehensive XML documentation for all members
- Null Safety: Always handle null parameters gracefully
- Thread Safety: Consider thread safety for shared providers
- Error Handling: Provide meaningful error messages
- Performance: Avoid unnecessary allocations and computations
- Dependency Injection: Register implementations in DI containers
- Factory Pattern: Use factories to create configured providers
- Builder Pattern: Use builders for complex query construction
- Strategy Pattern: Switch implementations based on requirements
- Initial release with centralized interfaces
- IAppOptions for configuration management
- IDynamicFilterProvider for dynamic filtering
- IDynamicOrderProvider for dynamic ordering
- Comprehensive documentation and examples
- IValidator - Standardized validation interface
- ICacheProvider - Caching abstraction
- ILoggerProvider - Logging abstraction
- IMapper<TSource, TDestination> - Object mapping interface
- Result - Operation result wrapper
- PagedResult - Paginated query results
- ApiError - Standardized error model
- AuditEvent - Audit logging model
For more information, see: