add support for Dynamic Entity Registration in DbContext by Provides IXEntityRegisterar interface and XBaseEntityRegisterar as an abstract Implementation ...
all other requirements handled inside Module.
This commit is contained in:
@@ -12,6 +12,7 @@ using xDataService.Constants;
|
||||
using xDataService.Extensions;
|
||||
using xDataService.Helpers;
|
||||
using xDataService.Models;
|
||||
using xDataService.Providers;
|
||||
using xExceptions.Constants;
|
||||
|
||||
namespace xDataService.DI
|
||||
@@ -189,6 +190,10 @@ namespace xDataService.DI
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Register Handler Service ...
|
||||
source.AddSingleton<XEntityRegisterarHandlerService>();
|
||||
|
||||
//
|
||||
// Configure Database Descriptor ...
|
||||
descriptor.Configure(source);
|
||||
|
||||
+109
-6
@@ -1,5 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Configuration;
|
||||
using xDataService.Helpers;
|
||||
using xDataService.Providers;
|
||||
|
||||
namespace xDataService.Db
|
||||
{
|
||||
@@ -9,7 +14,13 @@ namespace xDataService.Db
|
||||
/// </summary>
|
||||
public abstract class XDbContext : DbContext
|
||||
{
|
||||
//
|
||||
#region Properties ...
|
||||
private readonly string database;
|
||||
private readonly XDataServiceConfiguration config;
|
||||
private readonly string[] dynamicEntityRegisterarsNames;
|
||||
private readonly XEntityRegisterarHandlerService dynamicEntityHandlers;
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Entities ...
|
||||
@@ -18,10 +29,13 @@ namespace xDataService.Db
|
||||
#endregion
|
||||
|
||||
//
|
||||
// Constructor ...
|
||||
#region Constructor ...
|
||||
public XDbContext(
|
||||
string database,
|
||||
DbContextOptions options,
|
||||
XDataServiceConfiguration config
|
||||
XDataServiceConfiguration config,
|
||||
XEntityRegisterarHandlerService dynamicEntityHandlers,
|
||||
string[] dynamicEntityRegisterarsNames = null
|
||||
) : base(options)
|
||||
{
|
||||
//
|
||||
@@ -32,11 +46,62 @@ namespace xDataService.Db
|
||||
}
|
||||
catch { }
|
||||
|
||||
//
|
||||
this.config = config;
|
||||
this.database = database;
|
||||
this.dynamicEntityHandlers = dynamicEntityHandlers;
|
||||
this.dynamicEntityRegisterarsNames = dynamicEntityRegisterarsNames;
|
||||
|
||||
//
|
||||
// Handling DbSets of Dynamic Entities ...
|
||||
if (!dynamicEntityHandlers.IsNull())
|
||||
{
|
||||
//
|
||||
// Configure XEntityRegisterarHandlerService ...
|
||||
dynamicEntityHandlers.HandleRegisteredEntities();
|
||||
|
||||
//
|
||||
// Loop through Entity Types ...
|
||||
foreach (var entityType in dynamicEntityHandlers.GetEntityTypes(dynamicEntityRegisterarsNames))
|
||||
{
|
||||
//
|
||||
try
|
||||
{
|
||||
//
|
||||
// Use reflection to call Set<T>()
|
||||
var method = typeof(XDbContext)
|
||||
.GetMethod(nameof(XDbContext.Set), Type.EmptyTypes)
|
||||
.MakeGenericMethod(entityType);
|
||||
if (!method.IsNull())
|
||||
{
|
||||
method.Invoke(this, null);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Extract Current Database Configuration ...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XDataBaseConfiguration GetDatabaseConfiguration()
|
||||
{
|
||||
//
|
||||
XDataBaseConfiguration result =
|
||||
config.Databases.FirstOrDefault(db => string
|
||||
.Equals(db.Key, database, StringComparison.InvariantCultureIgnoreCase)).Value;
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare Fluent API ...
|
||||
#region Overrides ...
|
||||
//
|
||||
// Prepare Model Configs ...
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
//
|
||||
@@ -46,15 +111,37 @@ namespace xDataService.Db
|
||||
// Automatically apply all IEntityTypeConfiguration<T> in this assembly
|
||||
modelBuilder.ApplyConfigurationsFromAssembly(typeof(XDbContext).Assembly);
|
||||
|
||||
//
|
||||
// Extract Entity Container Assemblies ...
|
||||
var entityAssemblies = XDataServiceHelper.ExtractEntityAssemblies();
|
||||
foreach(var assembly in entityAssemblies)
|
||||
{
|
||||
modelBuilder.ApplyConfigurationsFromAssembly(assembly);
|
||||
}
|
||||
|
||||
//
|
||||
// Check Dynamic Registered Service is Exists or not ...
|
||||
if (!dynamicEntityHandlers.IsNull())
|
||||
{
|
||||
//
|
||||
// Set Entity of Dynamically Registered Entities ...
|
||||
foreach (var entityType in dynamicEntityHandlers.GetEntityTypes(dynamicEntityRegisterarsNames))
|
||||
{
|
||||
modelBuilder.Entity(entityType);
|
||||
}
|
||||
|
||||
//
|
||||
// Configure Entity using Provided Registerar ...
|
||||
dynamicEntityHandlers
|
||||
.HandleApplyRegisteredEntitiesConfigurations(modelBuilder, dynamicEntityRegisterarsNames);
|
||||
}
|
||||
|
||||
//
|
||||
// Since this class inherit from Identity Db Context,
|
||||
// we have to pass this to Building Identity Models ...
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
|
||||
//
|
||||
public abstract void OnXModelCreating(ModelBuilder modelBuilder);
|
||||
|
||||
//
|
||||
// Prepare Configuring ...
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
@@ -87,8 +174,24 @@ namespace xDataService.Db
|
||||
//
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Abstracts ...
|
||||
//
|
||||
public abstract void OnXModelCreating(ModelBuilder modelBuilder);
|
||||
|
||||
//
|
||||
public abstract void OnXConfiguring(DbContextOptionsBuilder optionsBuilder);
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Dynamic DbSet(s) ...
|
||||
public override DbSet<TEntity> Set<TEntity>()
|
||||
where TEntity : class
|
||||
{
|
||||
return base.Set<TEntity>();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -63,5 +63,38 @@ namespace xDataService.Extensions {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a Type is XBaseEntity or not ...
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsXEntity(this Type source)
|
||||
{
|
||||
//
|
||||
var result = !source.IsNull();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
var baseType = source.BaseType;
|
||||
while(!baseType.IsNull() && baseType != typeof(object))
|
||||
{
|
||||
//
|
||||
if (baseType.IsGenericType &&
|
||||
baseType.GetGenericTypeDefinition() == typeof(XBaseEntity<>))
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using GraphQL;
|
||||
using GraphQL.Server;
|
||||
using GraphQL.Server.Ui.Playground;
|
||||
@@ -1245,6 +1248,41 @@ namespace xDataService.Helpers
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Entity Assembly Detector ...
|
||||
/// <summary>
|
||||
/// Extract Assemblies which Contains Entity ...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<Assembly> ExtractEntityAssemblies()
|
||||
{
|
||||
//
|
||||
var result = new List<Assembly>();
|
||||
|
||||
//
|
||||
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in allAssemblies)
|
||||
{
|
||||
//
|
||||
try
|
||||
{
|
||||
//
|
||||
bool hasEntity = assembly
|
||||
.GetExportedTypes()
|
||||
.Any(t => t.IsXEntity());
|
||||
if (hasEntity)
|
||||
{
|
||||
result.Add(assembly);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
//
|
||||
#region Private ...
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// an Interface to Intruduce Entities for Dynamic Registration ...
|
||||
/// </summary>
|
||||
public interface IXEntityRegisterar
|
||||
{
|
||||
/// <summary>
|
||||
/// Database Name ...
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns all entity types this plugin wants registered in the DbContext
|
||||
/// </summary>
|
||||
IEnumerable<Type> GetEntityTypes();
|
||||
|
||||
/// <summary>
|
||||
/// Optional: Apply additional per-entity configuration
|
||||
/// </summary>
|
||||
void ConfigureEntities(ModelBuilder modelBuilder);
|
||||
|
||||
/// <summary>
|
||||
/// Add XDataService Based Entity to List ...
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
void AddEntity<TEntity, TKey>()
|
||||
where TEntity : XBaseEntity<TKey>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xDataService.Interfaces;
|
||||
using xModels.Base;
|
||||
|
||||
namespace xDataService.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Base Entity Registerar ...
|
||||
/// </summary>
|
||||
public abstract class XBaseEntityRegisterar : IXEntityRegisterar
|
||||
{
|
||||
/// <summary>
|
||||
/// Private Entity Types Collection ...
|
||||
/// </summary>
|
||||
private readonly List<Type> entityTypes = [];
|
||||
|
||||
/// <summary>
|
||||
/// Name ...
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
protected XBaseEntityRegisterar(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Specified Entity to Collection ...
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public void AddEntity<TEntity, TKey>()
|
||||
where TEntity : XBaseEntity<TKey>
|
||||
{
|
||||
entityTypes.Add(typeof(TEntity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Registered Entity Types ...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Type> GetEntityTypes()
|
||||
{
|
||||
return entityTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Dynamic Entities ...
|
||||
/// </summary>
|
||||
/// <param name="modelBuilder"></param>
|
||||
public virtual void ConfigureEntities(ModelBuilder modelBuilder)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using xCommons.Extensions;
|
||||
using xDataService.Interfaces;
|
||||
|
||||
namespace xDataService.Providers
|
||||
{
|
||||
public class XEntityRegisterarHandlerService
|
||||
{
|
||||
/// <summary>
|
||||
/// a Collection for Extract Registerars Services ...
|
||||
/// </summary>
|
||||
private readonly List<IXEntityRegisterar> registerars = [];
|
||||
|
||||
/// <summary>
|
||||
/// a Collection for Holding all Entity Types ...
|
||||
/// </summary>
|
||||
public IReadOnlyList<Type> AllEntityTypes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Specified Registerars Entity Types ...
|
||||
/// </summary>
|
||||
/// <param name="names"></param>
|
||||
/// <returns></returns>
|
||||
public IReadOnlyList<Type> GetEntityTypes(string[] names = null)
|
||||
{
|
||||
//
|
||||
var items = new List<Type>();
|
||||
|
||||
//
|
||||
if (!names.IsNull() && names.HasChild())
|
||||
{
|
||||
//
|
||||
items = [.. registerars
|
||||
.Where(r => names.Contains(r.Name))
|
||||
.SelectMany(r => r.GetEntityTypes())];
|
||||
}
|
||||
|
||||
//
|
||||
return items.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load all Dynamically Registered Entities ...
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public void HandleRegisteredEntities()
|
||||
{
|
||||
//
|
||||
var entityTypes = new List<Type>();
|
||||
// path = Path.Combine (Directory.GetCurrentDirectory ());
|
||||
var path = Path.Combine (AppContext.BaseDirectory);
|
||||
var files = Directory.GetFiles(path, "*.dll");
|
||||
foreach (var dll in files)
|
||||
{
|
||||
//
|
||||
// Retrieve DLL Assembly ...
|
||||
var assembly = Assembly.LoadFrom(dll);
|
||||
|
||||
//
|
||||
// Extract Registerar Types ...
|
||||
var registerarTypes = assembly
|
||||
.GetExportedTypes()
|
||||
.Where(t =>
|
||||
!t.IsAbstract &&
|
||||
typeof(IXEntityRegisterar).IsAssignableFrom(t));
|
||||
foreach (var registerarType in registerarTypes)
|
||||
{
|
||||
//
|
||||
// Create Instance of Registerar Type ...
|
||||
var registerar = (IXEntityRegisterar)Activator.CreateInstance(registerarType);
|
||||
if (!registerar.IsNull())
|
||||
{
|
||||
//
|
||||
// Add Registerar Instance to Collection ...
|
||||
registerars.Add(registerar);
|
||||
|
||||
//
|
||||
// Add Registerar Provided Entities to Collection ...
|
||||
entityTypes.AddRange(registerar.GetEntityTypes());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Converts to Readonly List ...
|
||||
AllEntityTypes = entityTypes.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure all Dynamically Registered Entities ...
|
||||
/// </summary>
|
||||
/// <param name="modelBuilder"></param>
|
||||
public void HandleApplyRegisteredEntitiesConfigurations(
|
||||
ModelBuilder modelBuilder,
|
||||
string[] names = null
|
||||
)
|
||||
{
|
||||
//
|
||||
var selectedRegisterars =
|
||||
!names.IsNull() &&
|
||||
names.HasChild()
|
||||
? registerars
|
||||
.Where(r => names.Contains(r.Name))
|
||||
: registerars;
|
||||
|
||||
//
|
||||
// Loop Through Extracted Registerars ...
|
||||
foreach(var registerar in selectedRegisterars)
|
||||
{
|
||||
//
|
||||
// Configure Specified Registerars Entities ...
|
||||
registerar.ConfigureEntities(modelBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,8 +94,18 @@ public class XSaherElmDbContext : XDbContext {
|
||||
|
||||
public XSaherElmDbContext(
|
||||
DbContextOptions options,
|
||||
XDataServiceConfiguration config
|
||||
) : base(options, config) { }
|
||||
XDataServiceConfiguration config,
|
||||
XEntityRegisterarHandlerService dynamicEntityHandlers
|
||||
) : base(
|
||||
// Database nae in Data Service Configurations ...
|
||||
"xApiDb",
|
||||
options,
|
||||
config,
|
||||
dynamicEntityHandlers,
|
||||
// a List allowed Dynamic Entity Registerars in this Context ...
|
||||
new string[] { nameof(XStringEntityRegisterar) }
|
||||
)
|
||||
{ }
|
||||
|
||||
//
|
||||
public override void OnXModelCreating(ModelBuilder modelBuilder)
|
||||
@@ -107,6 +117,70 @@ public class XSaherElmDbContext : XDbContext {
|
||||
}
|
||||
```
|
||||
|
||||
#### Dynamic Entities
|
||||
|
||||
**xDataService** provides a solution for Dynamic Entity Registration in it's Contexts out of box.
|
||||
this is useful when some modules needs to presist data.
|
||||
|
||||
**IXEntityRegisterar**: an interface for Describe how to Provides Dynamic Entities.
|
||||
**XBaseEntityRegisterar**: ab abstract of Implementing Dynamic Entity Providers.
|
||||
|
||||
```C#
|
||||
public interface IXEntityRegisterar
|
||||
{}
|
||||
|
||||
public abstract class XBaseEntityRegisterar : IXEntityRegisterar
|
||||
{}
|
||||
```
|
||||
|
||||
for exampe we have **xStringService** which it is a module for Provides String resources in an Application. this module required to has Data Persist using Specified Entities in Implemented Application Contexts.
|
||||
|
||||
```C#
|
||||
public class XString : XBaseIntIDEntity
|
||||
{
|
||||
[Required]
|
||||
[StringLength (10)]
|
||||
public string Language { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength (255)]
|
||||
public string ResourceTitle { get; set; }
|
||||
|
||||
[Required]
|
||||
public string TranslatedValue { get; set; }
|
||||
}
|
||||
|
||||
public class XStringEntityConfiguration : XBaseEntityTypeConfiguration<XString, int>
|
||||
{
|
||||
public override void Configure(EntityTypeBuilder<XString> builder)
|
||||
{
|
||||
builder.ToTable("Strings");
|
||||
}
|
||||
}
|
||||
|
||||
public class XStringEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar
|
||||
{
|
||||
public XStringEntityRegisterar()
|
||||
: base(nameof(XStringEntityRegisterar))
|
||||
{
|
||||
//
|
||||
// Add XString Entity ...
|
||||
AddEntity<XString, int>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Entities ...
|
||||
/// </summary>
|
||||
/// <param name="modelBuilder"></param>
|
||||
public override void ConfigureEntities(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.ConfigureEntities(modelBuilder);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
by this mechanism, provided Entity Dynamically Registered on Application DbContext automatically.
|
||||
|
||||
#### Repository Pattern
|
||||
|
||||
by this feature you can manipulate specified Entity using Repository Pttern implementation.
|
||||
|
||||
Reference in New Issue
Block a user