diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt
index 88b444903c..e8bfb02cf8 100644
--- a/build/UmbracoVersion.txt
+++ b/build/UmbracoVersion.txt
@@ -1,3 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.7.0
-beta
\ No newline at end of file
+beta002
\ No newline at end of file
diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs
index db3fa10631..d46dbdaf46 100644
--- a/src/SolutionInfo.cs
+++ b/src/SolutionInfo.cs
@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.7.0")]
-[assembly: AssemblyInformationalVersion("7.7.0-beta")]
\ No newline at end of file
+[assembly: AssemblyInformationalVersion("7.7.0-beta002")]
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs
index d4a18ddcf6..155cf3f124 100644
--- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
///
/// The version comment.
- public static string CurrentComment { get { return "beta"; } }
+ public static string CurrentComment { get { return "beta002"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index addd0bc925..236f0ed311 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -31,6 +31,7 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly ContentPreviewRepository _contentPreviewRepository;
private readonly ContentXmlRepository _contentXmlRepository;
private readonly PermissionRepository _permissionRepository;
+ private readonly ContentByGuidReadRepository _contentByGuidReadRepository;
public ContentRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider syntaxProvider, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection)
: base(work, cacheHelper, logger, syntaxProvider, contentSection)
@@ -44,7 +45,7 @@ namespace Umbraco.Core.Persistence.Repositories
_contentPreviewRepository = new ContentPreviewRepository(work, CacheHelper.NoCache, logger, syntaxProvider);
_contentXmlRepository = new ContentXmlRepository(work, CacheHelper.NoCache, logger, syntaxProvider);
_permissionRepository = new PermissionRepository(UnitOfWork, cacheHelper, Logger, SqlSyntax);
-
+ _contentByGuidReadRepository = new ContentByGuidReadRepository(this, work, cacheHelper, logger, syntaxProvider);
EnsureUniqueNaming = true;
}
@@ -938,6 +939,113 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
#endregion
+ #region Read Repository implementation for GUID keys
+ public IContent Get(Guid id)
+ {
+ return _contentByGuidReadRepository.Get(id);
+ }
+
+ IEnumerable IReadRepository.GetAll(params Guid[] ids)
+ {
+ return _contentByGuidReadRepository.GetAll(ids);
+ }
+
+ public bool Exists(Guid id)
+ {
+ return _contentByGuidReadRepository.Exists(id);
+ }
+
+ ///
+ /// A reading repository purely for looking up by GUID
+ ///
+ ///
+ /// TODO: This is ugly and to fix we need to decouple the IRepositoryQueryable -> IRepository -> IReadRepository which should all be separate things!
+ /// Then we can do the same thing with repository instances and we wouldn't need to leave all these methods as not implemented because we wouldn't need to implement them
+ ///
+ private class ContentByGuidReadRepository : PetaPocoRepositoryBase
+ {
+ private readonly ContentRepository _outerRepo;
+
+ public ContentByGuidReadRepository(ContentRepository outerRepo,
+ IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
+ : base(work, cache, logger, sqlSyntax)
+ {
+ _outerRepo = outerRepo;
+ }
+
+ protected override IContent PerformGet(Guid id)
+ {
+ var sql = _outerRepo.GetBaseQuery(BaseQueryType.FullSingle)
+ .Where(GetBaseWhereClause(), new { Id = id })
+ .Where(x => x.Newest, SqlSyntax)
+ .OrderByDescending(x => x.VersionDate, SqlSyntax);
+
+ var dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
+
+ if (dto == null)
+ return null;
+
+ var content = _outerRepo.CreateContentFromDto(dto, sql);
+
+ return content;
+ }
+
+ protected override IEnumerable PerformGetAll(params Guid[] ids)
+ {
+ Func translate = s =>
+ {
+ if (ids.Any())
+ {
+ s.Where("umbracoNode.uniqueID in (@ids)", new { ids });
+ }
+ //we only want the newest ones with this method
+ s.Where(x => x.Newest, SqlSyntax);
+ return s;
+ };
+
+ var sqlBaseFull = _outerRepo.GetBaseQuery(BaseQueryType.FullMultiple);
+ var sqlBaseIds = _outerRepo.GetBaseQuery(BaseQueryType.Ids);
+
+ return _outerRepo.ProcessQuery(translate(sqlBaseFull), new PagingSqlQuery(translate(sqlBaseIds)));
+ }
+
+ protected override Sql GetBaseQuery(bool isCount)
+ {
+ return _outerRepo.GetBaseQuery(isCount);
+ }
+
+ protected override string GetBaseWhereClause()
+ {
+ return "umbracoNode.uniqueID = @Id";
+ }
+
+ protected override Guid NodeObjectTypeId
+ {
+ get { return _outerRepo.NodeObjectTypeId; }
+ }
+
+ #region Not needed to implement
+
+ protected override IEnumerable PerformGetByQuery(IQuery query)
+ {
+ throw new NotImplementedException();
+ }
+ protected override IEnumerable GetDeleteClauses()
+ {
+ throw new NotImplementedException();
+ }
+ protected override void PersistNewItem(IContent entity)
+ {
+ throw new NotImplementedException();
+ }
+ protected override void PersistUpdatedItem(IContent entity)
+ {
+ throw new NotImplementedException();
+ }
+ #endregion
+ }
+ #endregion
+
protected override string GetDatabaseFieldNameForOrderBy(string orderBy)
{
//Some custom ones
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs
index c019b694d2..c98f073f40 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs
@@ -11,7 +11,7 @@ using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
- public interface IContentRepository : IRepositoryVersionable, IRecycleBinRepository, IDeleteMediaFilesRepository
+ public interface IContentRepository : IRepositoryVersionable, IRecycleBinRepository, IReadRepository, IDeleteMediaFilesRepository
{
///
/// This builds the Xml document used for the XML cache
diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs
index 4c9b1d3561..b6a22ba761 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs
@@ -7,9 +7,8 @@ using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
- public interface IMediaRepository : IRepositoryVersionable, IRecycleBinRepository, IDeleteMediaFilesRepository
+ public interface IMediaRepository : IRepositoryVersionable, IRecycleBinRepository, IReadRepository, IDeleteMediaFilesRepository
{
-
///
/// Used to add/update published xml for the media item
///
diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
index ee413c2a59..ad2dd26a43 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
@@ -27,6 +27,7 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly ITagRepository _tagRepository;
private readonly ContentXmlRepository _contentXmlRepository;
private readonly ContentPreviewRepository _contentPreviewRepository;
+ private readonly MediaByGuidReadRepository _mediaByGuidReadRepository;
public MediaRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection)
: base(work, cache, logger, sqlSyntax, contentSection)
@@ -37,6 +38,7 @@ namespace Umbraco.Core.Persistence.Repositories
_tagRepository = tagRepository;
_contentXmlRepository = new ContentXmlRepository(work, CacheHelper.NoCache, logger, sqlSyntax);
_contentPreviewRepository = new ContentPreviewRepository(work, CacheHelper.NoCache, logger, sqlSyntax);
+ _mediaByGuidReadRepository = new MediaByGuidReadRepository(this, work, cache, logger, sqlSyntax);
EnsureUniqueNaming = contentSection.EnsureUniqueNaming;
}
@@ -522,6 +524,104 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
+ #region Read Repository implementation for GUID keys
+ public IMedia Get(Guid id)
+ {
+ return _mediaByGuidReadRepository.Get(id);
+ }
+
+ IEnumerable IReadRepository.GetAll(params Guid[] ids)
+ {
+ return _mediaByGuidReadRepository.GetAll(ids);
+ }
+
+ public bool Exists(Guid id)
+ {
+ return _mediaByGuidReadRepository.Exists(id);
+ }
+
+ ///
+ /// A reading repository purely for looking up by GUID
+ ///
+ ///
+ /// TODO: This is ugly and to fix we need to decouple the IRepositoryQueryable -> IRepository -> IReadRepository which should all be separate things!
+ /// Then we can do the same thing with repository instances and we wouldn't need to leave all these methods as not implemented because we wouldn't need to implement them
+ ///
+ private class MediaByGuidReadRepository : PetaPocoRepositoryBase
+ {
+ private readonly MediaRepository _outerRepo;
+
+ public MediaByGuidReadRepository(MediaRepository outerRepo,
+ IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
+ : base(work, cache, logger, sqlSyntax)
+ {
+ _outerRepo = outerRepo;
+ }
+
+ protected override IMedia PerformGet(Guid id)
+ {
+ var sql = GetBaseQuery(false);
+ sql.Where(GetBaseWhereClause(), new { Id = id });
+ sql.OrderByDescending(x => x.VersionDate, SqlSyntax);
+
+ var dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
+
+ if (dto == null)
+ return null;
+
+ var content = _outerRepo.CreateMediaFromDto(dto, sql);
+
+ return content;
+ }
+
+ protected override IEnumerable PerformGetAll(params Guid[] ids)
+ {
+ var sql = GetBaseQuery(false);
+ if (ids.Any())
+ {
+ sql.Where("umbracoNode.uniqueID in (@ids)", new { ids = ids });
+ }
+
+ return _outerRepo.ProcessQuery(sql, new PagingSqlQuery(sql));
+ }
+
+ protected override Sql GetBaseQuery(bool isCount)
+ {
+ return _outerRepo.GetBaseQuery(isCount);
+ }
+
+ protected override string GetBaseWhereClause()
+ {
+ return "umbracoNode.uniqueID = @Id";
+ }
+
+ protected override Guid NodeObjectTypeId
+ {
+ get { return _outerRepo.NodeObjectTypeId; }
+ }
+
+ #region Not needed to implement
+
+ protected override IEnumerable PerformGetByQuery(IQuery query)
+ {
+ throw new NotImplementedException();
+ }
+ protected override IEnumerable GetDeleteClauses()
+ {
+ throw new NotImplementedException();
+ }
+ protected override void PersistNewItem(IMedia entity)
+ {
+ throw new NotImplementedException();
+ }
+ protected override void PersistUpdatedItem(IMedia entity)
+ {
+ throw new NotImplementedException();
+ }
+ #endregion
+ }
+ #endregion
+
///
/// Gets paged media results
///
diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs
index 1ba94105f5..42ea80a717 100644
--- a/src/Umbraco.Core/Services/ContentService.cs
+++ b/src/Umbraco.Core/Services/ContentService.cs
@@ -30,7 +30,6 @@ namespace Umbraco.Core.Services
private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer();
private readonly IDataTypeService _dataTypeService;
private readonly IUserService _userService;
- private readonly IdkMap _idkMap;
//Support recursive locks because some of the methods that require locking call other methods that require locking.
//for example, the Move method needs to be locked but this calls the Save method which also needs to be locked.
@@ -42,8 +41,7 @@ namespace Umbraco.Core.Services
ILogger logger,
IEventMessagesFactory eventMessagesFactory,
IDataTypeService dataTypeService,
- IUserService userService,
- IdkMap idkMap)
+ IUserService userService)
: base(provider, repositoryFactory, logger, eventMessagesFactory)
{
if (dataTypeService == null) throw new ArgumentNullException("dataTypeService");
@@ -51,7 +49,6 @@ namespace Umbraco.Core.Services
_publishingStrategy = new PublishingStrategy(UowProvider.ScopeProvider, eventMessagesFactory, logger);
_dataTypeService = dataTypeService;
_userService = userService;
- _idkMap = idkMap;
}
#region Static Queries
@@ -141,6 +138,26 @@ namespace Umbraco.Core.Services
}
}
+ ///
+ /// Creates an object using the alias of the
+ /// that this Content should based on.
+ ///
+ ///
+ /// Note that using this method will simply return a new IContent without any identity
+ /// as it has not yet been persisted. It is intended as a shortcut to creating new content objects
+ /// that does not invoke a save operation against the database.
+ ///
+ /// Name of the Content object
+ /// Id of Parent for the new Content
+ /// Alias of the
+ /// Optional id of the user creating the content
+ ///
+ public IContent CreateContent(string name, Guid parentId, string contentTypeAlias, int userId = 0)
+ {
+ var parent = GetById(parentId);
+ return CreateContent(name, parent, contentTypeAlias, userId);
+ }
+
///
/// Creates an object using the alias of the
/// that this Content should based on.
@@ -347,7 +364,7 @@ namespace Umbraco.Core.Services
}
///
- /// Gets an object by Id
+ /// Gets objects by Ids
///
/// Ids of the Content to retrieve
///
@@ -374,6 +391,34 @@ namespace Umbraco.Core.Services
}
}
+ ///
+ /// Gets objects by Ids
+ ///
+ /// Ids of the Content to retrieve
+ ///
+ public IEnumerable GetByIds(IEnumerable ids)
+ {
+ var idsArray = ids.ToArray();
+ if (idsArray.Length == 0) return Enumerable.Empty();
+
+ using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
+ {
+ var repository = RepositoryFactory.CreateContentRepository(uow);
+
+ // ensure that the result has the order based on the ids passed in
+ var result = repository.GetAll(idsArray);
+ var content = result.ToDictionary(x => x.Key, x => x);
+
+ var sortedResult = idsArray.Select(x =>
+ {
+ IContent c;
+ return content.TryGetValue(x, out c) ? c : null;
+ }).WhereNotNull();
+
+ return sortedResult;
+ }
+ }
+
///
/// Gets an object by its 'UniqueId'
///
@@ -381,13 +426,11 @@ namespace Umbraco.Core.Services
///
public IContent GetById(Guid key)
{
- // the repository implements a cache policy on int identifiers, not guids,
- // and we are not changing it now, but we still would like to rely on caching
- // instead of running a full query against the database, so relying on the
- // id-key map, which is fast.
-
- var a = _idkMap.GetIdForKey(key, UmbracoObjectTypes.Document);
- return a.Success ? GetById(a.Result) : null;
+ using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
+ {
+ var repository = RepositoryFactory.CreateContentRepository(uow);
+ return repository.Get(key);
+ }
}
///
@@ -1168,13 +1211,14 @@ namespace Umbraco.Core.Services
public IContent GetBlueprintById(Guid id)
{
- // the repository implements a cache policy on int identifiers, not guids,
- // and we are not changing it now, but we still would like to rely on caching
- // instead of running a full query against the database, so relying on the
- // id-key map, which is fast.
-
- var a = _idkMap.GetIdForKey(id, UmbracoObjectTypes.DocumentBlueprint);
- return a.Success ? GetBlueprintById(a.Result) : null;
+ using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
+ {
+ var repository = RepositoryFactory.CreateContentBlueprintRepository(uow);
+ var blueprint = repository.Get(id);
+ if (blueprint != null)
+ ((Content)blueprint).IsBlueprint = true;
+ return blueprint;
+ }
}
public void SaveBlueprint(IContent content, int userId = 0)
diff --git a/src/Umbraco.Core/Services/ContentServiceExtensions.cs b/src/Umbraco.Core/Services/ContentServiceExtensions.cs
index b3380cf1bf..6e3f75c42d 100644
--- a/src/Umbraco.Core/Services/ContentServiceExtensions.cs
+++ b/src/Umbraco.Core/Services/ContentServiceExtensions.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
@@ -8,8 +9,43 @@ using Umbraco.Core.Persistence.Repositories;
namespace Umbraco.Core.Services
{
+ ///
+ /// Content service extension methods
+ ///
public static class ContentServiceExtensions
- {
+ {
+ public static IEnumerable GetByIds(this IContentService contentService, IEnumerable ids)
+ {
+ var guids = new List();
+ foreach (var udi in ids)
+ {
+ var guidUdi = udi as GuidUdi;
+ if (guidUdi == null)
+ throw new InvalidOperationException("The UDI provided isn't of type " + typeof(GuidUdi) + " which is required by content");
+ guids.Add(guidUdi);
+ }
+
+ return contentService.GetByIds(guids.Select(x => x.Guid));
+ }
+
+ ///
+ /// Method to create an IContent object based on the Udi of a parent
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IContent CreateContent(this IContentService contentService, string name, Udi parentId, string mediaTypeAlias, int userId = 0)
+ {
+ var guidUdi = parentId as GuidUdi;
+ if (guidUdi == null)
+ throw new InvalidOperationException("The UDI provided isn't of type " + typeof(GuidUdi) + " which is required by content");
+ var parent = contentService.GetById(guidUdi.Guid);
+ return contentService.CreateContent(name, parent, mediaTypeAlias, userId);
+ }
+
///
/// Remove all permissions for this user for all nodes
///
diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs
index e33f46e3f4..7a5c64c0c0 100644
--- a/src/Umbraco.Core/Services/IContentService.cs
+++ b/src/Umbraco.Core/Services/IContentService.cs
@@ -161,6 +161,23 @@ namespace Umbraco.Core.Services
bool SendToPublication(IContent content, int userId = 0);
IEnumerable GetByIds(IEnumerable ids);
+ IEnumerable GetByIds(IEnumerable ids);
+
+ ///
+ /// Creates an object using the alias of the
+ /// that this Content should based on.
+ ///
+ ///
+ /// Note that using this method will simply return a new IContent without any identity
+ /// as it has not yet been persisted. It is intended as a shortcut to creating new content objects
+ /// that does not invoke a save operation against the database.
+ ///
+ /// Name of the Content object
+ /// Id of Parent for the new Content
+ /// Alias of the
+ /// Optional id of the user creating the content
+ ///
+ IContent CreateContent(string name, Guid parentId, string contentTypeAlias, int userId = 0);
///
/// Creates an object using the alias of the
diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs
index 8cd2b0eed5..04683e5747 100644
--- a/src/Umbraco.Core/Services/IMediaService.cs
+++ b/src/Umbraco.Core/Services/IMediaService.cs
@@ -87,6 +87,23 @@ namespace Umbraco.Core.Services
int CountDescendants(int parentId, string contentTypeAlias = null);
IEnumerable GetByIds(IEnumerable ids);
+ IEnumerable GetByIds(IEnumerable ids);
+
+ ///
+ /// Creates an object using the alias of the
+ /// that this Media should based on.
+ ///
+ ///
+ /// Note that using this method will simply return a new IMedia without any identity
+ /// as it has not yet been persisted. It is intended as a shortcut to creating new media objects
+ /// that does not invoke a save operation against the database.
+ ///
+ /// Name of the Media object
+ /// Id of Parent for the new Media item
+ /// Alias of the
+ /// Optional id of the user creating the media item
+ ///
+ IMedia CreateMedia(string name, Guid parentId, string mediaTypeAlias, int userId = 0);
///
/// Creates an object using the alias of the
diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs
index 38c0c20eb2..ee4cddb382 100644
--- a/src/Umbraco.Core/Services/MediaService.cs
+++ b/src/Umbraco.Core/Services/MediaService.cs
@@ -35,17 +35,35 @@ namespace Umbraco.Core.Services
private readonly IDataTypeService _dataTypeService;
private readonly IUserService _userService;
private readonly MediaFileSystem _mediaFileSystem = FileSystemProviderManager.Current.MediaFileSystem;
- private readonly IdkMap _idkMap;
- public MediaService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IDataTypeService dataTypeService, IUserService userService, IdkMap idkMap)
+ public MediaService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IDataTypeService dataTypeService, IUserService userService)
: base(provider, repositoryFactory, logger, eventMessagesFactory)
{
if (dataTypeService == null) throw new ArgumentNullException("dataTypeService");
if (userService == null) throw new ArgumentNullException("userService");
_dataTypeService = dataTypeService;
_userService = userService;
- _idkMap = idkMap;
- }
+ }
+
+ ///
+ /// Creates an object using the alias of the
+ /// that this Media should based on.
+ ///
+ ///
+ /// Note that using this method will simply return a new IMedia without any identity
+ /// as it has not yet been persisted. It is intended as a shortcut to creating new media objects
+ /// that does not invoke a save operation against the database.
+ ///
+ /// Name of the Media object
+ /// Id of Parent for the new Media item
+ /// Alias of the
+ /// Optional id of the user creating the media item
+ ///
+ public IMedia CreateMedia(string name, Guid parentId, string mediaTypeAlias, int userId = 0)
+ {
+ var parent = GetById(parentId);
+ return CreateMedia(name, parent, mediaTypeAlias, userId);
+ }
///
/// Creates an object using the alias of the
@@ -307,7 +325,7 @@ namespace Umbraco.Core.Services
}
///
- /// Gets an object by Id
+ /// Gets an objects by Ids
///
/// Ids of the Media to retrieve
///
@@ -322,6 +340,22 @@ namespace Umbraco.Core.Services
}
}
+ ///
+ /// Gets an objects by Ids
+ ///
+ /// Ids of the Media to retrieve
+ ///
+ public IEnumerable GetByIds(IEnumerable ids)
+ {
+ if (ids.Any() == false) return Enumerable.Empty();
+
+ using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
+ {
+ var repository = RepositoryFactory.CreateMediaRepository(uow);
+ return repository.GetAll(ids.ToArray());
+ }
+ }
+
///
/// Gets an object by its 'UniqueId'
///
@@ -329,15 +363,11 @@ namespace Umbraco.Core.Services
///
public IMedia GetById(Guid key)
{
- // the repository implements a cache policy on int identifiers, not guids,
- // and we are not changing it now, but we still would like to rely on caching
- // instead of running a full query against the database, so relying on the
- // id-key map, which is fast.
- //
- // we should inject the id-key map but ... breaking changes ... yada
-
- var a = _idkMap.GetIdForKey(key, UmbracoObjectTypes.Media);
- return a.Success ? GetById(a.Result) : null;
+ using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
+ {
+ var repository = RepositoryFactory.CreateMediaRepository(uow);
+ return repository.Get(key);
+ }
}
///
diff --git a/src/Umbraco.Core/Services/MediaServiceExtensions.cs b/src/Umbraco.Core/Services/MediaServiceExtensions.cs
new file mode 100644
index 0000000000..41e0ebe55e
--- /dev/null
+++ b/src/Umbraco.Core/Services/MediaServiceExtensions.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Core.Services
+{
+ ///
+ /// Media service extension methods
+ ///
+ ///
+ /// Many of these have to do with UDI lookups but we don't need to add these methods to the service interface since a UDI is just a GUID
+ /// and the services already support GUIDs
+ ///
+ public static class MediaServiceExtensions
+ {
+ public static IEnumerable GetByIds(this IMediaService mediaService, IEnumerable ids)
+ {
+ var guids = new List();
+ foreach (var udi in ids)
+ {
+ var guidUdi = udi as GuidUdi;
+ if (guidUdi == null)
+ throw new InvalidOperationException("The UDI provided isn't of type " + typeof(GuidUdi) + " which is required by media");
+ guids.Add(guidUdi);
+ }
+
+ return mediaService.GetByIds(guids.Select(x => x.Guid));
+ }
+
+ public static IMedia CreateMedia(this IMediaService mediaService, string name, Udi parentId, string mediaTypeAlias, int userId = 0)
+ {
+ var guidUdi = parentId as GuidUdi;
+ if (guidUdi == null)
+ throw new InvalidOperationException("The UDI provided isn't of type " + typeof(GuidUdi) + " which is required by media");
+ var parent = mediaService.GetById(guidUdi.Guid);
+ return mediaService.CreateMedia(name, parent, mediaTypeAlias, userId);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs
index 3da10e132b..144d548b78 100644
--- a/src/Umbraco.Core/Services/ServiceContext.cs
+++ b/src/Umbraco.Core/Services/ServiceContext.cs
@@ -261,10 +261,10 @@ namespace Umbraco.Core.Services
_memberService = new Lazy(() => new MemberService(provider, repositoryFactory, logger, eventMessagesFactory, _memberGroupService.Value, _dataTypeService.Value));
if (_contentService == null)
- _contentService = new Lazy(() => new ContentService(provider, repositoryFactory, logger, eventMessagesFactory, _dataTypeService.Value, _userService.Value, idkMap));
+ _contentService = new Lazy(() => new ContentService(provider, repositoryFactory, logger, eventMessagesFactory, _dataTypeService.Value, _userService.Value));
if (_mediaService == null)
- _mediaService = new Lazy(() => new MediaService(provider, repositoryFactory, logger, eventMessagesFactory, _dataTypeService.Value, _userService.Value, idkMap));
+ _mediaService = new Lazy(() => new MediaService(provider, repositoryFactory, logger, eventMessagesFactory, _dataTypeService.Value, _userService.Value));
if (_contentTypeService == null)
_contentTypeService = new Lazy(() => new ContentTypeService(provider, repositoryFactory, logger, eventMessagesFactory, _contentService.Value, _mediaService.Value));
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 96183f61bf..a676d8caa3 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -705,6 +705,7 @@
+
diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
index f969488055..22331bc850 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
@@ -2,10 +2,12 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Web;
using System.Xml.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
+using Umbraco.Core.Cache;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
@@ -41,32 +43,77 @@ namespace Umbraco.Tests.Persistence.Repositories
base.TearDown();
}
- private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out DataTypeDefinitionRepository dtdRepository)
+ private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out DataTypeDefinitionRepository dtdRepository, CacheHelper cacheHelper = null)
{
+ cacheHelper = cacheHelper ?? CacheHelper;
+
TemplateRepository tr;
var ctRepository = CreateRepository(unitOfWork, out contentTypeRepository, out tr);
- dtdRepository = new DataTypeDefinitionRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, contentTypeRepository);
+ dtdRepository = new DataTypeDefinitionRepository(unitOfWork, cacheHelper, Logger, SqlSyntax, contentTypeRepository);
return ctRepository;
}
- private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository)
+ private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, CacheHelper cacheHelper = null)
{
TemplateRepository tr;
- return CreateRepository(unitOfWork, out contentTypeRepository, out tr);
+ return CreateRepository(unitOfWork, out contentTypeRepository, out tr, cacheHelper);
}
- private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out TemplateRepository templateRepository)
+ private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out TemplateRepository templateRepository, CacheHelper cacheHelper = null)
{
- templateRepository = new TemplateRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of());
- var tagRepository = new TagRepository(unitOfWork, CacheHelper, Logger, SqlSyntax);
- contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, templateRepository);
- var repository = new ContentRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, contentTypeRepository, templateRepository, tagRepository, Mock.Of());
+ cacheHelper = cacheHelper ?? CacheHelper;
+
+ templateRepository = new TemplateRepository(unitOfWork, cacheHelper, Logger, SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of());
+ var tagRepository = new TagRepository(unitOfWork, cacheHelper, Logger, SqlSyntax);
+ contentTypeRepository = new ContentTypeRepository(unitOfWork, cacheHelper, Logger, SqlSyntax, templateRepository);
+ var repository = new ContentRepository(unitOfWork, cacheHelper, Logger, SqlSyntax, contentTypeRepository, templateRepository, tagRepository, Mock.Of());
return repository;
}
- private UserGroupRepository CreateUserGroupRepository(IScopeUnitOfWork unitOfWork)
+ [Test]
+ public void Cache_Active_By_Int_And_Guid()
{
- return new UserGroupRepository(unitOfWork, CacheHelper, Logger, SqlSyntax);
+ var provider = new PetaPocoUnitOfWorkProvider(Logger);
+ var unitOfWork = provider.GetUnitOfWork();
+ ContentTypeRepository contentTypeRepository;
+
+ var realCache = new CacheHelper(
+ new ObjectCacheRuntimeCacheProvider(),
+ new StaticCacheProvider(),
+ new StaticCacheProvider(),
+ new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()));
+
+ using (var repository = CreateRepository(unitOfWork, out contentTypeRepository, cacheHelper: realCache))
+ {
+ DatabaseContext.Database.DisableSqlCount();
+
+ var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage");
+ var content = MockedContent.CreateSimpleContent(contentType);
+ contentTypeRepository.AddOrUpdate(contentType);
+ repository.AddOrUpdate(content);
+ unitOfWork.Commit();
+
+ DatabaseContext.Database.EnableSqlCount();
+
+ //go get it, this should already be cached since the default repository key is the INT
+ var found = repository.Get(content.Id);
+ Assert.AreEqual(0, DatabaseContext.Database.SqlCount);
+ //retrieve again, this should use cache
+ found = repository.Get(content.Id);
+ Assert.AreEqual(0, DatabaseContext.Database.SqlCount);
+
+ //reset counter
+ DatabaseContext.Database.DisableSqlCount();
+ DatabaseContext.Database.EnableSqlCount();
+
+ //now get by GUID, this won't be cached yet because the default repo key is not a GUID
+ found = repository.Get(content.Key);
+ var sqlCount = DatabaseContext.Database.SqlCount;
+ Assert.Greater(sqlCount, 0);
+ //retrieve again, this should use cache now
+ found = repository.Get(content.Key);
+ Assert.AreEqual(sqlCount, DatabaseContext.Database.SqlCount);
+ }
}
[Test]
@@ -1039,6 +1086,16 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.That(contents, Is.Not.Null);
Assert.That(contents.Any(), Is.True);
Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(4));
+
+ contents = repository.GetAll(contents.Select(x => x.Id).ToArray());
+ Assert.That(contents, Is.Not.Null);
+ Assert.That(contents.Any(), Is.True);
+ Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(4));
+
+ contents = ((IReadRepository)repository).GetAll(contents.Select(x => x.Key).ToArray());
+ Assert.That(contents, Is.Not.Null);
+ Assert.That(contents.Any(), Is.True);
+ Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(4));
}
diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs
index c9f78fb201..a2f82b0ad0 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs
@@ -5,6 +5,7 @@ using System.Xml.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
+using Umbraco.Core.Cache;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
@@ -35,14 +36,62 @@ namespace Umbraco.Tests.Persistence.Repositories
CreateTestData();
}
- private MediaRepository CreateRepository(IScopeUnitOfWork unitOfWork, out MediaTypeRepository mediaTypeRepository)
+ private MediaRepository CreateRepository(IScopeUnitOfWork unitOfWork, out MediaTypeRepository mediaTypeRepository, CacheHelper cacheHelper = null)
{
- mediaTypeRepository = new MediaTypeRepository(unitOfWork, CacheHelper, Mock.Of(), SqlSyntax);
- var tagRepository = new TagRepository(unitOfWork, CacheHelper, Mock.Of(), SqlSyntax);
- var repository = new MediaRepository(unitOfWork, CacheHelper, Mock.Of(), SqlSyntax, mediaTypeRepository, tagRepository, Mock.Of());
+ cacheHelper = cacheHelper ?? CacheHelper;
+
+ mediaTypeRepository = new MediaTypeRepository(unitOfWork, cacheHelper, Mock.Of(), SqlSyntax);
+ var tagRepository = new TagRepository(unitOfWork, cacheHelper, Mock.Of(), SqlSyntax);
+ var repository = new MediaRepository(unitOfWork, cacheHelper, Mock.Of(), SqlSyntax, mediaTypeRepository, tagRepository, Mock.Of());
return repository;
}
+ [Test]
+ public void Cache_Active_By_Int_And_Guid()
+ {
+ var provider = new PetaPocoUnitOfWorkProvider(Logger);
+ var unitOfWork = provider.GetUnitOfWork();
+ MediaTypeRepository mediaTypeRepository;
+
+ var realCache = new CacheHelper(
+ new ObjectCacheRuntimeCacheProvider(),
+ new StaticCacheProvider(),
+ new StaticCacheProvider(),
+ new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()));
+
+ using (var repository = CreateRepository(unitOfWork, out mediaTypeRepository, cacheHelper: realCache))
+ {
+ DatabaseContext.Database.DisableSqlCount();
+
+ var mediaType = MockedContentTypes.CreateSimpleMediaType("umbTextpage1", "Textpage");
+ var media = MockedMedia.CreateSimpleMedia(mediaType, "hello", -1);
+ mediaTypeRepository.AddOrUpdate(mediaType);
+ repository.AddOrUpdate(media);
+ unitOfWork.Commit();
+
+ DatabaseContext.Database.EnableSqlCount();
+
+ //go get it, this should already be cached since the default repository key is the INT
+ var found = repository.Get(media.Id);
+ Assert.AreEqual(0, DatabaseContext.Database.SqlCount);
+ //retrieve again, this should use cache
+ found = repository.Get(media.Id);
+ Assert.AreEqual(0, DatabaseContext.Database.SqlCount);
+
+ //reset counter
+ DatabaseContext.Database.DisableSqlCount();
+ DatabaseContext.Database.EnableSqlCount();
+
+ //now get by GUID, this won't be cached yet because the default repo key is not a GUID
+ found = repository.Get(media.Key);
+ var sqlCount = DatabaseContext.Database.SqlCount;
+ Assert.Greater(sqlCount, 0);
+ //retrieve again, this should use cache now
+ found = repository.Get(media.Key);
+ Assert.AreEqual(sqlCount, DatabaseContext.Database.SqlCount);
+ }
+ }
+
[Test]
public void Rebuild_All_Xml_Structures()
{
@@ -627,6 +676,16 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.That(medias, Is.Not.Null);
Assert.That(medias.Any(), Is.True);
Assert.That(medias.Count(), Is.GreaterThanOrEqualTo(3));
+
+ medias = repository.GetAll(medias.Select(x => x.Id).ToArray());
+ Assert.That(medias, Is.Not.Null);
+ Assert.That(medias.Any(), Is.True);
+ Assert.That(medias.Count(), Is.GreaterThanOrEqualTo(3));
+
+ medias = ((IReadRepository)repository).GetAll(medias.Select(x => x.Key).ToArray());
+ Assert.That(medias, Is.Not.Null);
+ Assert.That(medias.Any(), Is.True);
+ Assert.That(medias.Count(), Is.GreaterThanOrEqualTo(3));
}
}