Created
October 20, 2010 18:40
-
-
Save jmarnold/637033 to your computer and use it in GitHub Desktop.
Simple reusable create entity action call example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// metadata for finding models | |
[AttributeUsage(AttributeTargets.Class, Inherited = false)] | |
public abstract class DecoratorAttribute : Attribute | |
{ | |
private readonly Type _entityType; | |
protected DecoratorAttribute(Type entityType) | |
{ | |
_entityType = entityType; | |
} | |
public Type EntityType | |
{ | |
get { return _entityType; } | |
} | |
} | |
public class InputForCreateAttribute : DecoratorAttribute | |
{ | |
public InputForCreateAttribute(Type entityType) | |
: base(entityType) | |
{ | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// the actual reusable action call | |
public class CreateEntityAction<TEntity, TInput, TBuilder> | |
where TBuilder : IEntityBuilder<TEntity, TInput> | |
{ | |
private readonly IUnitOfWork _unitOfWork; | |
private readonly TBuilder _builder; | |
public CreateEntityAction(IUnitOfWork unitOfWork, TBuilder builder) | |
{ | |
_unitOfWork = unitOfWork; | |
_builder = builder; | |
} | |
public JsonResponse Execute(TInput inputModel) | |
{ | |
var entity = _builder.Build(inputModel); | |
_unitOfWork.Insert(entity); | |
_unitOfWork.Commit(); | |
return new JsonResponse(); // obviously make this more meaningful | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Usually goes somewhere shared | |
public class RootEntities | |
{ | |
private static readonly TypePool Types; | |
private static readonly CompositeFilter<Type> TypeFilters; | |
static RootEntities() | |
{ | |
Types = new TypePool(); | |
Types.AddAssembly(typeof(EntityMarker).Assembly); | |
TypeFilters = new CompositeFilter<Type>(); | |
TypeFilters.Includes += (type => type.Namespace.StartsWith(typeof(EntityMarker).Namespace)); | |
TypeFilters.Excludes += (type => type == typeof(EntityMarker)); | |
TypeFilters.Excludes += (type => type.IsAbstract); | |
TypeFilters.Excludes += (type => type.IsInterface); | |
} | |
public static IEnumerable<Type> All { get { return Types.TypesMatching(TypeFilters.Matches); } } | |
} | |
// Marks your domain namespace | |
public class EntityMarker { } | |
public class Note | |
{ | |
protected Note() {} | |
public Note(string body, User author) | |
{ | |
Body = body; | |
Author = author; | |
} | |
public int NoteId { get; protected set; } | |
public string Body { get; protected set; } | |
public User Author { get; protected set; } | |
} | |
public class User | |
{ | |
protected User() { } | |
public User(string username) | |
{ | |
Username = username; | |
} | |
public int UserId { get; protected set; } | |
public string Username { get; protected set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IEntityBuilder<TEntity, TInput> | |
{ | |
TEntity Build(TInput inputModel); | |
} | |
// Marks the builders namespace | |
public class EntityBuilderMarker { } | |
// Could make a quick dsl but seems like overkill (and obviously you could refactor this and the RootEntities stuff) | |
public class EntityBuilders | |
{ | |
private static readonly TypePool Types; | |
private static readonly CompositeFilter<Type> TypeFilters; | |
static EntityBuilders() | |
{ | |
Types = new TypePool(); | |
Types.AddAssembly(typeof(EntityBuilderMarker).Assembly); | |
TypeFilters = new CompositeFilter<Type>(); | |
TypeFilters.Includes += (type => type.Namespace.StartsWith(typeof(EntityBuilderMarker).Namespace)); | |
TypeFilters.Excludes += (type => type == typeof(EntityBuilderMarker)); | |
TypeFilters.Excludes += (type => type.IsAbstract); | |
TypeFilters.Excludes += (type => type.IsInterface); | |
TypeFilters.Excludes += (type => !type.ImplementsInterfaceTemplate(typeof(IEntityBuilder<,>))); | |
} | |
public static IEnumerable<Type> All { get { return Types.TypesMatching(TypeFilters.Matches); } } | |
public static Type FindBuilderFor(Type entityType, Type inputModelType) | |
{ | |
foreach (var type in All) | |
{ | |
var builderInterface = type.FindInterfaceThatCloses(typeof (IEntityBuilder<,>)); | |
if (builderInterface == null) | |
{ | |
continue; | |
} | |
if (typeof (IEntityBuilder<,>).MakeGenericType(entityType, inputModelType).Equals(builderInterface)) | |
{ | |
return builderInterface; | |
} | |
} | |
return null; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// wire it up all up in fubu (just missing a url policy) | |
public class CreateEntitiesActionSource : IActionSource | |
{ | |
public IEnumerable<ActionCall> FindActions(TypePool types) | |
{ | |
var actionCalls = new List<ActionCall>(); | |
RootEntities | |
.All | |
.Each(entityType => | |
{ | |
var inputModelType = entityType.GetModelType<InputForCreateAttribute>(); | |
if (inputModelType == null) | |
{ | |
return; | |
} | |
var builderType = EntityBuilders.FindBuilderFor(entityType, inputModelType); | |
if (builderType == null) | |
{ | |
return; | |
} | |
var createActionType = typeof(CreateEntityAction<,,>).MakeGenericType(entityType, | |
inputModelType, | |
builderType); | |
actionCalls.Add(new ActionCall(createActionType, | |
createActionType.GetMethod("Execute", BindingFlags.Instance | BindingFlags.Public))); | |
}); | |
return actionCalls; | |
} | |
} | |
public static class TypeExtensions | |
{ | |
public static Type GetModelType<TAttribute>(this Type entityType) | |
where TAttribute : DecoratorAttribute | |
{ | |
return ModelTypes | |
.All | |
.Where(type => type.HasAttribute<TAttribute>() | |
&& type.GetAttribute<TAttribute>().EntityType == entityType) | |
.FirstOrDefault(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// could refactor to reuse the root entities and entity builders stuff | |
public class ModelTypes | |
{ | |
private static readonly TypePool Types; | |
private static readonly CompositeFilter<Type> TypeFilters; | |
static ModelTypes() | |
{ | |
Types = new TypePool(); | |
Types.AddAssembly(typeof(ModelMarker).Assembly); | |
TypeFilters = new CompositeFilter<Type>(); | |
TypeFilters.Includes += (type => type.Namespace.StartsWith(typeof(ModelMarker).Namespace)); | |
TypeFilters.Excludes += (type => type == typeof(ModelMarker)); | |
} | |
public static IEnumerable<Type> All { get { return Types.TypesMatching(TypeFilters.Matches); } } | |
} | |
// Marks the root models namespace | |
public class ModelMarker { } | |
// sample input | |
[InputForCreate(typeof(Note))] | |
public class CreateNoteInputModel | |
{ | |
public string Note { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment