Adds the foundation for the repository implementation with abstract Repository, Unit Of Work and Cache provider.

Adding the Query object implementation.
Adds the ModelDtoMapper, which is still a WIP.
Adds the initial implementation of the IContentRepository with dependencies.
This commit is contained in:
Morten@Thinkpad-X220
2012-10-04 13:44:02 -02:00
parent 75e11b2ba8
commit 145cddbcb6
18 changed files with 1783 additions and 7 deletions

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.Caching
{
/// <summary>
/// Defines the implementation of a Cache Provider intented to back a repository
/// </summary>
internal interface IRepositoryCacheProvider
{
/// <summary>
/// Gets an Entity from the cache by Type and Id
/// </summary>
/// <param name="type"></param>
/// <param name="id"></param>
/// <returns></returns>
IEntity GetById(Type type, Guid id);
/// <summary>
/// Gets an Entity from the cache by Type and Ids
/// </summary>
/// <param name="type"></param>
/// <param name="ids"></param>
/// <returns></returns>
IEnumerable<IEntity> GetByIds(Type type, List<Guid> ids);
/// <summary>
/// Gets all Entities of specified type
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
IEnumerable<IEntity> GetAllByType(Type type);
/// <summary>
/// Saves the Entity
/// </summary>
/// <param name="type"></param>
/// <param name="entity"></param>
void Save(Type type, IEntity entity);
/// <summary>
/// Deletes the Entity from the cache
/// </summary>
/// <param name="type"></param>
/// <param name="entity"></param>
void Delete(Type type, IEntity entity);
}
}

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.Caching
{
/// <summary>
/// The InMemory registry looks up objects in an in-memory dictionary for fast retrival
/// </summary>
internal class InMemoryCacheProvider : IRepositoryCacheProvider
{
#region Singleton
private static volatile InMemoryCacheProvider _instance;
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
private InMemoryCacheProvider() { }
public static InMemoryCacheProvider Current
{
get
{
using (new WriteLock(Lock))
{
if (_instance == null) _instance = new InMemoryCacheProvider();
}
return _instance;
}
}
#endregion
private readonly ConcurrentDictionary<string, IEntity> _cache = new ConcurrentDictionary<string, IEntity>();
/// <summary>
/// Retrives an object of the specified type by its Id
/// </summary>
/// <param name="type">The type of the object to retrive, which implements <see cref="IEntity"/></param>
/// <param name="id">The Guid Id of the Object to retrive</param>
/// <returns></returns>
public IEntity GetById(Type type, Guid id)
{
var compositeKey = GetCompositeId(type, id);
var containsKey = _cache.ContainsKey(compositeKey);
if (containsKey)
{
return _cache[compositeKey];
}
return null;
}
/// <summary>
/// Retrives objects of the specified type by their Ids
/// </summary>
/// <param name="type">The type of the objects to retrive, which implements <see cref="IEntity"/></param>
/// <param name="ids">The Guid Ids of the Objects to retrive</param>
/// <returns></returns>
public IEnumerable<IEntity> GetByIds(Type type, List<Guid> ids)
{
var list = (from id in ids
select GetCompositeId(type, id)
into key
let containsKey = _cache.ContainsKey(key)
where containsKey
select _cache[key]).ToList();
return list;
}
/// <summary>
/// Retrives all objects of the specified type
/// </summary>
/// <param name="type">The type of the objects to retrive, which implements <see cref="IEntity"/></param>
/// <returns></returns>
public IEnumerable<IEntity> GetAllByType(Type type)
{
var list = _cache.Keys.Where(key => key.Contains(type.Name)).Select(key => _cache[key]).ToList();
return list;
}
/// <summary>
/// Saves an object in the registry cache
/// </summary>
/// <param name="type"> </param>
/// <param name="entity"></param>
public void Save(Type type, IEntity entity)
{
_cache.AddOrUpdate(GetCompositeId(type, entity.Key), entity, (x, y) => entity);
}
/// <summary>
/// Deletes an object from the registry cache
/// </summary>
/// <param name="type"> </param>
/// <param name="entity"></param>
public void Delete(Type type, IEntity entity)
{
IEntity entity1;
bool result = _cache.TryRemove(GetCompositeId(type, entity.Key), out entity1);
}
public void Clear()
{
_cache.Clear();
}
private string GetCompositeId(Type type, Guid id)
{
return string.Format("{0}-{1}", type.Name, id.ToString());
}
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.Caching;
using System.Threading;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.Caching
{
/// <summary>
/// The Runtime Cache provider looks up objects in the Runtime cache for fast retrival
/// </summary>
internal sealed class RuntimeCacheProvider : IRepositoryCacheProvider
{
#region Singleton
private static volatile RuntimeCacheProvider _instance;
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
private RuntimeCacheProvider() { }
public static RuntimeCacheProvider Current
{
get
{
using (new WriteLock(Lock))
{
if (_instance == null) _instance = new RuntimeCacheProvider();
}
return _instance;
}
}
#endregion
private ObjectCache _memoryCache = new MemoryCache("in-memory");
private ConcurrentDictionary<string, string> _keyTracker = new ConcurrentDictionary<string, string>();
public IEntity GetById(Type type, Guid id)
{
var key = GetCompositeId(type, id);
var item = _memoryCache.Get(key);
return item as IEntity;
}
public IEnumerable<IEntity> GetByIds(Type type, List<Guid> ids)
{
foreach (var guid in ids)
{
yield return _memoryCache.Get(GetCompositeId(type, guid)) as IEntity;
}
}
public IEnumerable<IEntity> GetAllByType(Type type)
{
foreach (var key in _keyTracker.Keys)
{
if (key.StartsWith(type.Name))
{
yield return _memoryCache.Get(key) as IEntity;
}
}
}
public void Save(Type type, IEntity entity)
{
var key = GetCompositeId(type, entity.Key);
var exists = _memoryCache.GetCacheItem(key) != null;
_keyTracker.TryAdd(key, key);
if (exists)
{
_memoryCache.Set(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
return;
}
_memoryCache.Add(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
}
public void Delete(Type type, IEntity entity)
{
string throwaway = null;
var key = GetCompositeId(type, entity.Key);
var keyBeSure = _keyTracker.TryGetValue(key, out throwaway);
object itemRemoved = _memoryCache.Remove(key);
_keyTracker.TryRemove(key, out throwaway);
}
private string GetCompositeId(Type type, Guid id)
{
return string.Format("{0}-{1}", type.Name, id.ToString());
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal class ContentFactory
{
internal static Content CreateContent(int key, IContentType contentType, DocumentDto documentDto, IEnumerable<PropertyDataDto> propertyDataDtos)
{
var properties = new List<Property>();
foreach (var dto in propertyDataDtos)
{
var propertyType =
contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Id == dto.PropertyTypeId);
properties.Add(propertyType.CreatePropertyFromRawValue(dto.GetValue));
}
return new Content(documentDto.ContentVersionDto.ContentDto.NodeDto.ParentId, contentType)
{
Id = key,
Key = documentDto.ContentVersionDto.ContentDto.NodeDto.UniqueId.HasValue ? documentDto.ContentVersionDto.ContentDto.NodeDto.UniqueId.Value : key.ToGuid(),
Name = documentDto.ContentVersionDto.ContentDto.NodeDto.Text,
Path = documentDto.ContentVersionDto.ContentDto.NodeDto.Path,
UserId = documentDto.ContentVersionDto.ContentDto.NodeDto.UserId.HasValue ? documentDto.ContentVersionDto.ContentDto.NodeDto.UserId.Value : documentDto.UserId,
Level = documentDto.ContentVersionDto.ContentDto.NodeDto.Level,
ParentId = documentDto.ContentVersionDto.ContentDto.NodeDto.ParentId,
SortOrder = documentDto.ContentVersionDto.ContentDto.NodeDto.SortOrder,
Trashed = documentDto.ContentVersionDto.ContentDto.NodeDto.Trashed,
Published = documentDto.Published,
CreatedDate = documentDto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
ModifiedDate = documentDto.ContentVersionDto.VersionDate,
ExpireDate = documentDto.ExpiresDate,
ReleaseDate = documentDto.ReleaseDate,
Version = documentDto.ContentVersionDto.VersionId,
Properties = new PropertyCollection(properties)
};
}
internal static NodeDto CreateNodeDto(IContent entity, string nodeObjectType)
{
var nodeDto = new NodeDto
{
CreateDate = entity.CreatedDate,
NodeId = entity.Id,
Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
NodeObjectType = new Guid(nodeObjectType),
ParentId = entity.ParentId,
Path = entity.Path,
SortOrder = entity.SortOrder,
Text = entity.Name,
Trashed = entity.Trashed,
UniqueId = entity.Key,
UserId = entity.UserId
};
return nodeDto;
}
internal static NodeDto CreateNodeDto(IContent entity, string nodeObjectType, string path, int level, int sortOrder)
{
var nodeDto = new NodeDto
{
CreateDate = entity.CreatedDate,
NodeId = entity.Id,
Level = short.Parse(level.ToString(CultureInfo.InvariantCulture)),
NodeObjectType = new Guid(nodeObjectType),
ParentId = entity.ParentId,
Path = path,
SortOrder = sortOrder,
Text = entity.Name,
Trashed = entity.Trashed,
UniqueId = entity.Key,
UserId = entity.UserId
};
return nodeDto;
}
internal static ContentDto CreateContentDto(IContent entity, int primaryKey = 0)
{
var contentDto = new ContentDto
{
NodeId = entity.Id,
ContentType = entity.ContentTypeId
};
if (primaryKey > 0)
{
contentDto.PrimaryKey = primaryKey;
}
return contentDto;
}
internal static ContentVersionDto CreateContentVersionDto(IContent entity)
{
var contentVersionDto = new ContentVersionDto
{
NodeId = entity.Id,
VersionDate = entity.ModifiedDate,
VersionId = entity.Version
};
return contentVersionDto;
}
internal static DocumentDto CreateDocumentDto(IContent entity)
{
//NOTE Currently doesn't add Alias and templateId (legacy stuff that eventually will go away)
var documentDto = new DocumentDto
{
ExpiresDate = entity.ExpireDate,
Newest = true,
NodeId = entity.Id,
Published = entity.Published,
ReleaseDate = entity.ReleaseDate,
Text = entity.Name,
UpdateDate = entity.ModifiedDate,
UserId = entity.UserId,
VersionId = entity.Version
};
return documentDto;
}
internal static List<PropertyDataDto> CreateProperties(int id, Guid version, IEnumerable<Property> properties)
{
var propertyDataDtos = new List<PropertyDataDto>();
/*var serviceStackSerializer = new ServiceStackXmlSerializer();
var service = new SerializationService(serviceStackSerializer);*/
foreach (var property in properties)
{
var dto = new PropertyDataDto { NodeId = id, PropertyTypeId = property.PropertyTypeId, VersionId = version };
/*if (property.Value is IEditorModel)
{
var result = service.ToStream(property.Value);
dto.Text = result.ResultStream.ToJsonString();
}*/
if (property.Value is int)
{
dto.Integer = int.Parse(property.Value.ToString());
}
else if (property.Value is DateTime)
{
dto.Date = DateTime.Parse(property.Value.ToString());
}
else if (property.Value is string)
{
dto.Text = property.Value.ToString();
}
else if (property.Value != null)
{
dto.VarChar = property.Value.ToString();//TODO Check how/when NVarChar is actually set/used
}
propertyDataDtos.Add(dto);
}
return propertyDataDtos;
}
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Reflection;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Mappers
{
internal class ModelDtoMapper : IMapper
{
public void GetTableInfo(Type t, TableInfo ti)
{ }
public bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn)
{
if (pi.DeclaringType == typeof(Content) || pi.DeclaringType == typeof(IContent))
{
switch (pi.Name)
{
case "Trashed":
columnName = "[umbracoNode].[trashed]";
return true;
case "ParentId":
columnName = "[umbracoNode].[parentID]";
return true;
case "UserId":
columnName = "[umbracoNode].[nodeUser]";
return true;
case "Level":
columnName = "[umbracoNode].[level]";
return true;
case "Path":
columnName = "[umbracoNode].[path]";
return true;
case "SortOrder":
columnName = "[umbracoNode].[sortOrder]";
return true;
case "NodeId":
columnName = "[umbracoNode].[id]";
return true;
case "Published":
columnName = "[cmsDocument].[published]";
return true;
case "Key":
columnName = "[umbracoNode].[uniqueID]";
return true;
case "CreatedDate":
columnName = "[umbracoNode].[createDate]";
return true;
case "Name":
columnName = "[umbracoNode].[text]";
return true;
}
}
if (pi.DeclaringType == typeof(ContentType) || pi.DeclaringType == typeof(IContentType))
{
switch (pi.Name)
{
case "Alias":
columnName = "[cmsContentType].[alias]";
return true;
case "Icon":
columnName = "[cmsContentType].[icon]";
return true;
case "Thumbnail":
columnName = "[cmsContentType].[thumbnail]";
return true;
case "Description":
columnName = "[cmsContentType].[description]";
return true;
}
}
return true;
}
public Func<object, object> GetFromDbConverter(PropertyInfo pi, Type sourceType)
{
return null;
}
public Func<object, object> GetToDbConverter(Type sourceType)
{
return null;
}
}
}

View File

@@ -0,0 +1,572 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Persistence.Querying
{
internal class ExpressionHelper<T>
{
private string selectExpression = string.Empty;
private string whereExpression;
private string groupBy = string.Empty;
private string havingExpression;
private string orderBy = string.Empty;
IList<string> updateFields = new List<string>();
IList<string> insertFields = new List<string>();
private string sep = string.Empty;
private bool useFieldName = false;
private Database.PocoData pd;
public ExpressionHelper()
{
Database.Mapper = new ModelDtoMapper();
pd = new Database.PocoData(typeof(T));
}
protected internal virtual string Visit(Expression exp)
{
if (exp == null) return string.Empty;
switch (exp.NodeType)
{
case ExpressionType.Lambda:
return VisitLambda(exp as LambdaExpression);
case ExpressionType.MemberAccess:
return VisitMemberAccess(exp as MemberExpression);
case ExpressionType.Constant:
return VisitConstant(exp as ConstantExpression);
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Divide:
case ExpressionType.Modulo:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.Coalesce:
case ExpressionType.ArrayIndex:
case ExpressionType.RightShift:
case ExpressionType.LeftShift:
case ExpressionType.ExclusiveOr:
return "(" + VisitBinary(exp as BinaryExpression) + ")";
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.ArrayLength:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
return VisitUnary(exp as UnaryExpression);
case ExpressionType.Parameter:
return VisitParameter(exp as ParameterExpression);
case ExpressionType.Call:
return VisitMethodCall(exp as MethodCallExpression);
case ExpressionType.New:
return VisitNew(exp as NewExpression);
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
return VisitNewArray(exp as NewArrayExpression);
default:
return exp.ToString();
}
}
protected virtual string VisitLambda(LambdaExpression lambda)
{
if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ")
{
MemberExpression m = lambda.Body as MemberExpression;
if (m.Expression != null)
{
string r = VisitMemberAccess(m);
return string.Format("{0}={1}", r, GetQuotedTrueValue());
}
}
return Visit(lambda.Body);
}
protected virtual string VisitBinary(BinaryExpression b)
{
string left, right;
var operand = BindOperant(b.NodeType); //sep= " " ??
if (operand == "AND" || operand == "OR")
{
MemberExpression m = b.Left as MemberExpression;
if (m != null && m.Expression != null)
{
string r = VisitMemberAccess(m);
left = string.Format("{0}={1}", r, GetQuotedTrueValue());
}
else
{
left = Visit(b.Left);
}
m = b.Right as MemberExpression;
if (m != null && m.Expression != null)
{
string r = VisitMemberAccess(m);
right = string.Format("{0}={1}", r, GetQuotedTrueValue());
}
else
{
right = Visit(b.Right);
}
}
else
{
left = Visit(b.Left);
right = Visit(b.Right);
}
if (operand == "=" && right == "null") operand = "is";
else if (operand == "<>" && right == "null") operand = "is not";
else if (operand == "=" || operand == "<>")
{
if (IsTrueExpression(right)) right = GetQuotedTrueValue();
else if (IsFalseExpression(right)) right = GetQuotedFalseValue();
if (IsTrueExpression(left)) left = GetQuotedTrueValue();
else if (IsFalseExpression(left)) left = GetQuotedFalseValue();
}
switch (operand)
{
case "MOD":
case "COALESCE":
return string.Format("{0}({1},{2})", operand, left, right);
default:
return left + sep + operand + sep + right;
}
}
protected virtual string VisitMemberAccess(MemberExpression m)
{
if (m.Expression != null &&
m.Expression.NodeType == ExpressionType.Parameter
&& m.Expression.Type == typeof(T))
{
string field = GetFieldName(pd, m.Member.Name);
return field;
}
if (m.Expression != null && m.Expression.NodeType != ExpressionType.Constant)
{
Database.Mapper = new ModelDtoMapper();
var def = new Database.PocoData(m.Expression.Type);
string field = GetFieldName(def, m.Member.Name);
return field;
}
var member = Expression.Convert(m, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o != null ? o.GetType() : null);
}
protected virtual string VisitNew(NewExpression nex)
{
// TODO : check !
var member = Expression.Convert(nex, typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
try
{
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o.GetType());
}
catch (System.InvalidOperationException)
{ // FieldName ?
List<Object> exprs = VisitExpressionList(nex.Arguments);
var r = new StringBuilder();
foreach (Object e in exprs)
{
r.AppendFormat("{0}{1}",
r.Length > 0 ? "," : "",
e);
}
return r.ToString();
}
}
protected virtual string VisitParameter(ParameterExpression p)
{
return p.Name;
}
protected virtual string VisitConstant(ConstantExpression c)
{
if (c.Value == null)
return "null";
else if (c.Value.GetType() == typeof(bool))
{
object o = GetQuotedValue(c.Value, c.Value.GetType());
return string.Format("({0}={1})", GetQuotedTrueValue(), o);
}
else
return GetQuotedValue(c.Value, c.Value.GetType());
}
protected virtual string VisitUnary(UnaryExpression u)
{
switch (u.NodeType)
{
case ExpressionType.Not:
string o = Visit(u.Operand);
if (IsFieldName(o)) o = o + "=" + GetQuotedValue(true, typeof(bool));
return "NOT (" + o + ")";
default:
return Visit(u.Operand);
}
}
protected virtual string VisitMethodCall(MethodCallExpression m)
{
List<Object> args = this.VisitExpressionList(m.Arguments);
Object r;
if (m.Object != null)
r = Visit(m.Object);
else
{
r = args[0];
args.RemoveAt(0);
}
switch (m.Method.Name)
{
case "ToUpper":
return string.Format("upper({0})", r);
case "ToLower":
return string.Format("lower({0})", r);
case "StartsWith":
return string.Format("upper({0}) starting with {1} ", r, args[0].ToString().ToUpper());
case "EndsWith":
return string.Format("upper({0}) like '%{1}'", r, RemoveQuote(args[0].ToString()).ToUpper());
case "Contains":
return string.Format("upper({0}) like '%{1}%'", r, RemoveQuote(args[0].ToString()).ToUpper());
case "Substring":
var startIndex = Int32.Parse(args[0].ToString()) + 1;
if (args.Count == 2)
{
var length = Int32.Parse(args[1].ToString());
return string.Format("substring({0} from {1} for {2})",
r,
startIndex,
length);
}
else
return string.Format("substring({0} from {1})",
r,
startIndex);
case "Round":
case "Floor":
case "Ceiling":
case "Coalesce":
case "Abs":
case "Sum":
return string.Format("{0}({1}{2})",
m.Method.Name,
r,
args.Count == 1 ? string.Format(",{0}", args[0]) : "");
case "Concat":
var s = new StringBuilder();
foreach (Object e in args)
{
s.AppendFormat(" || {0}", e);
}
return string.Format("{0}{1}", r, s.ToString());
case "In":
var member = Expression.Convert(m.Arguments[1], typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
var inArgs = getter() as object[];
var sIn = new StringBuilder();
foreach (Object e in inArgs)
{
if (e.GetType().ToString() != "System.Collections.Generic.List`1[System.Object]")
{
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
GetQuotedValue(e, e.GetType()));
}
else
{
var listArgs = e as IList<Object>;
foreach (Object el in listArgs)
{
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
GetQuotedValue(el, el.GetType()));
}
}
}
return string.Format("{0} {1} ({2})", r, m.Method.Name, sIn.ToString());
case "Desc":
return string.Format("{0} DESC", r);
case "Alias":
case "As":
return string.Format("{0} As {1}", r,
GetQuotedColumnName(RemoveQuoteFromAlias(RemoveQuote(args[0].ToString()))));
case "ToString":
return r.ToString();
default:
var s2 = new StringBuilder();
foreach (Object e in args)
{
s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType()));
}
return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString());
}
}
protected virtual List<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
{
var list = new List<Object>();
for (int i = 0, n = original.Count; i < n; i++)
{
if (original[i].NodeType == ExpressionType.NewArrayInit ||
original[i].NodeType == ExpressionType.NewArrayBounds)
{
list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression));
}
else
list.Add(Visit(original[i]));
}
return list;
}
protected virtual string VisitNewArray(NewArrayExpression na)
{
List<Object> exprs = VisitExpressionList(na.Expressions);
var r = new StringBuilder();
foreach (Object e in exprs)
{
r.Append(r.Length > 0 ? "," + e : e);
}
return r.ToString();
}
protected virtual List<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
{
List<Object> exprs = VisitExpressionList(na.Expressions);
return exprs;
}
protected virtual string BindOperant(ExpressionType e)
{
switch (e)
{
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.AndAlso:
return "AND";
case ExpressionType.OrElse:
return "OR";
case ExpressionType.Add:
return "+";
case ExpressionType.Subtract:
return "-";
case ExpressionType.Multiply:
return "*";
case ExpressionType.Divide:
return "/";
case ExpressionType.Modulo:
return "MOD";
case ExpressionType.Coalesce:
return "COALESCE";
default:
return e.ToString();
}
}
public virtual string GetQuotedTableName(string tableName)
{
return string.Format("\"{0}\"", tableName);
}
public virtual string GetQuotedColumnName(string columnName)
{
return string.Format("\"{0}\"", columnName);
}
public virtual string GetQuotedName(string name)
{
return string.Format("\"{0}\"", name);
}
private string GetQuotedTrueValue()
{
return GetQuotedValue(true, typeof(bool));
}
private string GetQuotedFalseValue()
{
return GetQuotedValue(false, typeof(bool));
}
public virtual string GetQuotedValue(object value, Type fieldType)
{
if (value == null) return "NULL";
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
{
//if (TypeSerializer.CanCreateFromString(fieldType))
//{
// return "'" + EscapeParam(TypeSerializer.SerializeToString(value)) + "'";
//}
throw new NotSupportedException(
string.Format("Property of type: {0} is not supported", fieldType.FullName));
}
if (fieldType == typeof(int))
return ((int)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(float))
return ((float)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(double))
return ((double)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(decimal))
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
return ShouldQuoteValue(fieldType)
? "'" + EscapeParam(value) + "'"
: value.ToString();
}
public virtual string EscapeParam(object paramValue)
{
return paramValue.ToString().Replace("'", "''");
}
public virtual bool ShouldQuoteValue(Type fieldType)
{
return true;
}
protected virtual string GetFieldName(Database.PocoData pocoData, string name)
{
var column = pocoData.Columns.FirstOrDefault(x => x.Value.PropertyInfo.Name == name);
return column.Value.ColumnName;
}
protected virtual string GetFieldName(string name)
{
if (useFieldName)
{
//FieldDefinition fd = modelDef.FieldDefinitions.FirstOrDefault(x => x.Name == name);
//string fn = fd != default(FieldDefinition) ? fd.FieldName : name;
//return OrmLiteConfig.DialectProvider.GetQuotedColumnName(fn);
return "[" + name + "]";
}
else
{
return name;
}
}
protected string RemoveQuote(string exp)
{
if (exp.StartsWith("'") && exp.EndsWith("'"))
{
exp = exp.Remove(0, 1);
exp = exp.Remove(exp.Length - 1, 1);
}
return exp;
}
protected string RemoveQuoteFromAlias(string exp)
{
if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
&&
(exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'")))
{
exp = exp.Remove(0, 1);
exp = exp.Remove(exp.Length - 1, 1);
}
return exp;
}
private string GetTrueExpression()
{
object o = GetQuotedTrueValue();
return string.Format("({0}={1})", o, o);
}
private string GetFalseExpression()
{
return string.Format("({0}={1})",
GetQuotedTrueValue(),
GetQuotedFalseValue());
}
private bool IsTrueExpression(string exp)
{
return (exp == GetTrueExpression());
}
private bool IsFalseExpression(string exp)
{
return (exp == GetFalseExpression());
}
protected bool IsFieldName(string quotedExp)
{
return true;
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
using System.Linq.Expressions;
namespace Umbraco.Core.Persistence.Querying
{
public interface IQuery<T>
{
IQuery<T> Where(Expression<Func<T, bool>> predicate);
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Umbraco.Core.Persistence.Querying
{
public class Query<T> : IQuery<T>
{
private readonly ExpressionHelper<T> _expresionist = new ExpressionHelper<T>();
private readonly List<string> _wheres = new List<string>();
public Query()
: base()
{
}
public static IQuery<T> Builder
{
get
{
return new Query<T>();
}
}
public virtual IQuery<T> Where(Expression<Func<T, bool>> predicate)
{
if (predicate != null)
{
string whereExpression = _expresionist.Visit(predicate);
_wheres.Add(whereExpression);
}
return this;
}
public List<string> WhereClauses()
{
return _wheres;
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
namespace Umbraco.Core.Persistence.Querying
{
public class SqlTranslator<T>
{
private readonly Sql _sql;
public SqlTranslator(Sql sql, IQuery<T> query)
{
if (sql == null)
throw new Exception("Sql cannot be null");
var query1 = query as Query<T>;
if (query1 == null)
throw new Exception("Query cannot be null");
_sql = sql;
foreach (var clause in query1.WhereClauses())
{
_sql.Where(clause);
}
}
public Sql Translate()
{
return _sql;
}
public override string ToString()
{
return _sql.SQL;
}
}
}

View File

@@ -0,0 +1,48 @@
using System.Collections.Generic;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Relators
{
internal class TabPropertyTypeRelator
{
internal TabDto current;
internal TabDto Map(TabDto a, PropertyTypeDto p, DataTypeDto d)
{
// Terminating call. Since we can return null from this function
// we need to be ready for PetaPoco to callback later with null
// parameters
if (a == null)
return current;
//Set the PropertyTypeDto's DataTypeDto object
if (p.DataTypeId == d.DataTypeId)
p.DataTypeDto = d;
// Is this the same Tab as the current one we're processing
if (current != null && current.Id == a.Id)
{
// Yes, just add this PropertyType to the current Tab's collection of PropertyTypes
current.PropertyTypeDtos.Add(p);
// Return null to indicate we're not done with this Tab yet
return null;
}
// This is a different Tab to the current one, or this is the
// first time through and we don't have a Tab yet
// Save the current Tab
var prev = current;
// Setup the new current Tab
current = a;
current.PropertyTypeDtos = new List<PropertyTypeDto>();
current.PropertyTypeDtos.Add(p);
// Return the now populated previous Tab (or null if first time through)
return prev;
}
}
}

View File

@@ -0,0 +1,293 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Represents a repository for doing CRUD operations for <see cref="Content"/>
/// </summary>
internal class ContentRepository : Repository<IContent>, IContentRepository
{
private const string NodeObjectType = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972";
private readonly IContentTypeRepository _contentTypeRepository;
public ContentRepository(IUnitOfWork work, IContentTypeRepository contentTypeRepository)
: base(work)
{
_contentTypeRepository = contentTypeRepository;
}
internal ContentRepository(IUnitOfWork work, IContentTypeRepository contentTypeRepository, IRepositoryCacheProvider registry)
: base(work, registry)
{
_contentTypeRepository = contentTypeRepository;
}
protected override void PerformAdd(IContent entity)
{
((Content)entity).AddingEntity();
//NOTE Should the logic below have some kind of fallback for empty parent ids ?
//Logic for setting Path, Level and SortOrder
var parent = UnitOfWork.Storage.First<NodeDto>("WHERE id = @ParentId", new { ParentId = entity.ParentId });
int level = parent.Level + 1;
int sortOrder =
UnitOfWork.Storage.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoNode WHERE parentID = @ParentId AND nodeObjectType = @NodeObjectType",
new { ParentId = entity.ParentId, NodeObjectType = NodeObjectType });
//Create the (base) node data - umbracoNode
var nodeDto = ContentFactory.CreateNodeDto(entity, NodeObjectType, parent.Path, level, sortOrder);
var o = UnitOfWork.Storage.IsNew(nodeDto) ? Convert.ToInt32(UnitOfWork.Storage.Insert(nodeDto)) : UnitOfWork.Storage.Update(nodeDto);
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
UnitOfWork.Storage.Update(nodeDto);
//Update entity with correct values
entity.Id = nodeDto.NodeId; //Set Id on entity to ensure an Id is set
entity.Path = nodeDto.Path;
entity.SortOrder = sortOrder;
entity.Level = level;
//Create the Content specific data - cmsContent
var contentDto = ContentFactory.CreateContentDto(entity);
UnitOfWork.Storage.Insert(contentDto);
//Create the first version - cmsContentVersion
//Assumes a new Version guid and Version date (modified date) has been set
var contentVersionDto = ContentFactory.CreateContentVersionDto(entity);
UnitOfWork.Storage.Insert(contentVersionDto);
//Create the Document specific data for this version - cmsDocument
//Assumes a new Version guid has been generated
var documentDto = ContentFactory.CreateDocumentDto(entity);
UnitOfWork.Storage.Insert(documentDto);
//Create the PropertyData for this version - cmsPropertyData
var propertyDataDtos = ContentFactory.CreateProperties(entity.Id, entity.Version, entity.Properties);
//Add Properties
foreach (var propertyDataDto in propertyDataDtos)
{
UnitOfWork.Storage.Insert(propertyDataDto);
}
((Content)entity).ResetDirtyProperties();
}
protected override void PerformUpdate(IContent entity)
{
//Updates Modified date and Version Guid
((Content)entity).UpdatingEntity();
//Updates the (base) node data - umbracoNode
var nodeDto = ContentFactory.CreateNodeDto(entity, NodeObjectType);
var o = UnitOfWork.Storage.Update(nodeDto);
//Look up Content entry to get Primary for updating the DTO
var contentDto = UnitOfWork.Storage.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
//Only update this DTO if the contentType has actually changed
if (contentDto.ContentType != entity.ContentTypeId)
{
//Create the Content specific data - cmsContent
var newContentDto = ContentFactory.CreateContentDto(entity, contentDto.PrimaryKey);
UnitOfWork.Storage.Update(newContentDto);
}
//Look up entries in cmsDocument table to set newest = false
var documentDtos = UnitOfWork.Storage.Fetch<DocumentDto>("WHERE nodeId = @Id AND newest = '1'", new { Id = entity.Id });
foreach (var docDto in documentDtos)
{
var dto = docDto;
dto.Newest = false;
UnitOfWork.Storage.Update(dto);
}
//Create a new version - cmsContentVersion
//Assumes a new Version guid and Version date (modified date) has been set
var contentVersionDto = ContentFactory.CreateContentVersionDto(entity);
UnitOfWork.Storage.Insert(contentVersionDto);
//Create the Document specific data for this version - cmsDocument
//Assumes a new Version guid has been generated
var documentDto = ContentFactory.CreateDocumentDto(entity);
UnitOfWork.Storage.Insert(documentDto);
//Create the PropertyData for this version - cmsPropertyData
var propertyDataDtos = ContentFactory.CreateProperties(entity.Id, entity.Version, entity.Properties);
//Add Properties
foreach (var propertyDataDto in propertyDataDtos)
{
UnitOfWork.Storage.Insert(propertyDataDto);
}
((Content)entity).ResetDirtyProperties();
}
protected override void PerformDelete(IContent entity)
{
//Remove Notifications
UnitOfWork.Storage.Delete<User2NodeNotifyDto>("WHERE nodeId = @Id", new { Id = entity.Id });
//Remove Permissions
UnitOfWork.Storage.Delete<User2NodePermissionDto>("WHERE nodeId = @Id", new { Id = entity.Id });
//Remove associated tags
UnitOfWork.Storage.Delete<TagRelationshipDto>("WHERE nodeId = @Id", new { Id = entity.Id });
//Delete entry in Document table
UnitOfWork.Storage.Delete<DocumentDto>("WHERE NodeId = @Id", new { Id = entity.Id });
//Delete Properties
UnitOfWork.Storage.Delete<PropertyDataDto>("WHERE contentNodeId = @Id", new { Id = entity.Id });
//Delete Preview Xml
UnitOfWork.Storage.Delete<PreviewXmlDto>("WHERE nodeId = @Id", new { Id = entity.Id });
//Delete Version history
UnitOfWork.Storage.Delete<ContentVersionDto>("WHERE ContentId = @Id", new { Id = entity.Id });
//Delete Content Xml
UnitOfWork.Storage.Delete<ContentXmlDto>("WHERE nodeID = @Id", new { Id = entity.Id });
//Delete Content specific data
UnitOfWork.Storage.Delete<ContentDto>("WHERE NodeId = @Id", new { Id = entity.Id });
//Delete (base) node data
UnitOfWork.Storage.Delete<NodeDto>("WHERE uniqueID = @Id", new { Id = entity.Key });
}
protected override IContent PerformGet(int id)
{
var contentSql = BaseSqlClause(false);
contentSql.Where("[cmsDocument].[nodeId] = @Id", new { Id = id });
contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC");
var documentDto = UnitOfWork.Storage.Query<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(contentSql).FirstOrDefault();
if (documentDto == null)
return null;
var propertySql = new Sql();
propertySql.Select("*");
propertySql.From("cmsPropertyData");
propertySql.InnerJoin("cmsPropertyType ON ([cmsPropertyData].[propertytypeid] = [cmsPropertyType].[id])");
propertySql.Where("[cmsPropertyData].[contentNodeId] = @Id", new { Id = id });
propertySql.Where("[cmsPropertyData].[versionId] = @VersionId", new { VersionId = documentDto.ContentVersionDto.VersionId });
var propertyDataDtos = UnitOfWork.Storage.Fetch<PropertyDataDto, PropertyTypeDto>(propertySql);
var contentType = _contentTypeRepository.Get(documentDto.ContentVersionDto.ContentDto.ContentType);
var content = ContentFactory.CreateContent(id, contentType, documentDto, propertyDataDtos);
content.ResetDirtyProperties();
return content;
}
protected override IEnumerable<IContent> PerformGetAll(params int[] ids)
{
if (ids.Any())
{
foreach (var id in ids)
{
yield return Get(id);
}
}
else
{
var nodeDtos = UnitOfWork.Storage.Fetch<NodeDto>("WHERE nodeObjectType = @NodeObjectType", new { NodeObjectType = NodeObjectType });
foreach (var nodeDto in nodeDtos)
{
yield return Get(nodeDto.NodeId);
}
}
}
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
{
var sqlClause = BaseSqlClause(false);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate();
var documentDtos = UnitOfWork.Storage.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(sql);
foreach (var documentDto in documentDtos)
{
yield return Get(documentDto.NodeId);
}
}
protected override bool PerformExists(int id)
{
return UnitOfWork.Storage.Exists<NodeDto>(id);
}
protected override int PerformCount(IQuery<IContent> query)
{
var sqlClause = BaseSqlClause(true);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate();
return UnitOfWork.Storage.ExecuteScalar<int>(sql);
}
public IEnumerable<IContent> GetAllVersions(int id)
{
var contentSql = BaseSqlClause(false);
contentSql.Where("[cmsDocument].[nodeId] = @Id", new { Id = id });
contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC");
var documentDtos = UnitOfWork.Storage.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(contentSql);
foreach (var dto in documentDtos)
{
yield return GetByVersion(id, dto.ContentVersionDto.VersionId);
}
}
public IContent GetByVersion(int id, Guid versionId)
{
var contentSql = BaseSqlClause(false);
contentSql.Where("[cmsDocument].[nodeId] = @Id", new { Id = id });
contentSql.Where("[cmsContentVersion].[VersionId] = @VersionId", new { VersionId = versionId });
contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC");
var documentDto = UnitOfWork.Storage.Query<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(contentSql).FirstOrDefault();
if (documentDto == null)
return null;
var propertySql = new Sql();
propertySql.Select("*");
propertySql.From("cmsPropertyData");
propertySql.InnerJoin("cmsPropertyType ON [cmsPropertyData].[propertytypeid] = [cmsPropertyType].[id]");
propertySql.Where("[cmsPropertyData].[contentNodeId] = @Id", new { Id = id });
propertySql.Where("[cmsPropertyData].[versionId] = @VersionId", new { VersionId = versionId });
var propertyDataDtos = UnitOfWork.Storage.Query<PropertyDataDto, PropertyTypeDto>(propertySql);
var contentType = _contentTypeRepository.Get(documentDto.ContentVersionDto.ContentDto.ContentType);
var content = ContentFactory.CreateContent(id, contentType, documentDto, propertyDataDtos);
content.ResetDirtyProperties();
return content;
}
private Sql BaseSqlClause(bool doCount)
{
var sql = new Sql();
sql.Select(doCount ? "COUNT(*)" : "*");
sql.From("cmsDocument");
sql.InnerJoin("cmsContentVersion ON ([cmsDocument].[versionId]=[cmsContentVersion].[VersionId])");
sql.InnerJoin("cmsContent ON ([cmsContentVersion].[ContentId]=[cmsContent].[nodeId])");
sql.InnerJoin("umbracoNode ON ([cmsContent].[nodeId]=[umbracoNode].[id])");
sql.Where("[umbracoNode].[nodeObjectType]=@NodeObjectType", new { NodeObjectType = NodeObjectType });
return sql;
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentRepository : IRepository<IContent>
{
IEnumerable<IContent> GetAllVersions(int id);
IContent GetByVersion(int id, Guid versionId);
}
}

View File

@@ -0,0 +1,9 @@
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentTypeRepository : IRepository<IContentType>
{
}
}

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Defines the implementation of a Repository
/// </summary>
/// <typeparam name="TEntity">Type of <see cref="IAggregateRoot"/> entity for which the repository is used</typeparam>
public interface IRepository<TEntity> where TEntity : class, IAggregateRoot
{
/// <summary>
/// Adds or Updates an Entity
/// </summary>
/// <param name="entity"></param>
void AddOrUpdate(TEntity entity);
/// <summary>
/// Deletes an Entity
/// </summary>
/// <param name="entity"></param>
void Delete(TEntity entity);
/// <summary>
/// Gets an Entity by Id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
TEntity Get(int id);
/// <summary>
/// Gets all entities of the spefified type
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
IEnumerable<TEntity> GetAll(params int[] ids);
/// <summary>
/// Gets all entities of the spefified type and query
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
IEnumerable<TEntity> GetByQuery(IQuery<TEntity> query);
/// <summary>
/// Boolean indicating whether an Entity with the specified Id exists
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
bool Exists(int id);
/// <summary>
/// Returns the count for the specified query
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
int Count(IQuery<TEntity> query);
/// <summary>
/// Sets the Unit Of Work for the Repository
/// </summary>
/// <param name="work"></param>
void SetUnitOfWork(IUnitOfWork work);
}
}

View File

@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Represent an abstract Repository, which is the base of the Repository implementations
/// </summary>
/// <typeparam name="TEntity"></typeparam>
internal abstract class Repository<TEntity> : IDisposable, IRepository<TEntity> where TEntity : class, IAggregateRoot
{
private IUnitOfWork _work;
private readonly IRepositoryCacheProvider _cache;
protected Repository(IUnitOfWork work)
: this(work, RuntimeCacheProvider.Current)
{
}
internal Repository(IUnitOfWork work, IRepositoryCacheProvider cache)
{
_work = work;
_cache = cache;
}
internal IUnitOfWork UnitOfWork
{
get { return _work; }
}
protected abstract void PerformAdd(TEntity entity);
protected abstract void PerformUpdate(TEntity entity);
public void AddOrUpdate(TEntity entity)
{
if (!entity.HasIdentity)
{
PerformAdd(entity);
}
else
{
PerformUpdate(entity);
}
_cache.Save(typeof(TEntity), entity);
}
protected abstract void PerformDelete(TEntity entity);
public void Delete(TEntity entity)
{
_cache.Delete(typeof(TEntity), entity);
PerformDelete(entity);
}
protected abstract TEntity PerformGet(int id);
public TEntity Get(int id)
{
var rEntity = _cache.GetById(typeof(TEntity), ConvertIdToGuid(id));
if (rEntity != null)
{
return (TEntity)rEntity;
}
var entity = PerformGet(id);
if (entity != null)
{
_cache.Save(typeof(TEntity), entity);
}
return entity;
}
protected abstract IEnumerable<TEntity> PerformGetAll(params int[] ids);
public IEnumerable<TEntity> GetAll(params int[] ids)
{
if (ids.Any())
{
var entities = _cache.GetByIds(typeof(TEntity), ids.Select(ConvertIdToGuid).ToList());
if (ids.Count().Equals(entities.Count()))
return entities.Select(x => (TEntity)x);
}
else
{
var allEntities = _cache.GetAllByType(typeof(TEntity));
if (allEntities.Any())
return allEntities.Select(x => (TEntity)x);
}
var entityCollection = PerformGetAll(ids);
foreach (var entity in entityCollection)
{
if (entity != null)
{
_cache.Save(typeof(TEntity), entity);
}
}
return entityCollection;
}
protected abstract IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query);
public IEnumerable<TEntity> GetByQuery(IQuery<TEntity> query)
{
return PerformGetByQuery(query);
}
protected abstract bool PerformExists(int id);
public bool Exists(int id)
{
var rEntity = _cache.GetById(typeof(TEntity), ConvertIdToGuid(id));
if (rEntity != null)
{
return true;
}
return PerformExists(id);
}
protected abstract int PerformCount(IQuery<TEntity> query);
public int Count(IQuery<TEntity> query)
{
return PerformCount(query);
}
public void SetUnitOfWork(IUnitOfWork work)
{
_work = work;
}
public virtual void Dispose()
{
_work.Dispose();
}
/// <summary>
/// Internal method that handles the convertion of an object Id
/// to an Integer and then a Guid Id.
/// </summary>
/// <remarks>In the future it should be possible to change this method
/// so it converts from object to guid if/when we decide to go from
/// int to guid based ids.</remarks>
/// <param name="id"></param>
/// <returns></returns>
protected virtual Guid ConvertIdToGuid(int id)
{
return id.ToGuid();
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace Umbraco.Core.Persistence.UnitOfWork
{
public interface IUnitOfWork : IDisposable
{
void Commit();
Database Storage { get; } //TODO consider replacing 'Database' with a datastorage adapter, so there is no direct dependency on PetaPoco
}
}

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Core.Persistence.UnitOfWork
{
public interface IUnitOfWorkProvider
{
IUnitOfWork GetUnitOfWork();
}
}

View File

@@ -37,6 +37,7 @@
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
@@ -81,6 +82,23 @@
<Compile Include="Models\Script.cs" />
<Compile Include="Models\Stylesheet.cs" />
<Compile Include="Models\Template.cs" />
<Compile Include="Persistence\Caching\InMemoryCacheProvider.cs" />
<Compile Include="Persistence\Caching\IRepositoryCacheProvider.cs" />
<Compile Include="Persistence\Caching\RuntimeCacheProvider.cs" />
<Compile Include="Persistence\Factories\ContentFactory.cs" />
<Compile Include="Persistence\Mappers\ModelDtoMapper.cs" />
<Compile Include="Persistence\Querying\ExpressionHelper.cs" />
<Compile Include="Persistence\Querying\IQuery.cs" />
<Compile Include="Persistence\Querying\Query.cs" />
<Compile Include="Persistence\Querying\SqlTranslator.cs" />
<Compile Include="Persistence\Repositories\ContentRepository.cs" />
<Compile Include="Persistence\Repositories\IContentRepository.cs" />
<Compile Include="Persistence\Repositories\IContentTypeRepository.cs" />
<Compile Include="Persistence\Repositories\IRepository.cs" />
<Compile Include="Persistence\Relators\TabPropertyTypeRelator.cs" />
<Compile Include="Persistence\Repositories\Repository.cs" />
<Compile Include="Persistence\UnitOfWork\IUnitOfWork.cs" />
<Compile Include="Persistence\UnitOfWork\IUnitOfWorkProvider.cs" />
<Compile Include="PublishedContentExtensions.cs" />
<Compile Include="Dictionary\ICultureDictionary.cs" />
<Compile Include="Dynamics\ClassFactory.cs" />
@@ -271,13 +289,7 @@
<Name>umbraco.interfaces</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Persistence\Caching\" />
<Folder Include="Persistence\Factories\" />
<Folder Include="Persistence\Querying\" />
<Folder Include="Persistence\Repositories\" />
<Folder Include="Persistence\UnitOfWork\" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.