After digging through the ServiceStack assembly I found that the strongly-typed client's Store()
method is actually just SetEntry(key,value)
with a utility method for generating the key structure.
The utility method IdUtils.CreateUrn
has a few overloads that let you specify Type
and Id
and then returns a string used as the key.
What I have done, then, is write a CreateKey
method in my Repository base class which I then call when any AceOfBase
object is created and set it to Key
.
//In BaseRepository
public virtual string CreateKey<T>(T entity) where T : AceOfBase
{
return IdUtils.CreateUrn<AceOfBase>(entity.Id);
}
//In MemberRepository
Member m = new Member(); //inherits from AceOfBase
m.Key = CreateKey(m);
I've implemented two sets of classes in my application as well, these help me take advantage of Redis' crazy fast query time while allowing me to use relationships normally as I would with EF.
DomainModels -- These look like normal classes in that their properties are traditionally structured.
public class Activity : AceOfBase
{
public AceOfBase IndirectObject { get; set; }
public Participant Predicate { get; set; }
public Participant Subject { get; set; }
public Verb Verb { get; set; }
}
DataModels -- These contain the same primitive and enums as their DomainModel counterparts but if a field is a custom type that is stored in Redis it is replaced with a string(with the same property name) representing the key for that object.
public class Activity : AceOfBase
{
public string IndirectObject { get; set; }
public string Predicate { get; set; }
public string Subject { get; set; }
public Verb Verb { get; set; }
}
I have implemented AutoMapper
with a set of custom TypeConverter
classes that do the heavy lifting of converting DataModel
string properties to the respective property with the same name in the DomainModel
. AutoMapper
then gets called immediately before storing any object and after an object has been pulled from Redis and translates between the two model types.
//In Configure()
Mapper.CreateMap<string,Member>().ConvertUsing<KeyToBaseConverter<Member>>();
Mapper.CreateMap<Member, string>().ConvertUsing<BaseToKeyConverter<Member>>();
public class KeyToBaseConverter<T> : ITypeConverter<string, T> where T : AceOfBase
{
public RedisRepository Repository { get; set; }
public T Convert(ResolutionContext context)
{
return Repository.GetByKey<T>(context.SourceValue.ToString());
}
}
public class BaseToKeyConverter<T> : ITypeConverter<T, string> where T : AceOfBase
{
public string Convert(ResolutionContext context)
{
var f = context.SourceValue as AceOfBase;
return f.Key;
}
}
The only component left to address is ensuring that any Lists
or Sets
I create get the same formatting for their key.
Using this schema I will be able to seamlessly use the strongly-typed client alongside the string-client, ensuring that even when pulling objects without the strongly-typed client I can determine and safely cast the JSON blob into the correct Type by examining only the Key property.