Isn't this fun, we seem to have our own private forum.
I was bother that I seemed to be developing two sets of repositories, one for Sql and one for InMemory, each with large slabs of Linq stuff. The InMemory one was getting tested but that just seemed to be testing the testing code if you see what I mean. I took a step back and wrote single repositories which had an ISession value injected in to them. One ISession for Sql/Server and one for InMemory. ISession is just
using System;
using System.Linq;
namespace Ltg.Support
{
[CLSCompliant(true)]
public interface ISession
{
IQueryable<T> GetAll() where T : class;
void SubmitChanges();
void Rollback();
void Insert(T item) where T : class;
void Remove(T item) where T : class;
}
}
The SqlSession implementation is pretty trivial
using System.Data.Linq;
using System.Linq;
using Ltg.Support;
namespace Lms.Services.Repositories
{
public class SqlSession : ISession
{
LmsDataContext _db;
public SqlSession()
{
_db = new LmsDataContext();
}
#region ISession Members
public IQueryable<T> GetAll() where T : class
{
return GetTable();
}
public void SubmitChanges()
{
try
{
_db.SubmitChanges();
}
catch
{
IdentityMap.FlushMap();
throw;
}
}
public void Rollback()
{
_db.Transaction.Rollback();
}
public void Insert(T item) where T : class
{
GetTable().InsertOnSubmit(item);
}
public void Remove(T item) where T : class
{
GetTable().DeleteOnSubmit(item);
}
#endregion
Table GetTable() where T : class
{
return _db.GetTable();
}
}
}
InMemorySession hurt my brain for a little while but this seems to work OK. There are two layers of tables. _permanent is meant to simulate table saved to a backing store whereas _tables represents the current 'in progress' transaction. InitializePermanentDictionary (I wonder why I misspelt Initialize with a 'z'?) sets up my test data.
using System;
using System.Collections.Generic;
using System.Linq;
using Ltg.Support;
namespace Lms.Services.Repositories
{
public class InMemorySession : ISession
{
private static Dictionary<Type, Object> _permanent = new Dictionary();
private Dictionary _tables;
public InMemorySession()
{
_tables = new Dictionary();
}
static InMemorySession()
{
InitializePermanentDictionary();
}
private IList listFor()
{
if (!_tables.ContainsKey(typeof(T)))
{
if (!_permanent.ContainsKey(typeof(T)))
_permanent[typeof(T)] = new List();
IList permanentTable = (IList)_permanent[typeof(T)];
_tables[typeof(T)] = new List(permanentTable);
}
return (IList)_tables[typeof(T)];
}
#region ISession Members
public IQueryable GetAll() where T : class
{
return listFor().AsQueryable();
}
public void SubmitChanges()
{
foreach (Type table in _tables.Keys)
{
if (_permanent.ContainsKey(table))
_permanent.Remove(table);
_permanent[table] = _tables[table];
}
_tables = new Dictionaryobject>();
return;
}
public void Rollback()
{
_tables = new Dictionaryobject>();
}
public void Insert(T item) where T : class
{
listFor().Add(item);
}
public void Remove(T item) where T : class
{
listFor().Remove(item);
}
#endregion
#region Initialisation
private static void InitializePermanentDictionary()
{
Blurb opening = CreateBlurb("OpeningBodyText", "Stuff on the body of the opening page");
Blurb platitudes = CreateBlurb("OpeningPlatitudes", "Stuff that scrolls on the opening page");
BlurbText openingText = CreateBlurbText(opening, "Welcome to our web site");
BlurbText platitudeText = CreateBlurbText(platitudes, "Stuff that scrolls on the opening page");
.
.
.
}
private static BlurbText CreateBlurbText(Blurb blurb, string text)
{
BlurbText temp = new BlurbText()
{
BlurbTextGUID = Guid.NewGuid(),
BlurbGUID = blurb.BlurbGUID,
DisplayLanguage = System.Globalization.CultureInfo.CurrentUICulture.Name,
DisplayText = text,
LastModified = DateTime.Now
};
AddToPermanent(temp);
return temp;
}
private static Blurb CreateBlurb(string name, string description)
{
Blurb temp = new Blurb()
{
BlurbGUID = Guid.NewGuid(),
BlurbName = name,
BlurbDescription = description,
LastModified = DateTime.Now
};
AddToPermanent(temp);
return temp;
}
.
.
.
static void AddToPermanent(T item)
{
if (!_permanent.ContainsKey(typeof(T)))
_permanent[typeof(T)] = new List();
((List)_permanent[typeof(T)]).Add(item);
}
#endregion
}
}
A typical repository looks like
using System;
using System.Collections.Generic;
using System.Linq;
using Ltg.Support;
namespace Lms.Services.Repositories
{
class CourseRepository : Repository<Lms.Model.Course>
{
public CourseRepository(ISession session) : base(session) { }
public override IEnumerable GetBySpecification(Func<Lms.Model.Course, bool> specification)
{
return GetCourses().Where(specification).MapIt();
}
public override void Save(Lms.Model.Course course)
{
Course dbCourse;
bool newCourse = TranslateCourse(course, out dbCourse);
if (newCourse)
{
session.Insert(dbCourse);
}
}
public override void Delete(Lms.Model.Course item)
{
throw new NotImplementedException("public override void Delete(Lms.Model.Course item)");
}
#region Private methods
IQueryable GetCourses()
{
return from c in session.GetAll()
let availableTo = GetAvailableToIds(c.CourseGUID)
let thePrice = new Money(c.Price??0, Currency.Xlk)
select new Lms.Model.Course
{
AvailableTo = new AggregateSet(availableTo, customerRepository.GetById, EntityInformation.GetIdForEntity),
Id = c.CourseGUID,
CreatedBy = personRepository.GetById(c.CreatorGUID ?? Guid.Empty),
CreationDate = c.CreationDate ?? new DateTime(2000, 1, 1),
Custom = c.Custom,
Description = c.CourseDescription,
Duration = new TimeSpan(c.Duration ?? 0),
Name = c.CourseName,
OrganizationId = c.OrganizationID,
Price = thePrice,
UrlName = c.UrlName,
UploadedFileName = c.UploadedFileName
};
}
Everything seems to hang together quite well as long as the repositories don't get too complex. LinqToSql can't translate everything that I would like.
I don't use the pipes and filters pattern but instead went for passing a 'specification' parameter to the Repository Get methods. I am quite pleased with this as I had to get my head around lambda expressions and how to pass them around. Once I got it embedded in my brain it has turned into a most satisfactory method.
cheers
(some time if I am coming to Melbourne I might look you up, I am interested to know if your Avatar is just your passport photo!)