EntityProvider

Injectable entities

A beginning

The other day I was cleaning my keyboard. I don't know exactly what I did but BAM! a Djinn popped up on my screen!

Well, after establishing that meta-wishes were out of the question, the conversation went something like:

Me: I really wish I didn't need to include special attributes in my entities; just so I can serialize properly.

Mr. J: No prob.. what else?

Me: Hmm.. I wish I could use logging in my classes.. maybe even take some other services as dependencies, so my logic was in an entity class, not a service that took a DTO.

Mr. J: Yep, I can do that... anything else.. you know it doesn't have to be about code right?

Me: sure.. sure.. let me think... oh yeh I wish I could have a straight-forward async-int, you know not all instances are created equally.. if you get what I mean hehe.

Mr. J: Ok.. well, last time I had to start a world-war, so this time it should be a piece of cake!

Me: Now.. lets talk about C. Theron. I wish...

Mr J: Soz, you're done

Me: Aww.. F$*@.. I need a do-over.. cmon!

...15 minutes later...


EntityProvider

An approach for enabling richer entity classes in C#.

IInternalState<T>

Ever try to write a class with some calculated properties; only to back-track because you don't want to serialize the property?

I know, just use STJ: JsonIgnore, or Newtonsoft: JsonIgnore or other, but that get's messy. And it doesn't solve

  1. Needing a public property for what should be a private field.
  2. Wanting to expose a read-only collection, but needing to use a List for serialzation serialize.

In the end I always fall-back to needing some DTO to describe the 'storable' vs public properties, a rich class is just not viable as a serializable class itself.

So; use a simple interface:

public interfact IInternalState<TState>{
    TSTate CurrentState();  
}

This allows the entity to be itself, while still providing a serializer friendly 'internal-state'.

public class MyEntity : IInternalState<MyEntity.State>{

    private State _state;
    public record State(int A, int B);

    public MyEntity(State state)
    {_State = state;}

    State IInternalState<State>.CurrentState() => _state;

    //Don't store this, store the state
    public double Distance => Math.Sqrt(_state.X^2+_state.Y^2);
}

An entity and its internal-state are tightly-coupled (by design). So I use a nested class for the state.

Use Explicit interface implementation because I want the interface, but I DON'T want to express it is part of the entity public interface.

I tend to find using a object-centric base interface gives me more flexibility:

public interface IInternalState{
    public object GetCurrentState();
}
public interface IInternalState<TState> : IInternalState
{
   object IInternalState.GetCurrentState() => CurrentState();
}

Now we have a ready-to-serialize internal-state we can store/load a MyEntity:

MyEntity entity;

var dto = ((IInternalState)entity).GetCurrentState();
var json = JsonSerializer.Serialize(dto);

var dtoLoaded = JsonSerializer.Deserialize<MyEntity.State>(json);
var entityLoaded = new MyEntity(dtoLoaded);

:+1: wish-1 - Serialize without attributes.

IEntityFactory<TEntity, TState>

Use an IInternalState<TState> to convert an entity into a 'dumb' serializer-friendly type. Then follow up with a corresponding IEntityFactory to get from internal state to entity.

//Make a TEntity from its state
public interface IEntityFactory<TEntity, TState>{
    //Use a ValueTask so we're async friendly.
    public ValueTask<TEntity> Create(Tstate state);
}

We can now implement the factory and use it via a container

//An entity factory, with aService
public class MyEntityFactory : IEntityFactory<MyEntity, MyEntity.State>{
    private IService _aService;
    public MyEntityFactory(IService aService)
    {_aService = aService;}

    public ValueTask<MyEntity> Create(MyEntity.State state)
      => new MyEntity(state);
}

We'll add the MyEntityFactory to a IoC container, that way we can get it when needed, and it can take other services as dependencies.

EntityProvider

And now; an over-arching service to act as the entry point for entity creation. This service takes the IoC container as a dependency, and uses the entity-factory above to create a entity from its state.

You (defiantly) do not normally want to take the IServiceProvider as a dependency;
But the EntityProvider's responsibility is to use a ServiceProvider to create an entity from its factory. So I feel its ok here.

public class EntityProvider{
    private IServiceProvider _container;

    public EntityProvider(IServiceProvider container)
    {_container = container;}

    //Use the IInternalState interface to work-out the state type.
    public static EntityStateType(Type entityType) =>
      entityType.GetInterface(
        typeof(IInternalState<>).Name)
        .GetTypeArguments[0];

    //The factory MUST implement IEntityFactory.
    public static EntityFactoryType(Type e, Type s) =>
      typeof(IEntityFactory<,>).MakeGenericType(e,s);

    public ValueTask<TEntity> Crate<TEntity>(object state){

       var entityStateType = EntityStateType(typeof(TEntity));
       var factoryType = EntityFactoryType(typeof(TEntity), entityStateType);

       //Can't do it, need object-centric base.
       //var factory = (IEntityFactory)container.GetRequiredService(factoryType);
       //var entity = await factory.Create(state);

       //return (TEntity)entity;
    }
}

Woops; lets add a base interface, with object-centric method.

public interface IEntityFactory{
    public ValueTask<object> Create(object state);
}
public interface IEntityFactory<TEntity, TState> : IEntityFactory{
  async ValueTask<object> Create(object state) =>
        await Create((TState)state);
}

Done. Putting it all together:

var cont = new ServiceCollection()
    //some random service.
    .AddSingleton<IService, AService>()
    //Factory for MyEntity
    .AddScoped<
        IEntityFactory<MyEntity, MyEntity.State>, 
        MyEntityFactory>()
    //Entry point for entity creation.
    .AddScoped<EntityProvider>()
    .BuildServiceProvider();

  var p = cont.GetRequiredService<EntityProvider>();
  var entity = await p.Create<MyEntity>(new MyEntity.State(2,2));

Since we're injecting services:

public class MyEntity{
    //entity depends on IService.
    public MyEntity(State state, IService aService)
    {...}
}

public class MyEntityFactory{
    //entity-factory passes service to entity on create.
    public ValueTask<MyEntity> Create(State state)
      => new MyEntity(state, _aService);
}

:+1: wish-2 - Entity with services.

Now we have entity state, and a factory... small step to async-initialize, or maybe call it 2-stage construction.

So, our entity now wants to initialize during construction; lets wrap it in a static method to make it obvious.

public MyEntity{
  private MyEntity(State state, IService aService){}

  public static async ValueTask<MyEntity> New(State state, IService aService)
   {
     var result = new MyEntity(state, aService);
     //...other init stuff
     await OtherAsyncStuff();
     return result;
   }
}

Then the the entity factory uses this async create:

public class MyEntityFactory{

    public async ValueTask<MyEntity> Create(State state)
      => await MyEntity.New(state, _aService);    
}

:+1: wish-3 - Entity create with async initialization.

EntityMethodFactory

Well, that's the back bone of the approach. In its current form is a little verbose for my taste; the Factory type(s) are little more than wrappers around a static method.

We can implement a general approach for the entity factory, using some reflection.

Lets call the method factory EntityMethodFactory<,>

public class EntityMethodFactory<T, S> : IEntityFactory<T, S>{}

Since we're using reflection, better to maintain its results in a cached set of strategies. That way we incur the cost only during start-up. A service to maintain the discovered strategies.

//A method and some args to create a Entity.
public record Strategy(MethodInfo op, Type[] Args);

//Dictionary mapping an entity type to a create method (strategy).
public class FactoryStrategies{
    private Dictionary<Type,Strategy> _data = new();
    public Strategy this[Type t] {
        get => _data[t];
        set => _date[t] = value;}
}

Now a config class to setup:

public class EntityProviderConfig {

  private List<Type> _entityTypes = new();

  //try find a method to create the entity
  public static Strategy GetStrategy(Type e, Type s){

     var returnType = typeof(ValueTask<>).MakeGenericType(e);

     var op = e.GetMethods(BindingFlags.Static)
       //return a ValueTask<entity>.
      .Where(x => x.ReturnType == returnType)
      //has a parameter that takes the state.
      .Where(x => x.GetParameters().Any(p => p.ParameterType == s))
      .First();
      var args = op.GetParameters()
        .Select(p => p.ParameterType)
        .ToArray();

      return new Strategy(op, args);
     }

  private static Type GetMethodFactoryType(Type e, Type s)
    => typeof(EntityMethodFactory<,>).MakeGenericType(s, s);

  //register an entity to be created.
  public AddEntity<T>(){ 
    _entityTypes.Add(typeof(T));
  }

  //register strategy and MethodFactory for entity types.
  public Register(IServiceCollection services){

    var strategies = new FactoryStrategies();
    //keep the discovered method strategies.
    services.AddSingleton(strategies);

    foreach(var e in _entityTypes){
      var s = EntityProvider.EntityStateType(x);

      strategies[e] = GetStrategy(x, s);

      services.AddScoped(
        EntityProvider.EntityFactoryType(e,s),
        GetMethodFactoryType(e,s)
      );
    }
  }
}

Now our MethodFactory can use this collection of strategies to do its job.

public class EntityMethodFactory<T, S> : IEntityFactory<T, S>{
    private readonly IServiceProvider _container;
    private readonly FactoryStrategies _strategies;

    //I need the container, and the strategy to create a Entity.
    public EntityMethodFactory<T, S>(IServiceProvider container, FactoryStrategies strategies)
    {
        _container = container;
        _strategies = strategies;
    }

    public async ValueTask<T> Create(S state){
        var strategy = _strategies[typeof(T)];

        //resolve all except state with container.
        var args = strategy.Args.Select(x => 
            x is typeof(s) ? 
                 state :
                 _container.GetREquiredService(x);
        );

        var opResult = (ValueTask<T>)strategy.Op.Invoke(null, args);
        return await opResult;
    }
}

Putting it all together

We register all the parts in container setup, then use the EntityProvider as the entry point to create an entity.


var services = new ServiceCollection()
    .AddSingleton<IService, AService>()
    .AddScoped<EntityProvider>();

var cfg = new EntityProviderConfig()
      .AddEntity<MyEntity>();

cfg.Register(services);

var cont = services.BuildServiceProvider();

  var p = cont.GetRequiredService<EntityProvider>();
  var entity = await p.Create<MyEntity>(new MyEntity.State(2,2));

Where to next?

Currently I use this style to help drive toward richer domain models rather than anemic models

We can add some other capabilities, such as more flexibility for EntityMethodFactory strategies; but I think that's enough for now.

If you'd like some runnable code; I've packaged this into a little library kwd.CoreDomain / nuget.