Dynamic Content Types
In general it's recommended to bake content types in to your codebase because it makes it much simpler for incoming developers to see what is available and use things like the events system with autocomplete to quickly link up what they need. However there is also some occasions where it would be useful to create a basic one via the admin panel at runtime. These are dynamic content types, and they are available via the Api/CustomContentTypes module:
socialstack i Api/CustomContentTypes
How it works
Content types created via this module are first class citizens: they have the exact same performance characteristics as ones built in to the codebase. This is because the module generates a controller, service and a C# class to represent your content type at runtime. It then runs it through the regular service startup flow which allows things like the permission system and database engine to mount the content type automatically and in the same way it would if it was built in.
Working with one from code
If you later need to add events to a dynamic content type, you have 2 options: either create the source of the module so it becomes built in to the code, or alternatively, you can use Services.Get to find the service:
// Get a weak reference to a service for a custom content type called "GalleryItem":
var galleryItemService = Services.Get("GalleryItemService");
// Get the event group, which is what would be the equiv of Events.GalleryItem if it existed:
var eventGroup = galleryItemService.GetEventGroup();
These weak references are not particularly ideal in many scenario's though, particularly if you did want to add an event handler to it. This is because event handlers like to know which types you're working with so you can get good autocomplete and code correctness. Here though we are working with "some runtime generated content type" with unknown fields, but it is not completely unknowable. We can use generic methods to be able to add events in a generic way. Note that this works for every content type, including the built in ones, and is how e.g. the permission system mounts every type in a generic way.
// Get the service:
var service = Services.Get("GalleryItemService");
// MethodToRun is a generic method on this class which will basically accept any AutoService.
var methodToRun = GetType().GetMethod(nameof(MethodToRun));
// Create a specific flavour of that method:
var genericMethodToRun = methodToRun.MakeGenericMethod(new Type[] {
service.ServicedType,
service.IdType
});
// And invoke it:
genericMethodToRun.Invoke(this, new object[] {
service
});
// .. some time later ..
// The method itself:
public void MethodToRun<T, ID>(AutoService<T, ID> service)
where T : Content<ID>, new()
where ID : struct, IConvertible, IEquatable<ID>, IComparable<ID>
{
/*
service.EventGroup.AfterCreate.AddEventListener((Context context, T theThing) => {
// Adding an AfterCreate event handler to a totally generic content type
// because of the where T : .. rule above, C# knows it is at least some sort of content type with Id, CreatedUtc etc on it.
});
*/
}
You can be more specific with your where T: .. rule if you know the type you're working with is something more specialised such as UserCreatedContent<ID>. This gives you extra fields such as the UserId and created/ edited times.