The immediate uses of PLINQO Future Queries are readily apparent: Do you have multiple queries that need to be executed back to back? Add future to the back of them and save yourself a few database trips. What might not be quite as easy to spot, are those places where using a more advanced design pattern can really enhance your application's performance. Here is an example of one of those patters.
Part 1: Entity Loaders
When retrieving entities from the database you can not always use DataLoadOptions to populate the complete object model in a single trip to the database. Having to make multiple trips to populate one type of entity can be costly, and this can be compounded by the need to populate a series of different entities at the same time.
One solution is to create a loader class for your entities that use future queries to get their data, as this is a very reliable means with which to ensure that no extra unnecessary database calls are execute. Also, by creating an interface for these loader classes, you can use a generic piece of code to load any number of entities of any number of data types.
Use Cases
[Test]
public static Case Test(int id)
{
var loader = new CaseTagLoader(id);
loader.Load();
return loader.Entity;
}
[Test]
public static List<Case> Test(int[] ids)
{
var loaders = new List<CaseTagLoader>();
foreach (var id in ids)
loaders.Add(new CaseTagLoader(id));
using (var db = new BlogDataContext())
{
loaders.ForEach(l => l.Load(db));
db.ExecuteFutureQueries();
}
return loaders.Select(l => l.Entity).ToList();
}
Interface
public interface ILoader<T>
{
void Load();
void Load(BlogDataContext db);
T Entity { get; }
bool IsLoaded { get; }
}
Implementation
public class CaseTagLoader : ILoader<Case>
{
private readonly int _caseId;
private FutureValue<Case> _futureCase;
private IEnumerable<CaseTag> _caseTags;
private IEnumerable<Tag> _tags;
private Case _case;
public Case Entity
{
get
{
if (!IsLoaded)
return null;
if (_case == null)
CreateCase();
return _case;
}
}
public bool IsLoaded { get; private set; }
public CaseTagLoader(int caseId)
{
_caseId = caseId;
IsLoaded = false;
}
public void Load()
{
if (IsLoaded)
return;
using (var db = new BlogDataContext())
{
db.ObjectTrackingEnabled = false;
Load(db);
db.ExecuteFutureQueries();
}
}
public void Load(BlogDataContext db)
{
if (IsLoaded)
return;
_futureCase = db.Case.ById(_caseId).FutureFirstOrDefault();
var caseTagQuery = db.CaseTag.ByCaseId(_caseId);
_caseTags = caseTagQuery.Future();
_tags = db.Tag
.Join(caseTagQuery, t => t.Id, ct => ct.TagId, (t, ct) => t)
.Future();
IsLoaded = true;
}
private void CreateCase()
{
_case = _futureCase.Value;
_case.CaseTags.AddRange(_caseTags);
foreach (var caseTag in _caseTags)
caseTag.Tag = _tags.First(t => t.Id == caseTag.TagId);
}
}
No comments:
Post a Comment