Created TagRepository, added foreign key to cmsTags.ParentId

This commit is contained in:
Shannon
2013-10-02 18:57:44 +10:00
parent be7179b962
commit 880077f6e9
14 changed files with 408 additions and 156 deletions

View File

@@ -0,0 +1,16 @@
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
public interface ITag : IAggregateRoot
{
[DataMember]
string Text { get; set; }
[DataMember]
string Group { get; set; }
//TODO: enable this at some stage
//int ParentId { get; set; }
}
}

View File

@@ -19,6 +19,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("ParentId")]
[NullSetting(NullSetting = NullSettings.Null)]
[ForeignKey(typeof(TagDto), Name = "FK_cmsTags_cmsTags")]
public int? ParentId { get; set; }
[Column("group")]

View File

@@ -0,0 +1,57 @@
using System;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
[Serializable]
[DataContract(IsReference = true)]
public class Tag : Entity, ITag
{
public Tag()
{
}
public Tag(int id, string text, string @group)
{
Text = text;
Group = @group;
Id = id;
}
private static readonly PropertyInfo TextSelector = ExpressionHelper.GetPropertyInfo<Tag, string>(x => x.Text);
private static readonly PropertyInfo GroupSelector = ExpressionHelper.GetPropertyInfo<Tag, string>(x => x.Group);
private string _text;
private string _group;
public string Text
{
get { return _text; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_text = value;
return _text;
}, _text, TextSelector);
}
}
public string Group
{
get { return _group; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_group = value;
return _group;
}, _group, GroupSelector);
}
}
//TODO: enable this at some stage
//public int ParentId { get; set; }
}
}

View File

@@ -5,46 +5,7 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.Caching
{
//internal class RequestRepositoryCacheProvider : IRepositoryCacheProvider
//{
// private readonly ICacheProvider _requestCacheProvider;
// public RequestRepositoryCacheProvider(ICacheProvider requestCacheProvider)
// {
// _requestCacheProvider = requestCacheProvider;
// }
// public IEntity GetById(Type type, Guid id)
// {
// throw new NotImplementedException();
// }
// public IEnumerable<IEntity> GetByIds(Type type, List<Guid> ids)
// {
// throw new NotImplementedException();
// }
// public IEnumerable<IEntity> GetAllByType(Type type)
// {
// throw new NotImplementedException();
// }
// public void Save(Type type, IEntity entity)
// {
// throw new NotImplementedException();
// }
// public void Delete(Type type, IEntity entity)
// {
// throw new NotImplementedException();
// }
// public void Clear(Type type)
// {
// throw new NotImplementedException();
// }
//}
/// <summary>
/// Defines the implementation of a Cache Provider intented to back a repository
/// </summary>

View File

@@ -0,0 +1,27 @@
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal class TagFactory : IEntityFactory<ITag, TagDto>
{
public ITag BuildEntity(TagDto dto)
{
var model = new Tag(dto.Id, dto.Tag, dto.Group);
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
model.ResetDirtyProperties(false);
return model;
}
public TagDto BuildDto(ITag entity)
{
return new TagDto
{
Id = entity.Id,
Group = entity.Group,
Tag = entity.Text
};
}
}
}

View File

@@ -0,0 +1,130 @@
using System;
using System.Data;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
[Migration("7.0.0", 9, GlobalSettings.UmbracoMigrationName)]
public class AlterTagRelationsTable : MigrationBase
{
public override void Up()
{
if (Context == null || Context.Database == null) return;
Initial();
Upgrade();
Final();
}
private void Initial()
{
//create a new col which we will make a foreign key, but first needs to be populated with data.
Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable();
//we need to drop the primary key
Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship");
//drop the foreign key on umbracoNode
Delete.ForeignKey("FK_cmsTagRelationship_umbracoNode_id").OnTable("cmsTagRelationship");
}
private void Upgrade()
{
//get all data from the tag relationship table
var tagRelations = Context.Database.Fetch<TagRelationshipDto>("SELECT nodeId, tagId FROM cmsTagRelationship");
//get all node id -> property type id references for nodes that are in a tag relations and for properties that are of those nodes that are of the tag data type
var propertyTypeIdRef = Context.Database.Fetch<PropertyTypeReferenceDto>(@"SELECT DISTINCT cmsTagRelationship.nodeId as NodeId, cmsPropertyType.id as PropertyTypeId
FROM cmsTags
INNER JOIN cmsTagRelationship ON cmsTagRelationship.tagId = cmsTags.id
INNER JOIN umbracoNode ON umbracoNode.id = cmsTagRelationship.nodeId
INNER JOIN cmsContent ON cmsContent.nodeId = umbracoNode.id
INNER JOIN cmsContentType ON cmsContentType.nodeId = cmsContent.contentType
INNER JOIN cmsPropertyType ON cmsPropertyType.contentTypeId = cmsContentType.nodeId
INNER JOIN cmsDataType ON cmsDataType.nodeId = cmsPropertyType.dataTypeId
WHERE cmsDataType.controlId = '4023E540-92F5-11DD-AD8B-0800200C9A66'");
foreach (var tr in tagRelations)
{
//for each tag relation we need to assign it a property type id which must exist in our references, if it doesn't it means that
// someone has tag data that relates to node that is not in the cmsContent table - we'll have to delete it and log it if that is the case.
var propertyTypes = propertyTypeIdRef.Where(x => x.NodeId == tr.NodeId).ToArray();
if (propertyTypes.Length == 0)
{
LogHelper.Warn<AlterTagRelationsTable>("There was no cmsContent reference for cmsTagRelationship for nodeId "
+ tr.NodeId +
". The new tag system only supports tags with references to content in the cmsContent and cmsPropertyType tables. This row will be deleted: "
+ string.Format("nodeId: {0}, tagId: {1}", tr.NodeId, tr.TagId));
Delete.FromTable("cmsTagRelationship").Row(new { nodeId = tr.NodeId, tagId = tr.TagId });
}
else
{
//update the first one found to the existing row, there might be more if there are more than one tag property assigned to the node
//in that case we need to create a row.
//update the table with the alias, the current editorAlias will contain the original id
var first = propertyTypes[0];
Update.Table("cmsTagRelationship")
.Set(new { propertyTypeId = first.PropertyTypeId })
.Where(new { nodeId = tr.NodeId, tagId = tr.TagId });
if (propertyTypes.Length > 1)
{
//now we need to create rows for the other ones
for (var i = 1; i < propertyTypes.Length; i++)
{
Insert.IntoTable("cmsTagRelationship").Row(new { nodeId = tr.NodeId, tagId = tr.TagId, propertyTypeId = propertyTypes[i].PropertyTypeId });
}
}
}
}
}
private void Final()
{
//we need to change this to not nullable
Alter.Table("cmsTagRelationship").AlterColumn("propertyTypeId").AsInt32().NotNullable();
//we need to re-add the new primary key on all 3 columns
Create.PrimaryKey("PK_cmsTagRelationship").OnTable("cmsTagRelationship").Columns(new[] {"nodeId", "propertyTypeId", "tagId"});
//now we need to add a foreign key to the propertyTypeId column and change it's constraints
Create.ForeignKey("FK_cmsTagRelationship_cmsPropertyType")
.FromTable("cmsTagRelationship")
.ForeignColumn("propertyTypeId")
.ToTable("cmsPropertyType")
.PrimaryColumn("id")
.OnDelete(Rule.Cascade)
.OnUpdate(Rule.None);
//now we need to add a foreign key to the nodeId column to cmsContent (intead of the original umbracoNode)
Create.ForeignKey("FK_cmsTagRelationship_cmsContent")
.FromTable("cmsTagRelationship")
.ForeignColumn("nodeId")
.ToTable("cmsContent")
.PrimaryColumn("nodeId")
.OnDelete(Rule.Cascade)
.OnUpdate(Rule.None);
}
public override void Down()
{
throw new NotSupportedException();
}
/// <summary>
/// A custom class to map to so that we can linq to it easily without dynamics
/// </summary>
private class PropertyTypeReferenceDto
{
public int NodeId { get; set; }
public int PropertyTypeId { get; set; }
}
}
}

View File

@@ -1,131 +1,27 @@
using System;
using System.Data;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
{
[Migration("7.0.0", 9, GlobalSettings.UmbracoMigrationName)]
[Migration("7.0.0", 10, GlobalSettings.UmbracoMigrationName)]
public class AlterTagsTable : MigrationBase
{
public override void Up()
{
if (Context == null || Context.Database == null) return;
Initial();
Upgrade();
Final();
}
private void Initial()
{
//create a new col which we will make a foreign key, but first needs to be populated with data.
Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable();
//we need to drop the primary key
Delete.PrimaryKey("PK_cmsTagRelationship").FromTable("cmsTagRelationship");
//drop the foreign key on umbracoNode
Delete.ForeignKey("FK_cmsTagRelationship_umbracoNode_id").OnTable("cmsTagRelationship");
}
private void Upgrade()
{
//get all data from the tag relationship table
var tagRelations = Context.Database.Fetch<TagRelationshipDto>("SELECT nodeId, tagId FROM cmsTagRelationship");
//get all node id -> property type id references for nodes that are in a tag relations and for properties that are of those nodes that are of the tag data type
var propertyTypeIdRef = Context.Database.Fetch<PropertyTypeReferenceDto>(@"SELECT DISTINCT cmsTagRelationship.nodeId as NodeId, cmsPropertyType.id as PropertyTypeId
FROM cmsTags
INNER JOIN cmsTagRelationship ON cmsTagRelationship.tagId = cmsTags.id
INNER JOIN umbracoNode ON umbracoNode.id = cmsTagRelationship.nodeId
INNER JOIN cmsContent ON cmsContent.nodeId = umbracoNode.id
INNER JOIN cmsContentType ON cmsContentType.nodeId = cmsContent.contentType
INNER JOIN cmsPropertyType ON cmsPropertyType.contentTypeId = cmsContentType.nodeId
INNER JOIN cmsDataType ON cmsDataType.nodeId = cmsPropertyType.dataTypeId
WHERE cmsDataType.controlId = '4023E540-92F5-11DD-AD8B-0800200C9A66'");
foreach (var tr in tagRelations)
{
//for each tag relation we need to assign it a property type id which must exist in our references, if it doesn't it means that
// someone has tag data that relates to node that is not in the cmsContent table - we'll have to delete it and log it if that is the case.
var propertyTypes = propertyTypeIdRef.Where(x => x.NodeId == tr.NodeId).ToArray();
if (propertyTypes.Length == 0)
{
LogHelper.Warn<AlterTagsTable>("There was no cmsContent reference for cmsTagRelationship for nodeId "
+ tr.NodeId +
". The new tag system only supports tags with references to content in the cmsContent and cmsPropertyType tables. This row will be deleted: "
+ string.Format("nodeId: {0}, tagId: {1}", tr.NodeId, tr.TagId));
Delete.FromTable("cmsTagRelationship").Row(new { nodeId = tr.NodeId, tagId = tr.TagId });
}
else
{
//update the first one found to the existing row, there might be more if there are more than one tag property assigned to the node
//in that case we need to create a row.
//update the table with the alias, the current editorAlias will contain the original id
var first = propertyTypes[0];
Update.Table("cmsTagRelationship")
.Set(new { propertyTypeId = first.PropertyTypeId })
.Where(new { nodeId = tr.NodeId, tagId = tr.TagId });
if (propertyTypes.Length > 1)
{
//now we need to create rows for the other ones
for (var i = 1; i < propertyTypes.Length; i++)
{
Insert.IntoTable("cmsTagRelationship").Row(new { nodeId = tr.NodeId, tagId = tr.TagId, propertyTypeId = propertyTypes[i].PropertyTypeId });
}
}
}
}
}
private void Final()
{
//we need to change this to not nullable
Alter.Table("cmsTagRelationship").AlterColumn("propertyTypeId").AsInt32().NotNullable();
//we need to re-add the new primary key on all 3 columns
Create.PrimaryKey("PK_cmsTagRelationship").OnTable("cmsTagRelationship").Columns(new[] {"nodeId", "propertyTypeId", "tagId"});
//now we need to add a foreign key to the propertyTypeId column and change it's constraints
Create.ForeignKey("FK_cmsTagRelationship_cmsPropertyType")
.FromTable("cmsTagRelationship")
.ForeignColumn("propertyTypeId")
.ToTable("cmsPropertyType")
//add a foreign key to the parent id column too!
Create.ForeignKey("FK_cmsTags_cmsTags")
.FromTable("cmsTags")
.ForeignColumn("ParentId")
.ToTable("cmsTags")
.PrimaryColumn("id")
.OnDelete(Rule.Cascade)
.OnDelete(Rule.None)
.OnUpdate(Rule.None);
//now we need to add a foreign key to the nodeId column to cmsContent (intead of the original umbracoNode)
Create.ForeignKey("FK_cmsTagRelationship_cmsContent")
.FromTable("cmsTagRelationship")
.ForeignColumn("nodeId")
.ToTable("cmsContent")
.PrimaryColumn("nodeId")
.OnDelete(Rule.Cascade)
.OnUpdate(Rule.None);
}
public override void Down()
{
throw new NotSupportedException();
}
/// <summary>
/// A custom class to map to so that we can linq to it easily without dynamics
/// </summary>
private class PropertyTypeReferenceDto
{
public int NodeId { get; set; }
public int PropertyTypeId { get; set; }
throw new NotImplementedException();
}
}
}

View File

@@ -4,11 +4,9 @@ using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Text;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;

View File

@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
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
{
internal class TagsRepository : PetaPocoRepositoryBase<int, ITag>
{
protected TagsRepository(IDatabaseUnitOfWork work)
: this(work, RuntimeCacheProvider.Current)
{
}
internal TagsRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
protected override ITag PerformGet(int id)
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var tagDto = Database.Fetch<TagDto>(sql).FirstOrDefault();
if (tagDto == null)
return null;
var factory = new TagFactory();
var entity = factory.BuildEntity(tagDto);
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
((TracksChangesEntityBase)entity).ResetDirtyProperties(false);
return entity;
}
protected override IEnumerable<ITag> PerformGetAll(params int[] ids)
{
if (ids.Any())
{
return PerformGetAllOnIds(ids);
}
var sql = GetBaseQuery(false);
return ConvertFromDtos(Database.Fetch<TagDto>(sql))
.ToArray();// we don't want to re-iterate again!
}
private IEnumerable<ITag> PerformGetAllOnIds(params int[] ids)
{
if (ids.Any() == false) yield break;
foreach (var id in ids)
{
yield return Get(id);
}
}
private IEnumerable<ITag> ConvertFromDtos(IEnumerable<TagDto> dtos)
{
var factory = new TagFactory();
foreach (var entity in dtos.Select(factory.BuildEntity))
{
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
((TracksChangesEntityBase)entity).ResetDirtyProperties(false);
yield return entity;
}
}
protected override IEnumerable<ITag> PerformGetByQuery(IQuery<ITag> query)
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<ITag>(sqlClause, query);
var sql = translator.Translate();
var dtos = Database.Fetch<TagDto>(sql);
foreach (var dto in dtos)
{
yield return Get(dto.Id);
}
}
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql();
if (isCount)
{
sql.Select("COUNT(*)").From<TagDto>();
}
else
{
return GetBaseQuery();
}
return sql;
}
private static Sql GetBaseQuery()
{
var sql = new Sql();
sql.Select("*").From<TagDto>();
return sql;
}
protected override string GetBaseWhereClause()
{
return "id = @Id";
}
protected override IEnumerable<string> GetDeleteClauses()
{
var list = new List<string>
{
"DELETE FROM cmsTagRelationship WHERE tagId = @Id",
"DELETE FROM cmsTags WHERE id = @Id"
};
return list;
}
protected override Guid NodeObjectTypeId
{
get { throw new NotImplementedException(); }
}
protected override void PersistNewItem(ITag entity)
{
((Entity)entity).AddingEntity();
var factory = new TagFactory();
var dto = factory.BuildDto(entity);
var id = Convert.ToInt32(Database.Insert(dto));
entity.Id = id;
((ICanBeDirty)entity).ResetDirtyProperties();
}
protected override void PersistUpdatedItem(ITag entity)
{
((Entity)entity).UpdatingEntity();
var factory = new TagFactory();
var dto = factory.BuildDto(entity);
Database.Update(dto);
((ICanBeDirty)entity).ResetDirtyProperties();
}
}
}

View File

@@ -326,6 +326,7 @@
<Compile Include="Models\IMacro.cs" />
<Compile Include="Models\IMacroProperty.cs" />
<Compile Include="Models\IMacroPropertyType.cs" />
<Compile Include="Models\ITag.cs" />
<Compile Include="Models\Macro.cs" />
<Compile Include="Models\MacroProperty.cs" />
<Compile Include="Models\MacroPropertyCollection.cs" />
@@ -375,6 +376,7 @@
<Compile Include="Models\Script.cs" />
<Compile Include="Models\Stylesheet.cs" />
<Compile Include="Models\StylesheetProperty.cs" />
<Compile Include="Models\Tag.cs" />
<Compile Include="Models\Task.cs" />
<Compile Include="Models\TaskType.cs" />
<Compile Include="Models\Template.cs" />
@@ -398,6 +400,7 @@
<Compile Include="Persistence\Factories\MemberTypeFactory.cs" />
<Compile Include="Persistence\Factories\MemberTypeReadOnlyFactory.cs" />
<Compile Include="Persistence\Factories\ServerRegistrationFactory.cs" />
<Compile Include="Persistence\Factories\TagFactory.cs" />
<Compile Include="Persistence\Factories\UmbracoEntityFactory.cs" />
<Compile Include="Persistence\Factories\UserSectionFactory.cs" />
<Compile Include="Persistence\FaultHandling\ITransientErrorDetectionStrategy.cs" />
@@ -416,6 +419,7 @@
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\AlterCmsMacroPropertyTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\AddIndexToCmsMacroPropertyTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\AddIndexToCmsMacroTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\AlterTagRelationsTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\AlterTagsTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\DropControlIdColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\RemoveCmsMacroPropertyTypeTable.cs" />
@@ -424,6 +428,7 @@
<Compile Include="Persistence\Relators\MacroPropertyRelator.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IMacroRepository.cs" />
<Compile Include="Persistence\Repositories\MacroRepository.cs" />
<Compile Include="Persistence\Repositories\TagsRepository.cs" />
<Compile Include="PropertyEditors\BackwardsCompatibleData.cs" />
<Compile Include="PropertyEditors\BackwardsCompatibleDataType.cs" />
<Compile Include="PropertyEditors\LegacyParameterEditorAliasConverter.cs" />

View File

@@ -114,7 +114,7 @@ namespace Umbraco.Tests.Migrations.Upgrades
DatabaseContext.Database.Insert(new TagRelationshipDto { NodeId = -1, TagId = alltags.First().Id });
var migration = new AlterTagsTable();
var migration = new AlterTagRelationsTable();
var migrationContext = new MigrationContext(DatabaseProviders.SqlServerCE, DatabaseContext.Database);
migration.GetUpExpressions(migrationContext);

View File

@@ -10,7 +10,7 @@ NOTES:
* Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config
* A new version will invalidate both client and server cache and create new persisted files
-->
<clientDependency version="19" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<clientDependency version="21" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<!--
This section is used for Web Forms only, the enableCompositeFiles="true" is optional and by default is set to true.

View File

@@ -10,6 +10,7 @@ using Umbraco.Web.Media.ThumbnailProviders;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.Tags;
using Umbraco.Web.BaseRest;
using Tag = umbraco.cms.businesslogic.Tags.Tag;
namespace Umbraco.Web.WebServices
{

View File

@@ -20,6 +20,7 @@ using umbraco.cms.businesslogic.workflow;
using umbraco.cms.businesslogic.Tags;
using File = System.IO.File;
using Media = umbraco.cms.businesslogic.media.Media;
using Tag = umbraco.cms.businesslogic.Tags.Tag;
using Task = umbraco.cms.businesslogic.task.Task;
namespace umbraco.cms.businesslogic