Writing cache based repositories for web application prototyping
When I started building in-house demo application I thought about how to solve temporary data layer so I don’t have to use real database and object mappings for it. Playing with new object model and new components I move way faster if I don’t have any additional ballast that I can avoid. So I wrote simple cache based repository mechanism I can use to imitate real repositories that I will write in the future.
NB! Don’t develop this idea too much further because you can use my method only to some certain point. Don’t forget that you have also maintain relations between objects and keep them consistent. Use my method until your object model is small and simple.
If you look for something perfect you don’t find it here. It is just my pretty raw solution that I still test and I am not very sure if I don’t replace it with something better soon.
Structure of my application
Basically my application follows structure show here (example data, just took it from air):
- AppName.Core (all business classes and interfaces are here)
- Contacts
- Person.cs
- Company.cs
- …
- Projects
- Project.cs
- ProjectTask.cs
- …
- Repositories
- IPersonRepository
- ICompanyRepository
- …
- BusinessEntity.cs
- Contacts
- AppName.Data.CacheBasedStorage (this is the topic of this posting)
- Repositories
- BaseRepository.cs
- PersonRepository.cs
- CompanyRepository.cs
- …
- Repositories
- AppName.Data.NHibernate (not there really, but comes if users like my app)
- AppName.Infrastructure (IoC, validation and other “low level” stuff)
- Resolver.cs
- Validator.cs
- …
- AppName.Web (web version of my application)
I think this structure tells almost everything about architecture. Okay, some notes too. AppName.Core has only interfaces for repositories because it doesn’t deal with objects persisting and doesn’t provide any Data Access Layer logic. It is all done in external repository implementations. I use Unity as IoC container and Resolver class in infrastructure library is mediator between client applications and repository implementations. I can always move from Unity to some other IoC container and client applications doesn’t need to be built again.
Why cache based storage?
I considered different ways about how to implement my toy-repositories and cache seemed to me as pretty good option:
- it is accessible to all users of my application,
- it is easy to use,
- it needs no additional coding for saving and loading data,
- it’s not a problem when data expires – I need data for building and demonstration purposes only.
Of course, it is also possible to use some more elegant solutions but … I am just too lazy to run for idealism and beautiful things. When I get something that works right now for me – I am happy. In this point I don’t know if users like my app or not and I don’t want to make any moves I can avoid.
Implementation of repositories
Cache based data storage implementation is in AppName.Data.CacheBasedStorage library. In short we have abstract class BaseRepository that does all the dirty work for us. The real repositories extend it and they also follow repository interfaces from core library. Basically they wrap calls to base library with correct types.
Let’s start with BaseRepository. The implementation shown here is awful in multi-user context, but for one-man testing and developing it is okay.
public abstract class BaseRepository
{
private Cache _cache;
protected BaseRepository()
{
// Here we add some sample data to cache
}
private Cache Cache
{
get
{
if (_cache != null)
return _cache;
_cache = HttpContext.Current.Cache;
return _cache;
}
}
protected T Get<T>(int id)
{
var list = GetList<T>();
if (list == null)
return default(T);
if (list.ContainsKey(id))
return list[id];
return default(T);
}
protected void Save<T>(T instance) where T : BusinessEntity
{
var list = GetList<T>();
var id = instance.Id;
if (id == 0)
{
if (list.Count > 0)
id = (from l in list
orderby l.Key descending
select l.Key).First();
id++;
instance.Id = id;
if (list.ContainsKey(id))
list[id] = instance;
else
list.Add(id, instance);
}
else
list[id] = instance;
}
protected void Delete<T>(T instance) where T : BusinessEntity
{
var list = GetList<T>();
if (list.ContainsKey(instance.Id))
list.Remove(instance.Id);
}
protected IDictionary<int, T> GetList<T>() where T : BusinessEntity
{
var type = typeof(T).ToString();
if (Cache[type] != null)
return (IDictionary<int, T>)Cache[type];
var list = new Dictionary<int, T>();
Cache[type] = list;
return list;
}
}
Now you may have question: what about data querying? My answer is simple: let’s take LINQ and use it. We don’t have tons of data, we don’t have millions of users – we have only our development machine and one or two users.
And here is one example repository. It is very simple and as stated above it just wraps calls to base repository with correct types. Of course, if I need some more logic in repositories then I can add it to appropriate methods. Later, if users like my applications, I can take this logic from my toy-repositories and put it in real repositories.
public class CompanyRepository : BaseRepository, ICompanyRepository
{
public Company GetCompanyById(int id)
{
return Get<Company>(id);
}
public CompanyName GetCompanyNameById(int id)
{
return Get<CompanyName>(id);
}
public IList<Company> ListCompanies()
{
return GetList<Company>().ToList();
}
private void Save(Company company)
{
Save<Company>(company);
}
public void SaveCompanyName(CompanyName name)
{
Save<CompanyName>(name);
}
public void Delete(Company company)
{
Delete<Company>(company);
}
public void DeleteCompanyName(CompanyName name)
{
Delete<CompanyName>(name);
}
}
Well, this is my current experiment with repositories that should save me some time and be flexible enough to get working prototypes done. I also like the idea that I can keep my object model evolving with my prototype so I create additional value when building prototypes – when I have to start real coding I can use classes I wrote during prototyping and hopefully it saves me some time.
|
|
|
|
|
|
|