Refactoring Unit Of Work, adding TransactionTypes and a new Repository structure.

Adding new Factory structure.
Related to U4-973, U4-955 and U4-979
This commit is contained in:
Morten@Thinkpad-X220
2012-10-08 08:42:57 -02:00
parent ca6356170c
commit 660ecfa91b
20 changed files with 958 additions and 529 deletions

View File

@@ -0,0 +1,47 @@
using System.Threading;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.Persistence
{
/// <summary>
/// Provides access to the PetaPoco database as Singleton, so the database is created once in app lifecycle.
/// This is necessary for transactions to work properly
/// </summary>
public sealed class DatabaseFactory
{
#region Singleton
private static Database _database;
private static volatile DatabaseFactory _instance;
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
private DatabaseFactory() { }
public static DatabaseFactory Current
{
get
{
using (new WriteLock(Lock))
{
if (_instance == null)
{
_instance = new DatabaseFactory();
_database = new Database(GlobalSettings.DbDsn);
}
}
return _instance;
}
}
#endregion
/// <summary>
/// Returns an instance of the PetaPoco database
/// </summary>
public Database Database
{
get { return _database; }
}
}
}

View File

@@ -1,165 +0,0 @@
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,
CreateDate = documentDto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
UpdateDate = 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.CreateDate,
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.CreateDate,
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.UpdateDate,
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.UpdateDate,
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 };
//TODO Add complex (PropertyEditor) ValueModels to the Ntext/Nvarchar column as a serialized 'Object' (DataTypeDatabaseType.Object)
/*if (property.Value is IEditorModel)
{
var result = service.ToStream(property.Value);
dto.Text = result.ResultStream.ToJsonString();
}*/
if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer)
{
dto.Integer = int.Parse(property.Value.ToString());
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Date)
{
dto.Date = DateTime.Parse(property.Value.ToString());
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Ntext)
{
dto.Text = property.Value.ToString();
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Nvarchar)
{
dto.VarChar = property.Value.ToString();
}
propertyDataDtos.Add(dto);
}
return propertyDataDtos;
}
}
}

View File

@@ -0,0 +1,127 @@
using System;
using System.Globalization;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal class EntityFactory : IEntityFactory<IContent, DocumentDto>
{
private readonly IContentType _contentType;
private readonly Guid _nodeObjectTypeId;
private readonly int _id;
private int _primaryKey;
public EntityFactory(IContentType contentType, Guid nodeObjectTypeId, int id)
{
_contentType = contentType;
_nodeObjectTypeId = nodeObjectTypeId;
_id = id;
}
#region Implementation of IEntityFactory<IContent,DocumentDto>
public IContent BuildEntity(DocumentDto dto)
{
return new Content(dto.ContentVersionDto.ContentDto.NodeDto.ParentId, _contentType)
{
Id = _id,
Key =
dto.ContentVersionDto.ContentDto.NodeDto.UniqueId.HasValue
? dto.ContentVersionDto.ContentDto.NodeDto.UniqueId.Value
: _id.ToGuid(),
Name = dto.ContentVersionDto.ContentDto.NodeDto.Text,
Path = dto.ContentVersionDto.ContentDto.NodeDto.Path,
UserId =
dto.ContentVersionDto.ContentDto.NodeDto.UserId.HasValue
? dto.ContentVersionDto.ContentDto.NodeDto.UserId.Value
: dto.UserId,
Level = dto.ContentVersionDto.ContentDto.NodeDto.Level,
ParentId = dto.ContentVersionDto.ContentDto.NodeDto.ParentId,
SortOrder = dto.ContentVersionDto.ContentDto.NodeDto.SortOrder,
Trashed = dto.ContentVersionDto.ContentDto.NodeDto.Trashed,
Published = dto.Published,
CreateDate = dto.ContentVersionDto.ContentDto.NodeDto.CreateDate,
UpdateDate = dto.ContentVersionDto.VersionDate,
ExpireDate = dto.ExpiresDate,
ReleaseDate = dto.ReleaseDate,
Version = dto.ContentVersionDto.VersionId
};
}
public DocumentDto BuildDto(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.UpdateDate,
UserId = entity.UserId,
VersionId = entity.Version,
ContentVersionDto = BuildContentVersionDto(entity)
};
return documentDto;
}
#endregion
private ContentVersionDto BuildContentVersionDto(IContent entity)
{
var contentVersionDto = new ContentVersionDto
{
NodeId = entity.Id,
VersionDate = entity.UpdateDate,
VersionId = entity.Version,
ContentDto = BuildContentDto(entity)
};
return contentVersionDto;
}
private ContentDto BuildContentDto(IContent entity)
{
var contentDto = new ContentDto
{
NodeId = entity.Id,
ContentType = entity.ContentTypeId,
NodeDto = BuildNodeDto(entity)
};
if (_primaryKey > 0)
{
contentDto.PrimaryKey = _primaryKey;
}
return contentDto;
}
private NodeDto BuildNodeDto(IContent entity)
{
var nodeDto = new NodeDto
{
CreateDate = entity.CreateDate,
NodeId = entity.Id,
Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
NodeObjectType = _nodeObjectTypeId,
ParentId = entity.ParentId,
Path = entity.Path,
SortOrder = entity.SortOrder,
Text = entity.Name,
Trashed = entity.Trashed,
UniqueId = entity.Key,
UserId = entity.UserId
};
return nodeDto;
}
public void SetPrimaryKey(int primaryKey)
{
_primaryKey = primaryKey;
}
}
}

View File

@@ -0,0 +1,12 @@
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.Factories
{
internal interface IEntityFactory<TEntity, TDto>
where TEntity : class
where TDto : class
{
TEntity BuildEntity(TDto dto);
TDto BuildDto(TEntity entity);
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal class PropertyFactory : IEntityFactory<IEnumerable<Property>, IEnumerable<PropertyDataDto>>
{
private readonly IContentType _contentType;
private readonly Guid _version;
private readonly int _id;
#region Implementation of IEntityFactory<IContent,PropertyDataDto>
public PropertyFactory(IContentType contentType, Guid version, int id)
{
_contentType = contentType;
_version = version;
_id = id;
}
public IEnumerable<Property> BuildEntity(IEnumerable<PropertyDataDto> dtos)
{
var properties = new List<Property>();
foreach (var dto in dtos)
{
var propertyType =
_contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Id == dto.PropertyTypeId);
properties.Add(propertyType.CreatePropertyFromRawValue(dto.GetValue));
}
return properties;
}
public IEnumerable<PropertyDataDto> BuildDto(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 };
//TODO Add complex (PropertyEditor) ValueModels to the Ntext/Nvarchar column as a serialized 'Object' (DataTypeDatabaseType.Object)
/*if (property.Value is IEditorModel)
{
var result = service.ToStream(property.Value);
dto.Text = result.ResultStream.ToJsonString();
}*/
if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer)
{
dto.Integer = int.Parse(property.Value.ToString());
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Date)
{
dto.Date = DateTime.Parse(property.Value.ToString());
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Ntext)
{
dto.Text = property.Value.ToString();
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Nvarchar)
{
dto.VarChar = property.Value.ToString();
}
propertyDataDtos.Add(dto);
}
return propertyDataDtos;
}
#endregion
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
@@ -10,166 +11,31 @@ 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
internal class ContentRepository : PetaPocoRepositoryBase<int, IContent>, IContentRepository
{
private const string NodeObjectType = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972";
private readonly IContentTypeRepository _contentTypeRepository;
public ContentRepository(IUnitOfWork<Database> work, IContentTypeRepository contentTypeRepository)
public ContentRepository(IUnitOfWork work, IContentTypeRepository contentTypeRepository)
: base(work)
{
_contentTypeRepository = contentTypeRepository;
}
internal ContentRepository(IUnitOfWork<Database> work, IContentTypeRepository contentTypeRepository, IRepositoryCacheProvider registry)
: base(work, registry)
public ContentRepository(IUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository)
: base(work, cache)
{
_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 });
}
#region Overrides of RepositoryBase<IContent>
protected override IContent PerformGet(int id)
{
var contentSql = BaseSqlClause(false);
var contentSql = GetBaseQuery(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();
var documentDto = Database.Query<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(contentSql).FirstOrDefault();
if (documentDto == null)
return null;
@@ -181,11 +47,19 @@ namespace Umbraco.Core.Persistence.Repositories
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 propertyDataDtos = Database.Fetch<PropertyDataDto, PropertyTypeDto>(propertySql);
var contentType = _contentTypeRepository.Get(documentDto.ContentVersionDto.ContentDto.ContentType);
var content = ContentFactory.CreateContent(id, contentType, documentDto, propertyDataDtos);
content.ResetDirtyProperties();
//var content = ContentFactory.CreateContent(id, contentType, documentDto, propertyDataDtos);
var factory = new EntityFactory(contentType, NodeObjectTypeId, id);
var content = factory.BuildEntity(documentDto);
var propertyFactory = new PropertyFactory(contentType, documentDto.ContentVersionDto.VersionId, id);
var properties = propertyFactory.BuildEntity(propertyDataDtos);
content.Properties = new PropertyCollection(properties);
((Content)content).ResetDirtyProperties();
return content;
}
@@ -200,7 +74,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
var nodeDtos = UnitOfWork.Storage.Fetch<NodeDto>("WHERE nodeObjectType = @NodeObjectType", new { NodeObjectType = NodeObjectType });
var nodeDtos = Database.Fetch<NodeDto>("WHERE nodeObjectType = @NodeObjectType", new { NodeObjectType = NodeObjectTypeId });
foreach (var nodeDto in nodeDtos)
{
yield return Get(nodeDto.NodeId);
@@ -210,11 +84,11 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
{
var sqlClause = BaseSqlClause(false);
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate();
var documentDtos = UnitOfWork.Storage.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(sql);
var documentDtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(sql);
foreach (var documentDto in documentDtos)
{
@@ -224,25 +98,189 @@ namespace Umbraco.Core.Persistence.Repositories
protected override bool PerformExists(int id)
{
return UnitOfWork.Storage.Exists<NodeDto>(id);
return Database.Exists<NodeDto>(id);
}
#endregion
protected override int PerformCount(IQuery<IContent> query)
#region Overrides of PetaPocoRepositoryBase<IContent>
protected override Sql GetBaseQuery(bool isCount)
{
var sqlClause = BaseSqlClause(true);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate();
return UnitOfWork.Storage.ExecuteScalar<int>(sql);
var sql = new Sql();
sql.Select(isCount ? "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])");
return sql;
}
protected override Sql GetBaseWhereClause()
{
var sql = new Sql();
sql.Where("[umbracoNode].[nodeObjectType] = @NodeObjectType", new { NodeObjectType = NodeObjectTypeId });
return sql;
}
protected override IEnumerable<string> GetDeleteClauses()
{
var list = new List<string>
{
string.Format("DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id"),
string.Format("DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id"),
string.Format("DELETE FROM cmsTagRelationship WHERE nodeId = @Id"),
string.Format("DELETE FROM cmsDocument WHERE NodeId = @Id"),
string.Format("DELETE FROM cmsPropertyData WHERE contentNodeId = @Id"),
string.Format("DELETE FROM cmsPreviewXml WHERE nodeId = @Id"),
string.Format("DELETE FROM cmsContentVersion WHERE ContentId = @Id"),
string.Format("DELETE FROM cmsContentXml WHERE nodeID = @Id"),
string.Format("DELETE FROM cmsContent WHERE NodeId = @Id"),
string.Format("DELETE FROM umbracoNode WHERE id = @Id")
};
return list;
}
protected override Guid NodeObjectTypeId
{
get { return new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"); }
}
#endregion
#region Unit of Work Implementation
protected override void PersistNewItem(IContent entity)
{
((Content)entity).AddingEntity();
var factory = new EntityFactory(null, NodeObjectTypeId, entity.Id);
var dto = factory.BuildDto(entity);
//NOTE Should the logic below have some kind of fallback for empty parent ids ?
//Logic for setting Path, Level and SortOrder
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { ParentId = entity.ParentId });
int level = parent.Level + 1;
int sortOrder =
Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoNode WHERE parentID = @ParentId AND nodeObjectType = @NodeObjectType",
new { ParentId = entity.ParentId, NodeObjectType = NodeObjectTypeId });
//Create the (base) node data - umbracoNode
//var nodeDto = ContentFactory.CreateNodeDto(entity, NodeObjectTypeId, parent.Path, level, sortOrder);
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
Database.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);
var contentDto = dto.ContentVersionDto.ContentDto;
Database.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);
var contentVersionDto = dto.ContentVersionDto;
Database.Insert(contentVersionDto);
//Create the Document specific data for this version - cmsDocument
//Assumes a new Version guid has been generated
//var documentDto = ContentFactory.CreateDocumentDto(entity);
Database.Insert(dto);
//Create the PropertyData for this version - cmsPropertyData
var propertyFactory = new PropertyFactory(null, entity.Version, entity.Id);
var propertyDataDtos = propertyFactory.BuildDto(entity.Properties);
//var propertyDataDtos = ContentFactory.CreateProperties(entity.Id, entity.Version, entity.Properties);
//Add Properties
foreach (var propertyDataDto in propertyDataDtos)
{
Database.Insert(propertyDataDto);
}
((Content)entity).ResetDirtyProperties();
}
protected override void PersistUpdatedItem(IContent entity)
{
//Updates Modified date and Version Guid
((Content)entity).UpdatingEntity();
var factory = new EntityFactory(null, NodeObjectTypeId, entity.Id);
//Look up Content entry to get Primary for updating the DTO
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
factory.SetPrimaryKey(contentDto.PrimaryKey);
var dto = factory.BuildDto(entity);
//Updates the (base) node data - umbracoNode
//var nodeDto = ContentFactory.CreateNodeDto(entity, NodeObjectTypeId);
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
var o = Database.Update(nodeDto);
//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);
var newContentDto = dto.ContentVersionDto.ContentDto;
Database.Update(newContentDto);
}
//Look up entries in cmsDocument table to set newest = false
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId = @Id AND newest = '1'", new { Id = entity.Id });
foreach (var documentDto in documentDtos)
{
var docDto = documentDto;
docDto.Newest = false;
Database.Update(docDto);
}
//Create a new version - cmsContentVersion
//Assumes a new Version guid and Version date (modified date) has been set
//var contentVersionDto = ContentFactory.CreateContentVersionDto(entity);
var contentVersionDto = dto.ContentVersionDto;
Database.Insert(contentVersionDto);
//Create the Document specific data for this version - cmsDocument
//Assumes a new Version guid has been generated
//var documentDto = ContentFactory.CreateDocumentDto(entity);
Database.Insert(dto);
//Create the PropertyData for this version - cmsPropertyData
//var propertyDataDtos = ContentFactory.CreateProperties(entity.Id, entity.Version, entity.Properties);
var propertyFactory = new PropertyFactory(null, entity.Version, entity.Id);
var propertyDataDtos = propertyFactory.BuildDto(entity.Properties);
//Add Properties
foreach (var propertyDataDto in propertyDataDtos)
{
Database.Insert(propertyDataDto);
}
((Content)entity).ResetDirtyProperties();
}
#endregion
#region Implementation of IContentRepository
public IEnumerable<IContent> GetAllVersions(int id)
{
var contentSql = BaseSqlClause(false);
var contentSql = GetBaseQuery(false);
contentSql.Where("[cmsDocument].[nodeId] = @Id", new { Id = id });
contentSql.OrderBy("[cmsContentVersion].[VersionDate] DESC");
var documentDtos = UnitOfWork.Storage.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(contentSql);
var documentDtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(contentSql);
foreach (var dto in documentDtos)
{
yield return GetByVersion(id, dto.ContentVersionDto.VersionId);
@@ -251,12 +289,12 @@ namespace Umbraco.Core.Persistence.Repositories
public IContent GetByVersion(int id, Guid versionId)
{
var contentSql = BaseSqlClause(false);
var contentSql = GetBaseQuery(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();
var documentDto = Database.Query<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(contentSql).FirstOrDefault();
if (documentDto == null)
return null;
@@ -268,26 +306,22 @@ namespace Umbraco.Core.Persistence.Repositories
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 propertyDataDtos = Database.Query<PropertyDataDto, PropertyTypeDto>(propertySql);
var contentType = _contentTypeRepository.Get(documentDto.ContentVersionDto.ContentDto.ContentType);
var content = ContentFactory.CreateContent(id, contentType, documentDto, propertyDataDtos);
content.ResetDirtyProperties();
//var content = ContentFactory.CreateContent(id, contentType, documentDto, propertyDataDtos);
var factory = new EntityFactory(contentType, NodeObjectTypeId, id);
var content = factory.BuildEntity(documentDto);
var propertyFactory = new PropertyFactory(contentType, documentDto.ContentVersionDto.VersionId, id);
var properties = propertyFactory.BuildEntity(propertyDataDtos);
content.Properties = new PropertyCollection(properties);
((Content)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;
}
#endregion
}
}

View File

@@ -4,7 +4,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentRepository : IRepository<IContent>
public interface IContentRepository : IRepository<int, IContent>
{
IEnumerable<IContent> GetAllVersions(int id);
IContent GetByVersion(int id, Guid versionId);

View File

@@ -2,7 +2,7 @@
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentTypeRepository : IRepository<IContentType>
public interface IContentTypeRepository : IRepository<int, IContentType>
{
}

View File

@@ -9,7 +9,9 @@ namespace Umbraco.Core.Persistence.Repositories
/// 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
/// <typeparam name="TId">Type of the Id used for this entity</typeparam>
public interface IRepository<TId, TEntity>
where TEntity : IAggregateRoot
{
/// <summary>
/// Adds or Updates an Entity
@@ -28,14 +30,14 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
TEntity Get(int id);
TEntity Get(TId id);
/// <summary>
/// Gets all entities of the spefified type
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
IEnumerable<TEntity> GetAll(params int[] ids);
IEnumerable<TEntity> GetAll(params TId[] ids);
/// <summary>
/// Gets all entities of the spefified type and query
@@ -49,7 +51,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
bool Exists(int id);
bool Exists(TId id);
/// <summary>
/// Returns the count for the specified query
@@ -62,6 +64,6 @@ namespace Umbraco.Core.Persistence.Repositories
/// Sets the Unit Of Work for the Repository
/// </summary>
/// <param name="work"></param>
void SetUnitOfWork<T>(IUnitOfWork<T> work);
void SetUnitOfWork(IUnitOfWork work);
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
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 for PetaPoco based repositories
/// </summary>
/// <typeparam name="TId"></typeparam>
/// <typeparam name="TEntity"></typeparam>
internal abstract class PetaPocoRepositoryBase<TId, TEntity> : RepositoryBase<TId, TEntity>
where TEntity : IAggregateRoot
{
private Database _database;
protected PetaPocoRepositoryBase(IUnitOfWork work) : base(work)
{
}
protected PetaPocoRepositoryBase(IUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache)
{
_database = DatabaseFactory.Current.Database;
}
protected Database Database
{
get { return _database; }
}
#region Abstract Methods
protected abstract Sql GetBaseQuery(bool isCount);
protected abstract Sql GetBaseWhereClause();
protected abstract IEnumerable<string> GetDeleteClauses();
protected abstract Guid NodeObjectTypeId { get; }
protected abstract override void PersistNewItem(TEntity entity);
protected abstract override void PersistUpdatedItem(TEntity entity);
#endregion
protected override int PerformCount(IQuery<TEntity> query)
{
var sqlClause = GetBaseQuery(true);
var translator = new SqlTranslator<TEntity>(sqlClause, query);
var sql = translator.Translate();
return _database.ExecuteScalar<int>(sql);
}
protected override void PersistDeletedItem(TEntity entity)
{
var deletes = GetDeleteClauses();
foreach (var delete in deletes)
{
_database.Execute(delete, new {Id = entity.Id});
}
}
}
}

View File

@@ -1,155 +0,0 @@
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<Database> _work;
private readonly IRepositoryCacheProvider _cache;
protected Repository(IUnitOfWork<Database> work)
: this(work, RuntimeCacheProvider.Current)
{
}
internal Repository(IUnitOfWork<Database> work, IRepositoryCacheProvider cache)
{
_work = work;
_cache = cache;
}
internal IUnitOfWork<Database> 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<T>(IUnitOfWork<T> work)
{
_work = work as IUnitOfWork<Database>;
}
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,247 @@
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">Type of <see cref="IAggregateRoot"/> entity for which the repository is used</typeparam>
/// <typeparam name="TId">Type of the Id used for this entity</typeparam>
internal abstract class RepositoryBase<TId, TEntity> : IRepository<TId, TEntity>,
IUnitOfWorkRepository where TEntity : IAggregateRoot
{
private IUnitOfWork _work;
private readonly IRepositoryCacheProvider _cache;
protected RepositoryBase(IUnitOfWork work)
: this(work, RuntimeCacheProvider.Current)
{
}
internal RepositoryBase(IUnitOfWork work, IRepositoryCacheProvider cache)
{
_work = work;
_cache = cache;
}
/// <summary>
/// Returns the Unit of Work added to the repository
/// </summary>
protected IUnitOfWork UnitOfWork
{
get { return _work; }
}
#region IRepository<TEntity> Members
/// <summary>
/// Adds or Updates an entity of type TEntity
/// </summary>
/// <remarks>This method is backed by an <see cref="IRepositoryCacheProvider"/> cache</remarks>
/// <param name="entity"></param>
public void AddOrUpdate(TEntity entity)
{
if (!entity.HasIdentity)
{
_work.RegisterAdded(entity, this);
}
else
{
_work.RegisterChanged(entity, this);
}
_cache.Save(typeof(TEntity), entity);
}
/// <summary>
/// Deletes the passed in entity
/// </summary>
/// <param name="entity"></param>
public void Delete(TEntity entity)
{
_cache.Delete(typeof(TEntity), entity);
if(_work != null)
{
_work.RegisterRemoved(entity, this);
}
}
protected abstract TEntity PerformGet(TId id);
/// <summary>
/// Gets an entity by the passed in Id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public TEntity Get(TId id)
{
Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString());
var rEntity = _cache.GetById(typeof(TEntity), key);
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 TId[] ids);
/// <summary>
/// Gets all entities of type TEntity or a list according to the passed in Ids
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
public IEnumerable<TEntity> GetAll(params TId[] ids)
{
if (ids.Any())
{
var entities = _cache.GetByIds(typeof(TEntity), ids.Select(id => id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString())).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);
/// <summary>
/// Gets a list of entities by the passed in query
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
public IEnumerable<TEntity> GetByQuery(IQuery<TEntity> query)
{
return PerformGetByQuery(query);
}
protected abstract bool PerformExists(TId id);
/// <summary>
/// Returns a boolean indicating whether an entity with the passed Id exists
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public bool Exists(TId id)
{
Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString());
var rEntity = _cache.GetById(typeof(TEntity), key);
if (rEntity != null)
{
return true;
}
return PerformExists(id);
}
protected abstract int PerformCount(IQuery<TEntity> query);
/// <summary>
/// Returns an integer with the count of entities found with the passed in query
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
public int Count(IQuery<TEntity> query)
{
return PerformCount(query);
}
/// <summary>
/// Sets the repository's Unit Of Work with the passed in <see cref="IUnitOfWork"/>
/// </summary>
/// <param name="work"></param>
public void SetUnitOfWork(IUnitOfWork work)
{
_work = work;
}
#endregion
#region IUnitOfWorkRepository Members
/// <summary>
/// Unit of work method that tells the repository to persist the new entity
/// </summary>
/// <param name="item"></param>
public virtual void PersistNewItem(IEntity item)
{
PersistNewItem((TEntity)item);
}
/// <summary>
/// Unit of work method that tells the repository to persist the updated entity
/// </summary>
/// <param name="item"></param>
public virtual void PersistUpdatedItem(IEntity item)
{
PersistUpdatedItem((TEntity)item);
}
/// <summary>
/// Unit of work method that tells the repository to persist the deletion of the entity
/// </summary>
/// <param name="item"></param>
public virtual void PersistDeletedItem(IEntity item)
{
PersistDeletedItem((TEntity)item);
}
#endregion
#region Abstract IUnitOfWorkRepository Methods
protected abstract void PersistNewItem(TEntity item);
protected abstract void PersistUpdatedItem(TEntity item);
protected abstract void PersistDeletedItem(TEntity item);
#endregion
/// <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(TId id)
{
int i = 0;
if(int.TryParse(id.ToString(), out i))
{
return i.ToGuid();
}
return ConvertStringIdToGuid(id.ToString());
}
protected virtual Guid ConvertStringIdToGuid(string id)
{
return id.EncodeAsGuid();
}
}
}

View File

@@ -22,8 +22,8 @@ namespace Umbraco.Core.Persistence
//Otherwise look for an entity type in the config
//- If type exists check dependencies, create new object, add it to dictionary and return it
//If we have come this far the correct types wasn't found and we throw an exception
internal static TRepository ResolveByType<TRepository, TEntity>(IUnitOfWork<Database> unitOfWork)
where TRepository : class, IRepository<TEntity>
internal static TRepository ResolveByType<TRepository, TEntity, TId>(IUnitOfWork unitOfWork)
where TRepository : class, IRepository<TId, TEntity>
where TEntity : class, IAggregateRoot
{
//Initialize the provider's default value
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Persistence
if (Repositories.ContainsKey(interfaceShortName))
{
repository = (TRepository)Repositories[interfaceShortName];
if (unitOfWork != null && repository.GetType().IsSubclassOf(typeof(IRepository<TEntity>)))
if (unitOfWork != null && repository.GetType().IsSubclassOf(typeof(IRepository<TId, TEntity>)))
{
repository.SetUnitOfWork(unitOfWork);
}
@@ -83,7 +83,7 @@ namespace Umbraco.Core.Persistence
}
//Recursive create and dependency check
private static object Resolve(Type repositoryType, IUnitOfWork<Database> unitOfWork)
private static object Resolve(Type repositoryType, IUnitOfWork unitOfWork)
{
var constructor = repositoryType.GetConstructors().SingleOrDefault();
if (constructor == null)

View File

@@ -0,0 +1,9 @@
namespace Umbraco.Core.Persistence
{
internal enum TransactionType
{
Insert,
Update,
Delete
}
}

View File

@@ -1,14 +1,16 @@
using System;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.UnitOfWork
{
/// <summary>
/// Defines a Unit Of Work
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IUnitOfWork<T> : IDisposable
public interface IUnitOfWork
{
void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository);
void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository);
void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository);
void Commit();
T Storage { get; }//TODO This won't work! Need to change it
object Key { get; }
}
}

View File

@@ -3,9 +3,8 @@
/// <summary>
/// Defines a Unit of Work Provider
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IUnitOfWorkProvider<T>
public interface IUnitOfWorkProvider
{
IUnitOfWork<T> GetUnitOfWork();
IUnitOfWork GetUnitOfWork();
}
}

View File

@@ -0,0 +1,11 @@
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.UnitOfWork
{
public interface IUnitOfWorkRepository
{
void PersistNewItem(IEntity entity);
void PersistUpdatedItem(IEntity entity);
void PersistDeletedItem(IEntity entity);
}
}

View File

@@ -1,32 +1,142 @@
using Umbraco.Core.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.UnitOfWork
{
internal class PetaPocoUnitOfWork : IUnitOfWork<Database>
/// <summary>
/// Represents the Unit of Work implementation for PetaPoco
/// </summary>
internal class PetaPocoUnitOfWork : IUnitOfWork
{
private readonly Transaction _petaTransaction;
private readonly Database _storage;
private Guid _key;
private List<Operation> _operations;
public PetaPocoUnitOfWork()
{
var connectionString = GlobalSettings.DbDsn;
_storage = new Database(connectionString);
_petaTransaction = new Transaction(_storage);
_key = Guid.NewGuid();
_operations = new List<Operation>();
}
public void Dispose()
/// <summary>
/// Registers an <see cref="IEntity" /> instance to be added through this <see cref="UnitOfWork" />
/// </summary>
/// <param name="entity">The <see cref="IEntity" /></param>
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository)
{
_petaTransaction.Dispose();
_operations.Add(
new Operation
{
Entity = entity,
ProcessDate = DateTime.Now,
Repository = repository,
Type = TransactionType.Insert
});
}
/// <summary>
/// Registers an <see cref="IEntity" /> instance to be changed through this <see cref="UnitOfWork" />
/// </summary>
/// <param name="entity">The <see cref="IEntity" /></param>
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository)
{
_operations.Add(
new Operation
{
Entity = entity,
ProcessDate = DateTime.Now,
Repository = repository,
Type = TransactionType.Update
});
}
/// <summary>
/// Registers an <see cref="IEntity" /> instance to be removed through this <see cref="UnitOfWork" />
/// </summary>
/// <param name="entity">The <see cref="IEntity" /></param>
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository)
{
_operations.Add(
new Operation
{
Entity = entity,
ProcessDate = DateTime.Now,
Repository = repository,
Type = TransactionType.Delete
});
}
/// <summary>
/// Commits all batched changes within the scope of a PetaPoco transaction <see cref="Transaction"/>
/// </summary>
public void Commit()
{
_petaTransaction.Complete();
using(Transaction transaction = DatabaseFactory.Current.Database.GetTransaction())
{
foreach (var operation in _operations.OrderBy(o => o.ProcessDate))
{
switch (operation.Type)
{
case TransactionType.Insert:
operation.Repository.PersistNewItem(operation.Entity);
break;
case TransactionType.Delete:
operation.Repository.PersistDeletedItem(operation.Entity);
break;
case TransactionType.Update:
operation.Repository.PersistUpdatedItem(operation.Entity);
break;
}
}
transaction.Complete();
}
// Clear everything
_operations.Clear();
_key = Guid.NewGuid();
}
public Database Storage
public object Key
{
get { return _storage; }
get { return _key; }
}
#region Operation
/// <summary>
/// Provides a snapshot of an entity and the repository reference it belongs to.
/// </summary>
private sealed class Operation
{
/// <summary>
/// Gets or sets the entity.
/// </summary>
/// <value>The entity.</value>
public IEntity Entity { get; set; }
/// <summary>
/// Gets or sets the process date.
/// </summary>
/// <value>The process date.</value>
public DateTime ProcessDate { get; set; }
/// <summary>
/// Gets or sets the repository.
/// </summary>
/// <value>The repository.</value>
public IUnitOfWorkRepository Repository { get; set; }
/// <summary>
/// Gets or sets the type of operation.
/// </summary>
/// <value>The type of operation.</value>
public TransactionType Type { get; set; }
}
#endregion
}
}

View File

@@ -1,10 +1,14 @@
namespace Umbraco.Core.Persistence.UnitOfWork
{
internal class PetaPocoUnitOfWorkProvider : IUnitOfWorkProvider<Database>
internal class PetaPocoUnitOfWorkProvider : IUnitOfWorkProvider
{
public IUnitOfWork<Database> GetUnitOfWork()
#region Implementation of IUnitOfWorkProvider
public IUnitOfWork GetUnitOfWork()
{
return new PetaPocoUnitOfWork();
}
#endregion
}
}

View File

@@ -91,7 +91,10 @@
<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\DatabaseFactory.cs" />
<Compile Include="Persistence\Factories\EntityFactory.cs" />
<Compile Include="Persistence\Factories\IEntityFactory.cs" />
<Compile Include="Persistence\Factories\PropertyFactory.cs" />
<Compile Include="Persistence\Mappers\ModelDtoMapper.cs" />
<Compile Include="Persistence\Querying\ExpressionHelper.cs" />
<Compile Include="Persistence\Querying\IQuery.cs" />
@@ -102,10 +105,13 @@
<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\Repositories\PetaPocoRepositoryBase.cs" />
<Compile Include="Persistence\Repositories\RepositoryBase.cs" />
<Compile Include="Persistence\RepositoryResolver.cs" />
<Compile Include="Persistence\TransactionType.cs" />
<Compile Include="Persistence\UnitOfWork\IUnitOfWork.cs" />
<Compile Include="Persistence\UnitOfWork\IUnitOfWorkProvider.cs" />
<Compile Include="Persistence\UnitOfWork\IUnitOfWorkRepository.cs" />
<Compile Include="Persistence\UnitOfWork\PetaPocoUnitOfWork.cs" />
<Compile Include="Persistence\UnitOfWork\PetaPocoUnitOfWorkProvider.cs" />
<Compile Include="PropertyEditors\Attributes\PropertyEditorAttribute.cs" />