In current project I need implement some generic Operations infrastructure.
In a example:
An entity can have X operations , each operation will have these methods:
IsAvaliable - perform some check if I can execute operation above entity, bool
Execute - perform some logic if result of methos IsAvaliable is true, void
Also I need to get all avaliable operations for concrete Entity - these list will be shown to user, based on which entity is user currenty editing in UI
I decided to combine Command Patterm and also Specification Pattern for IsAvaliable logic.
I created Generic abstract OperationBase class for all Operations:
public interface IOperation
{
void Execute();
}
public interface IOperation<T> : IOperation
{
bool IsAvaliable(T o);
}
public abstract class OperationBase<TEntity> : IOperation<TEntity>, ITransientDependency
{
private ISpecification<TEntity> specification;
protected TEntity entity;
public OperationBase(TEntity entity)
{
this.entity = entity;
}
public abstract void Execute();
public virtual bool IsAvaliable(TEntity entitySpec)
{
return specification.IsSatisfiedBy(entitySpec);
}
public void SetSpecification(ISpecification<TEntity> specification)
{
this.specification = specification;
}
}
Then I also created base class for Operations which specified Entity Type:
public abstract class TestOperationBase<TestEntity> : OperationBase<TestEntity>
{
public ODUOperationBase(TestEntity entity) : base(entity)
{
}
protected Action Operation { get; set; }
public override void Execute()
{
if (base.IsAvaliable(entity))
{
Operation?.Invoke();
}
}
}
After that I creted concete operation above entity:
public class TestConceteOperation1 : TestOperationBase<TestEntity>
{
public TestConceteOperation1(TestEntity entity) : base(entity)
{
base.Operation = MethodImplementation;
base.SetSpecification(OperationSpecs.TestEntitySpec);
}
public void MethodImplementation()
{
Console.WriteLine("Custom operation invoking");
}
}
And there is example of useage of this logic:
public void ExecuteTestOp()
{
TestEntity testEntity= new TestEntity();
TestConceteOperation1 oper = new TestConceteOperation1(testEntity);
oper.Execute();
}
Also smaple code for Operation specification:
public static ISpecification<ODUZaznam> TestEntitySpec = new Specification<TestEntity>(
o =>
{
return (o.Param1== 1 &&
o.Param2 == 2 &&
o.Param3 > 0 &&
o.Param4 == false
);
}
);
Now what I need to know is How to get list of avaliabe operations. Reflections is a solution (find all interfaces IOperation which has generic parameter with concrete type e.g.
private List<Type> GetAllAvalibleOperationTypesFor<TEntity>()
{
List<Type> operationsTypes = new List<Type>();
Type tOperation = typeof(IOperation<TEntity>);
Type tConcrete = null;
foreach (Type t in Assembly.GetExecutingAssembly().GetTypes())
{
// Find a type that implements IOperation<TEntity> and is concrete.
// Assumes that the type is found in the executing assembly.
if (tOperation.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface)
{
tConcrete = t;
operationsTypes.Add(tConcrete);
}
}
return operationsTypes;
}
Now I need to check if operation is IsAvaliable, to do that I need to create instances for each operation and invoke IsAvaliable
method. I don´t like this idea of creating instances. Specification method is static and only need to know concrete entity, which I know when i get OperationsTypes collection.
I have no idea how to solve this situation. Maybe some static method inside TestConceteOperation1
class with TEntity
as parameter which call static specification method. But this appropoach will lead to that Develepoper must remeber implement this static class in every operation.
Do have anybody some idea (how get List of avaliable operations for concete entity based on Specification = true result) how to do it better way ?
Thanks for any ideas, and sorry for my english.