I have a repository class that uses NPoco/PetaPoco to access data which reads from a common content table.
create table Content (
Id int not null identity primary key,
Type tinyint not null,
...
)
Then I have an abstract ContentBase
class that other types inherit. The main difference between inherited types being the value of that type DB column value. They do have some additional columns per concrete content type, but that's not relevant here.
So. In order for my repository class to return any of the actual concrete classes I wanted to write a medhod:
public TContent Create<TContent>(string name, ...)
{
...
// SP executes a few insers and returns newly created data instance
return db.Single<TContent>(
new Sql.Builder.Append(";exec dbo.CreateContent @Type, @Name, ...", new {
Type = TContent.Type, // this is the problem
Name = name,
...
}));
}
As you can see I would require my base abstract class to define a static member to get the actual type value that should be accessible through generic type specification. And inheriting classes should set it according to their concrete type implementation.
The problem is of course that there's no such thing as abstract static
members in C#.
How should I approach this problem in a way so that my repository will be able to provide Type
value on its own without me providing it explicitly with the call? I would only like to provide generic class when calling it and get back correct concrete type.
My method doesn't get an object instance of a particular type but it should create one. That's why I can't really have an instance member Type
and read from that one while executing my repository method.
As static members are shared among all instances and if this is base class all inheriting classes share the same member unless this class is generic. In that case we get a static member per generic type.
So I was thinking of adding an additional class between the base abstract and concrete classes:
public abstract class ContentBase
{
...
}
public abstract class ContentBase<TConcrete>
where TConcrete: ContentBase<ContentBase> // is this constraint ok?
{
public static ContentType Type = ???;
}
and then concrete classes:
public class ContentOne : ContentBase<ContentOne>
{
???
}
And as said I should be able to call my repository method as:
repo.Create<ContentOne>(name, ...)
where within this method repository should be able to access static member of generic type provided by the call...
Even with your idea of having an abstract ContentBase class, you won't be able to access a custom behavior for each derived class; here is a small test i tried to see if your idea could be used:
public abstract class ContentBase<T>
{
public static Func<string> TypeLocator { get; set; }
static ContentBase()
{
TypeLocator = () => typeof(T).Name;
}
}
public class Content1 : ContentBase<Content1> {
private static string Content1Type = "The type of Content 1";
static Content1()
{
TypeLocator = () => Content1Type;
}
}
public class Content2 : ContentBase<Content2> {}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Content1.Type => " + GetObject<Content1>()); // Content1
Console.WriteLine("Content2.Type => " + GetObject<Content2>()); // Content2
}
private static string GetObject<TContent>() where TContent: ContentBase<TContent>
{
var typeLocator = typeof(ContentBase<TContent>).GetProperties()[0].GetValue(null, null) as Func<string>;
return typeLocator.Invoke();
}
}
The best you can get is the type of the TContent, which may be enough for you to explore the mapping proposition below.
I don't really see a way to implement abstract static members, but you could resort to an external registering dictionary living as a singleton throughout your program which would be used as the mapper between your Type
and your Type.DBType
public TContent Create<TContent>(string name, ...)
{
...
// SP executes a few insers and returns newly created data instance
return db.Single<TContent>(
new Sql.Builder.Append(";exec dbo.CreateContent @Type, @Name, ...", new {
Type = RegisteredTypes[typeof TContent],
Name = name,
...
}));
}