diff --git a/src/Umbraco.Core/Collections/StackQueue.cs b/src/Umbraco.Core/Collections/StackQueue.cs
index 9bf9c365f077..1d8b19f88945 100644
--- a/src/Umbraco.Core/Collections/StackQueue.cs
+++ b/src/Umbraco.Core/Collections/StackQueue.cs
@@ -1,60 +1,39 @@
using System.Collections.Generic;
-namespace Umbraco.Core.Collections
+namespace Umbraco.Cms.Core.Collections
{
///
- /// Collection that can be both a queue and a stack.
+ /// Collection that can be both a queue and a stack.
///
///
public class StackQueue
{
- private readonly LinkedList _linkedList = new LinkedList();
+ private readonly LinkedList _linkedList = new();
- public void Clear()
- {
- _linkedList.Clear();
- }
+ public int Count => _linkedList.Count;
- public void Push(T obj)
- {
- _linkedList.AddFirst(obj);
- }
+ public void Clear() => _linkedList.Clear();
- public void Enqueue(T obj)
- {
- _linkedList.AddFirst(obj);
- }
+ public void Push(T obj) => _linkedList.AddFirst(obj);
+
+ public void Enqueue(T obj) => _linkedList.AddFirst(obj);
public T Pop()
{
- var obj = _linkedList.First.Value;
+ T obj = _linkedList.First.Value;
_linkedList.RemoveFirst();
return obj;
}
public T Dequeue()
{
- var obj = _linkedList.Last.Value;
+ T obj = _linkedList.Last.Value;
_linkedList.RemoveLast();
return obj;
}
- public T PeekStack()
- {
- return _linkedList.First.Value;
- }
+ public T PeekStack() => _linkedList.First.Value;
- public T PeekQueue()
- {
- return _linkedList.Last.Value;
- }
-
- public int Count
- {
- get
- {
- return _linkedList.Count;
- }
- }
+ public T PeekQueue() => _linkedList.Last.Value;
}
}
diff --git a/src/Umbraco.Infrastructure/Persistence/NPocoDatabaseExtensions-Bulk.cs b/src/Umbraco.Infrastructure/Persistence/NPocoDatabaseExtensions-Bulk.cs
index 443032c67ae8..f07867cccc83 100644
--- a/src/Umbraco.Infrastructure/Persistence/NPocoDatabaseExtensions-Bulk.cs
+++ b/src/Umbraco.Infrastructure/Persistence/NPocoDatabaseExtensions-Bulk.cs
@@ -1,38 +1,43 @@
using System;
using System.Collections.Generic;
using System.Data;
+using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using NPoco;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Infrastructure.Persistence;
namespace Umbraco.Extensions
{
///
- /// Provides extension methods to NPoco Database class.
+ /// Provides extension methods to NPoco Database class.
///
public static partial class NPocoDatabaseExtensions
{
///
- /// Configures NPoco's SqlBulkCopyHelper to use the correct SqlConnection and SqlTransaction instances from the underlying RetryDbConnection and ProfiledDbTransaction
+ /// Configures NPoco's SqlBulkCopyHelper to use the correct SqlConnection and SqlTransaction instances from the
+ /// underlying RetryDbConnection and ProfiledDbTransaction
///
///
- /// This is required to use NPoco's own method because we use wrapped DbConnection and DbTransaction instances.
- /// NPoco's InsertBulk method only caters for efficient bulk inserting records for Sql Server, it does not cater for bulk inserting of records for
- /// any other database type and in which case will just insert records one at a time.
- /// NPoco's InsertBulk method also deals with updating the passed in entity's PK/ID once it's inserted whereas our own BulkInsertRecords methods
- /// do not handle this scenario.
+ /// This is required to use NPoco's own method because we use
+ /// wrapped DbConnection and DbTransaction instances.
+ /// NPoco's InsertBulk method only caters for efficient bulk inserting records for Sql Server, it does not cater for
+ /// bulk inserting of records for
+ /// any other database type and in which case will just insert records one at a time.
+ /// NPoco's InsertBulk method also deals with updating the passed in entity's PK/ID once it's inserted whereas our own
+ /// BulkInsertRecords methods
+ /// do not handle this scenario.
///
public static void ConfigureNPocoBulkExtensions()
{
-
SqlBulkCopyHelper.SqlConnectionResolver = dbConn => GetTypedConnection(dbConn);
SqlBulkCopyHelper.SqlTransactionResolver = dbTran => GetTypedTransaction(dbTran);
}
///
- /// Creates bulk-insert commands.
+ /// Creates bulk-insert commands.
///
/// The type of the records.
/// The database.
@@ -40,17 +45,22 @@ public static void ConfigureNPocoBulkExtensions()
/// The sql commands to execute.
internal static IDbCommand[] GenerateBulkInsertCommands(this IUmbracoDatabase database, T[] records)
{
- if (database?.Connection == null) throw new ArgumentException("Null database?.connection.", nameof(database));
+ if (database?.Connection == null)
+ {
+ throw new ArgumentException("Null database?.connection.", nameof(database));
+ }
- var pocoData = database.PocoDataFactory.ForType(typeof(T));
+ PocoData pocoData = database.PocoDataFactory.ForType(typeof(T));
// get columns to include, = number of parameters per row
- var columns = pocoData.Columns.Where(c => IncludeColumn(pocoData, c)).ToArray();
+ KeyValuePair[] columns =
+ pocoData.Columns.Where(c => IncludeColumn(pocoData, c)).ToArray();
var paramsPerRecord = columns.Length;
// format columns to sql
var tableName = database.DatabaseType.EscapeTableName(pocoData.TableInfo.TableName);
- var columnNames = string.Join(", ", columns.Select(c => tableName + "." + database.DatabaseType.EscapeSqlIdentifier(c.Key)));
+ var columnNames = string.Join(", ",
+ columns.Select(c => tableName + "." + database.DatabaseType.EscapeSqlIdentifier(c.Key)));
// example:
// assume 4168 records, each record containing 8 fields, ie 8 command parameters
@@ -58,7 +68,9 @@ internal static IDbCommand[] GenerateBulkInsertCommands(this IUmbracoDatabase
// Math.Floor(2100 / 8) = 262 record per command
// 4168 / 262 = 15.908... = there will be 16 command in total
// (if we have disabled db parameters, then all records will be included, in only one command)
- var recordsPerCommand = paramsPerRecord == 0 ? int.MaxValue : Convert.ToInt32(Math.Floor(2000.00 / paramsPerRecord));
+ var recordsPerCommand = paramsPerRecord == 0
+ ? int.MaxValue
+ : Convert.ToInt32(Math.Floor((double)Constants.Sql.MaxParameterCount / paramsPerRecord));
var commandsCount = Convert.ToInt32(Math.Ceiling((double)records.Length / recordsPerCommand));
var commands = new IDbCommand[commandsCount];
@@ -67,23 +79,27 @@ internal static IDbCommand[] GenerateBulkInsertCommands(this IUmbracoDatabase
var prefix = database.DatabaseType.GetParameterPrefix(database.ConnectionString);
for (var commandIndex = 0; commandIndex < commandsCount; commandIndex++)
{
- var command = database.CreateCommand(database.Connection, CommandType.Text, string.Empty);
+ DbCommand command = database.CreateCommand(database.Connection, CommandType.Text, string.Empty);
var parameterIndex = 0;
var commandRecords = Math.Min(recordsPerCommand, recordsLeftToInsert);
var recordsValues = new string[commandRecords];
- for (var commandRecordIndex = 0; commandRecordIndex < commandRecords; commandRecordIndex++, recordsIndex++, recordsLeftToInsert--)
+ for (var commandRecordIndex = 0;
+ commandRecordIndex < commandRecords;
+ commandRecordIndex++, recordsIndex++, recordsLeftToInsert--)
{
- var record = records[recordsIndex];
+ T record = records[recordsIndex];
var recordValues = new string[columns.Length];
for (var columnIndex = 0; columnIndex < columns.Length; columnIndex++)
{
database.AddParameter(command, columns[columnIndex].Value.GetValue(record));
recordValues[columnIndex] = prefix + parameterIndex++;
}
+
recordsValues[commandRecordIndex] = "(" + string.Join(",", recordValues) + ")";
}
- command.CommandText = $"INSERT INTO {tableName} ({columnNames}) VALUES {string.Join(", ", recordsValues)}";
+ command.CommandText =
+ $"INSERT INTO {tableName} ({columnNames}) VALUES {string.Join(", ", recordsValues)}";
commands[commandIndex] = command;
}
@@ -91,19 +107,14 @@ internal static IDbCommand[] GenerateBulkInsertCommands(this IUmbracoDatabase
}
///
- /// Determines whether a column should be part of a bulk-insert.
+ /// Determines whether a column should be part of a bulk-insert.
///
/// The PocoData object corresponding to the record's type.
/// The column.
/// A value indicating whether the column should be part of the bulk-insert.
/// Columns that are primary keys and auto-incremental, or result columns, are excluded from bulk-inserts.
- public static bool IncludeColumn(PocoData pocoData, KeyValuePair column)
- {
- return column.Value.ResultColumn == false
- && (pocoData.TableInfo.AutoIncrement == false || column.Key != pocoData.TableInfo.PrimaryKey);
- }
-
-
-
+ public static bool IncludeColumn(PocoData pocoData, KeyValuePair column) =>
+ column.Value.ResultColumn == false
+ && (pocoData.TableInfo.AutoIncrement == false || column.Key != pocoData.TableInfo.PrimaryKey);
}
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditEntryRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditEntryRepository.cs
index c439c1a80d18..e3685dd32ccf 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditEntryRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditEntryRepository.cs
@@ -3,6 +3,7 @@
using System.Linq;
using Microsoft.Extensions.Logging;
using NPoco;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Persistence.Querying;
@@ -16,26 +17,47 @@
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
{
///
- /// Represents the NPoco implementation of .
+ /// Represents the NPoco implementation of .
///
internal class AuditEntryRepository : EntityRepositoryBase, IAuditEntryRepository
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
public AuditEntryRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
: base(scopeAccessor, cache, logger)
- { }
+ {
+ }
+
+ ///
+ public IEnumerable GetPage(long pageIndex, int pageCount, out long records)
+ {
+ Sql sql = Sql()
+ .Select()
+ .From()
+ .OrderByDescending(x => x.EventDateUtc);
+
+ Page page = Database.Page(pageIndex + 1, pageCount, sql);
+ records = page.TotalItems;
+ return page.Items.Select(AuditEntryFactory.BuildEntity);
+ }
+
+ ///
+ public bool IsAvailable()
+ {
+ var tables = SqlSyntax.GetTablesInSchema(Database).ToArray();
+ return tables.InvariantContains(Constants.DatabaseSchema.Tables.AuditEntry);
+ }
///
protected override IAuditEntry PerformGet(int id)
{
- var sql = Sql()
+ Sql sql = Sql()
.Select()
.From()
.Where(x => x.Id == id);
- var dto = Database.FirstOrDefault(sql);
+ AuditEntryDto dto = Database.FirstOrDefault(sql);
return dto == null ? null : AuditEntryFactory.BuildEntity(dto);
}
@@ -44,7 +66,7 @@ protected override IEnumerable PerformGetAll(params int[] ids)
{
if (ids.Length == 0)
{
- var sql = Sql()
+ Sql sql = Sql()
.Select()
.From();
@@ -53,9 +75,9 @@ protected override IEnumerable PerformGetAll(params int[] ids)
var entries = new List();
- foreach (var group in ids.InGroupsOf(Constants.Sql.MaxParameterCount))
+ foreach (IEnumerable group in ids.InGroupsOf(Constants.Sql.MaxParameterCount))
{
- var sql = Sql()
+ Sql sql = Sql()
.Select()
.From()
.WhereIn(x => x.Id, group);
@@ -69,68 +91,41 @@ protected override IEnumerable PerformGetAll(params int[] ids)
///
protected override IEnumerable PerformGetByQuery(IQuery query)
{
- var sqlClause = GetBaseQuery(false);
+ Sql sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator(sqlClause, query);
- var sql = translator.Translate();
+ Sql sql = translator.Translate();
return Database.Fetch(sql).Select(AuditEntryFactory.BuildEntity);
}
///
protected override Sql GetBaseQuery(bool isCount)
{
- var sql = Sql();
+ Sql sql = Sql();
sql = isCount ? sql.SelectCount() : sql.Select();
sql = sql.From();
return sql;
}
///
- protected override string GetBaseWhereClause()
- {
- return $"{Cms.Core.Constants.DatabaseSchema.Tables.AuditEntry}.id = @id";
- }
+ protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.AuditEntry}.id = @id";
///
- protected override IEnumerable GetDeleteClauses()
- {
+ protected override IEnumerable GetDeleteClauses() =>
throw new NotSupportedException("Audit entries cannot be deleted.");
- }
///
protected override void PersistNewItem(IAuditEntry entity)
{
entity.AddingEntity();
- var dto = AuditEntryFactory.BuildDto(entity);
+ AuditEntryDto dto = AuditEntryFactory.BuildDto(entity);
Database.Insert(dto);
entity.Id = dto.Id;
entity.ResetDirtyProperties();
}
///
- protected override void PersistUpdatedItem(IAuditEntry entity)
- {
+ protected override void PersistUpdatedItem(IAuditEntry entity) =>
throw new NotSupportedException("Audit entries cannot be updated.");
- }
-
- ///
- public IEnumerable GetPage(long pageIndex, int pageCount, out long records)
- {
- var sql = Sql()
- .Select()
- .From()
- .OrderByDescending(x => x.EventDateUtc);
-
- var page = Database.Page(pageIndex + 1, pageCount, sql);
- records = page.TotalItems;
- return page.Items.Select(AuditEntryFactory.BuildEntity);
- }
-
- ///
- public bool IsAvailable()
- {
- var tables = SqlSyntax.GetTablesInSchema(Database).ToArray();
- return tables.InvariantContains(Cms.Core.Constants.DatabaseSchema.Tables.AuditEntry);
- }
}
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityContainerRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityContainerRepository.cs
index ac69d02587aa..b30c5ae1a44d 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityContainerRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityContainerRepository.cs
@@ -3,6 +3,7 @@
using System.Linq;
using Microsoft.Extensions.Logging;
using NPoco;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Persistence.Querying;
@@ -14,47 +15,55 @@
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
{
///
- /// An internal repository for managing entity containers such as doc type, media type, data type containers.
+ /// An internal repository for managing entity containers such as doc type, media type, data type containers.
///
internal class EntityContainerRepository : EntityRepositoryBase, IEntityContainerRepository
{
- private readonly Guid _containerObjectType;
-
- public EntityContainerRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, Guid containerObjectType)
+ public EntityContainerRepository(IScopeAccessor scopeAccessor, AppCaches cache,
+ ILogger logger, Guid containerObjectType)
: base(scopeAccessor, cache, logger)
{
- var allowedContainers = new[] { Cms.Core.Constants.ObjectTypes.DocumentTypeContainer, Cms.Core.Constants.ObjectTypes.MediaTypeContainer, Cms.Core.Constants.ObjectTypes.DataTypeContainer };
- _containerObjectType = containerObjectType;
- if (allowedContainers.Contains(_containerObjectType) == false)
- throw new InvalidOperationException("No container type exists with ID: " + _containerObjectType);
+ Guid[] allowedContainers = new[]
+ {
+ Constants.ObjectTypes.DocumentTypeContainer, Constants.ObjectTypes.MediaTypeContainer,
+ Constants.ObjectTypes.DataTypeContainer
+ };
+ NodeObjectTypeId = containerObjectType;
+ if (allowedContainers.Contains(NodeObjectTypeId) == false)
+ {
+ throw new InvalidOperationException("No container type exists with ID: " + NodeObjectTypeId);
+ }
}
+ protected Guid NodeObjectTypeId { get; }
+
// never cache
- protected override IRepositoryCachePolicy CreateCachePolicy()
- {
- return NoCacheRepositoryCachePolicy.Instance;
- }
+ protected override IRepositoryCachePolicy CreateCachePolicy() =>
+ NoCacheRepositoryCachePolicy.Instance;
protected override EntityContainer PerformGet(int id)
{
- var sql = GetBaseQuery(false).Where(GetBaseWhereClause(), new { id = id, NodeObjectType = NodeObjectTypeId });
+ Sql sql = GetBaseQuery(false)
+ .Where(GetBaseWhereClause(), new { id, NodeObjectType = NodeObjectTypeId });
- var nodeDto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
+ NodeDto nodeDto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
return nodeDto == null ? null : CreateEntity(nodeDto);
}
// temp - so we don't have to implement GetByQuery
public EntityContainer Get(Guid id)
{
- var sql = GetBaseQuery(false).Where("UniqueId=@uniqueId", new { uniqueId = id });
+ Sql sql = GetBaseQuery(false).Where("UniqueId=@uniqueId", new { uniqueId = id });
- var nodeDto = Database.Fetch(sql).FirstOrDefault();
+ NodeDto nodeDto = Database.Fetch(sql).FirstOrDefault();
return nodeDto == null ? null : CreateEntity(nodeDto);
}
public IEnumerable Get(string name, int level)
{
- var sql = GetBaseQuery(false).Where("text=@name AND level=@level AND nodeObjectType=@umbracoObjectTypeId", new { name, level, umbracoObjectTypeId = NodeObjectTypeId });
+ Sql sql = GetBaseQuery(false)
+ .Where("text=@name AND level=@level AND nodeObjectType=@umbracoObjectTypeId",
+ new { name, level, umbracoObjectTypeId = NodeObjectTypeId });
return Database.Fetch(sql).Select(CreateEntity);
}
@@ -63,38 +72,38 @@ protected override IEnumerable PerformGetAll(params int[] ids)
if (ids.Any())
{
return Database.FetchByGroups(ids, Constants.Sql.MaxParameterCount, batch =>
- GetBaseQuery(false)
- .Where(x => x.NodeObjectType == NodeObjectTypeId)
- .WhereIn(x => x.NodeId, batch))
+ GetBaseQuery(false)
+ .Where(x => x.NodeObjectType == NodeObjectTypeId)
+ .WhereIn(x => x.NodeId, batch))
.Select(CreateEntity);
}
// else
- var sql = GetBaseQuery(false)
+ Sql sql = GetBaseQuery(false)
.Where("nodeObjectType=@umbracoObjectTypeId", new { umbracoObjectTypeId = NodeObjectTypeId })
.OrderBy(x => x.Level);
return Database.Fetch(sql).Select(CreateEntity);
}
- protected override IEnumerable PerformGetByQuery(IQuery query)
- {
+ protected override IEnumerable PerformGetByQuery(IQuery query) =>
throw new NotImplementedException();
- }
private static EntityContainer CreateEntity(NodeDto nodeDto)
{
if (nodeDto.NodeObjectType.HasValue == false)
+ {
throw new InvalidOperationException("Node with id " + nodeDto.NodeId + " has no object type.");
+ }
// throws if node is not a container
- var containedObjectType = EntityContainer.GetContainedObjectType(nodeDto.NodeObjectType.Value);
+ Guid containedObjectType = EntityContainer.GetContainedObjectType(nodeDto.NodeObjectType.Value);
var entity = new EntityContainer(nodeDto.NodeId, nodeDto.UniqueId,
nodeDto.ParentId, nodeDto.Path, nodeDto.Level, nodeDto.SortOrder,
containedObjectType,
- nodeDto.Text, nodeDto.UserId ?? Cms.Core.Constants.Security.UnknownUserId);
+ nodeDto.Text, nodeDto.UserId ?? Constants.Security.UnknownUserId);
// reset dirty initial properties (U4-1946)
entity.ResetDirtyProperties(false);
@@ -104,11 +113,16 @@ private static EntityContainer CreateEntity(NodeDto nodeDto)
protected override Sql GetBaseQuery(bool isCount)
{
- var sql = Sql();
+ Sql sql = Sql();
if (isCount)
+ {
sql.SelectCount();
+ }
else
+ {
sql.SelectAll();
+ }
+
sql.From();
return sql;
}
@@ -117,23 +131,29 @@ protected override Sql GetBaseQuery(bool isCount)
protected override IEnumerable GetDeleteClauses() => throw new NotImplementedException();
- protected Guid NodeObjectTypeId => _containerObjectType;
-
protected override void PersistDeletedItem(EntityContainer entity)
{
- if (entity == null) throw new ArgumentNullException(nameof(entity));
+ if (entity == null)
+ {
+ throw new ArgumentNullException(nameof(entity));
+ }
+
EnsureContainerType(entity);
- var nodeDto = Database.FirstOrDefault(Sql().SelectAll()
+ NodeDto nodeDto = Database.FirstOrDefault(Sql().SelectAll()
.From()
.Where(dto => dto.NodeId == entity.Id && dto.NodeObjectType == entity.ContainerObjectType));
- if (nodeDto == null) return;
+ if (nodeDto == null)
+ {
+ return;
+ }
// move children to the parent so they are not orphans
- var childDtos = Database.Fetch(Sql().SelectAll()
+ List childDtos = Database.Fetch(Sql().SelectAll()
.From()
- .Where("parentID=@parentID AND (nodeObjectType=@containedObjectType OR nodeObjectType=@containerObjectType)",
+ .Where(
+ "parentID=@parentID AND (nodeObjectType=@containedObjectType OR nodeObjectType=@containerObjectType)",
new
{
parentID = entity.Id,
@@ -141,7 +161,7 @@ protected override void PersistDeletedItem(EntityContainer entity)
containerObjectType = entity.ContainerObjectType
}));
- foreach (var childDto in childDtos)
+ foreach (NodeDto childDto in childDtos)
{
childDto.ParentId = nodeDto.ParentId;
Database.Update(childDto);
@@ -155,31 +175,51 @@ protected override void PersistDeletedItem(EntityContainer entity)
protected override void PersistNewItem(EntityContainer entity)
{
- if (entity == null) throw new ArgumentNullException(nameof(entity));
+ if (entity == null)
+ {
+ throw new ArgumentNullException(nameof(entity));
+ }
+
EnsureContainerType(entity);
- if (entity.Name == null) throw new InvalidOperationException("Entity name can't be null.");
- if (string.IsNullOrWhiteSpace(entity.Name)) throw new InvalidOperationException("Entity name can't be empty or consist only of white-space characters.");
+ if (entity.Name == null)
+ {
+ throw new InvalidOperationException("Entity name can't be null.");
+ }
+
+ if (string.IsNullOrWhiteSpace(entity.Name))
+ {
+ throw new InvalidOperationException(
+ "Entity name can't be empty or consist only of white-space characters.");
+ }
+
entity.Name = entity.Name.Trim();
// guard against duplicates
- var nodeDto = Database.FirstOrDefault(Sql().SelectAll()
+ NodeDto nodeDto = Database.FirstOrDefault(Sql().SelectAll()
.From()
- .Where(dto => dto.ParentId == entity.ParentId && dto.Text == entity.Name && dto.NodeObjectType == entity.ContainerObjectType));
+ .Where(dto =>
+ dto.ParentId == entity.ParentId && dto.Text == entity.Name &&
+ dto.NodeObjectType == entity.ContainerObjectType));
if (nodeDto != null)
+ {
throw new InvalidOperationException("A container with the same name already exists.");
+ }
// create
var level = 0;
var path = "-1";
if (entity.ParentId > -1)
{
- var parentDto = Database.FirstOrDefault(Sql().SelectAll()
+ NodeDto parentDto = Database.FirstOrDefault(Sql().SelectAll()
.From()
- .Where(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType));
+ .Where(dto =>
+ dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType));
if (parentDto == null)
+ {
throw new InvalidOperationException("Could not find parent container with id " + entity.ParentId);
+ }
level = parentDto.Level;
path = parentDto.Path;
@@ -203,7 +243,7 @@ protected override void PersistNewItem(EntityContainer entity)
// insert, get the id, update the path with the id
var id = Convert.ToInt32(Database.Insert(nodeDto));
nodeDto.Path = nodeDto.Path + "," + nodeDto.NodeId;
- Database.Save(nodeDto);
+ Database.Save(nodeDto);
// refresh the entity
entity.Id = id;
@@ -218,26 +258,45 @@ protected override void PersistNewItem(EntityContainer entity)
//
protected override void PersistUpdatedItem(EntityContainer entity)
{
- if (entity == null) throw new ArgumentNullException(nameof(entity));
+ if (entity == null)
+ {
+ throw new ArgumentNullException(nameof(entity));
+ }
+
EnsureContainerType(entity);
- if (entity.Name == null) throw new InvalidOperationException("Entity name can't be null.");
- if (string.IsNullOrWhiteSpace(entity.Name)) throw new InvalidOperationException("Entity name can't be empty or consist only of white-space characters.");
+ if (entity.Name == null)
+ {
+ throw new InvalidOperationException("Entity name can't be null.");
+ }
+
+ if (string.IsNullOrWhiteSpace(entity.Name))
+ {
+ throw new InvalidOperationException(
+ "Entity name can't be empty or consist only of white-space characters.");
+ }
+
entity.Name = entity.Name.Trim();
// find container to update
- var nodeDto = Database.FirstOrDefault(Sql().SelectAll()
+ NodeDto nodeDto = Database.FirstOrDefault(Sql().SelectAll()
.From()
.Where(dto => dto.NodeId == entity.Id && dto.NodeObjectType == entity.ContainerObjectType));
if (nodeDto == null)
+ {
throw new InvalidOperationException("Could not find container with id " + entity.Id);
+ }
// guard against duplicates
- var dupNodeDto = Database.FirstOrDefault(Sql().SelectAll()
+ NodeDto dupNodeDto = Database.FirstOrDefault(Sql().SelectAll()
.From()
- .Where(dto => dto.ParentId == entity.ParentId && dto.Text == entity.Name && dto.NodeObjectType == entity.ContainerObjectType));
+ .Where(dto =>
+ dto.ParentId == entity.ParentId && dto.Text == entity.Name &&
+ dto.NodeObjectType == entity.ContainerObjectType));
if (dupNodeDto != null && dupNodeDto.NodeId != nodeDto.NodeId)
+ {
throw new InvalidOperationException("A container with the same name already exists.");
+ }
// update
nodeDto.Text = entity.Name;
@@ -247,16 +306,21 @@ protected override void PersistUpdatedItem(EntityContainer entity)
nodeDto.Path = "-1";
if (entity.ParentId > -1)
{
- var parent = Database.FirstOrDefault( Sql().SelectAll()
+ NodeDto parent = Database.FirstOrDefault(Sql().SelectAll()
.From()
- .Where(dto => dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType));
+ .Where(dto =>
+ dto.NodeId == entity.ParentId && dto.NodeObjectType == entity.ContainerObjectType));
if (parent == null)
- throw new InvalidOperationException("Could not find parent container with id " + entity.ParentId);
+ {
+ throw new InvalidOperationException(
+ "Could not find parent container with id " + entity.ParentId);
+ }
nodeDto.Level = Convert.ToInt16(parent.Level + 1);
nodeDto.Path = parent.Path + "," + nodeDto.NodeId;
}
+
nodeDto.ParentId = entity.ParentId;
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs
index 1589c9d52ef0..79e6f732a2a9 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs
@@ -3,6 +3,7 @@
using System.Linq;
using Microsoft.Extensions.Logging;
using NPoco;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Persistence;
@@ -14,38 +15,37 @@
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
{
///
- /// Provides a base class to all based repositories.
+ /// Provides a base class to all based repositories.
///
/// The type of the entity's unique identifier.
/// The type of the entity managed by this repository.
public abstract class EntityRepositoryBase : RepositoryBase, IReadWriteQueryRepository
where TEntity : class, IEntity
{
+ private static RepositoryCachePolicyOptions s_defaultOptions;
private IRepositoryCachePolicy _cachePolicy;
private IQuery _hasIdQuery;
- private static RepositoryCachePolicyOptions s_defaultOptions;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- protected EntityRepositoryBase(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger> logger)
- : base(scopeAccessor, appCaches)
- {
+ protected EntityRepositoryBase(IScopeAccessor scopeAccessor, AppCaches appCaches,
+ ILogger> logger)
+ : base(scopeAccessor, appCaches) =>
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
///
- /// Gets the logger
+ /// Gets the logger
///
protected ILogger> Logger { get; }
///
- /// Gets the isolated cache for the
+ /// Gets the isolated cache for the
///
protected IAppPolicyCache GlobalIsolatedCache => AppCaches.IsolatedCaches.GetOrCreate();
///
- /// Gets the isolated cache.
+ /// Gets the isolated cache.
///
/// Depends on the ambient scope cache mode.
protected IAppPolicyCache IsolatedCache
@@ -67,19 +67,20 @@ protected IAppPolicyCache IsolatedCache
}
///
- /// Gets the default
+ /// Gets the default
///
protected virtual RepositoryCachePolicyOptions DefaultOptions => s_defaultOptions ?? (s_defaultOptions
- = new RepositoryCachePolicyOptions(() =>
- {
- // get count of all entities of current type (TEntity) to ensure cached result is correct
- // create query once if it is needed (no need for locking here) - query is static!
- IQuery query = _hasIdQuery ?? (_hasIdQuery = AmbientScope.SqlContext.Query().Where(x => x.Id != 0));
- return PerformCount(query);
- }));
+ = new RepositoryCachePolicyOptions(() =>
+ {
+ // get count of all entities of current type (TEntity) to ensure cached result is correct
+ // create query once if it is needed (no need for locking here) - query is static!
+ IQuery query = _hasIdQuery ??
+ (_hasIdQuery = AmbientScope.SqlContext.Query().Where(x => x.Id != 0));
+ return PerformCount(query);
+ }));
///
- /// Gets the repository cache policy
+ /// Gets the repository cache policy
///
protected IRepositoryCachePolicy CachePolicy
{
@@ -110,21 +111,9 @@ protected IRepositoryCachePolicy CachePolicy
}
///
- /// Get the entity id for the
- ///
- protected virtual TId GetEntityId(TEntity entity)
- => (TId)(object)entity.Id;
-
- ///
- /// Create the repository cache policy
+ /// Adds or Updates an entity of type TEntity
///
- protected virtual IRepositoryCachePolicy CreateCachePolicy()
- => new DefaultRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, DefaultOptions);
-
- ///
- /// Adds or Updates an entity of type TEntity
- ///
- /// This method is backed by an cache
+ /// This method is backed by an cache
public virtual void Save(TEntity entity)
{
if (entity.HasIdentity == false)
@@ -138,64 +127,19 @@ public virtual void Save(TEntity entity)
}
///
- /// Deletes the passed in entity
+ /// Deletes the passed in entity
///
public virtual void Delete(TEntity entity)
=> CachePolicy.Delete(entity, PersistDeletedItem);
- protected abstract TEntity PerformGet(TId id);
-
- protected abstract IEnumerable PerformGetAll(params TId[] ids);
-
- protected abstract IEnumerable PerformGetByQuery(IQuery query);
-
- protected abstract void PersistNewItem(TEntity item);
-
- protected abstract void PersistUpdatedItem(TEntity item);
-
- // TODO: obsolete, use QueryType instead everywhere like GetBaseQuery(QueryType queryType);
- protected abstract Sql GetBaseQuery(bool isCount);
-
- protected abstract string GetBaseWhereClause();
-
- protected abstract IEnumerable GetDeleteClauses();
-
- protected virtual bool PerformExists(TId id)
- {
- var sql = GetBaseQuery(true);
- sql.Where(GetBaseWhereClause(), new { id = id });
- var count = Database.ExecuteScalar(sql);
- return count == 1;
- }
-
- protected virtual int PerformCount(IQuery query)
- {
- var sqlClause = GetBaseQuery(true);
- var translator = new SqlTranslator(sqlClause, query);
- var sql = translator.Translate();
-
- return Database.ExecuteScalar(sql);
- }
-
- protected virtual void PersistDeletedItem(TEntity entity)
- {
- var deletes = GetDeleteClauses();
- foreach (var delete in deletes)
- {
- Database.Execute(delete, new { id = GetEntityId(entity) });
- }
-
- entity.DeleteDate = DateTime.Now;
- }
-
///
- /// Gets an entity by the passed in Id utilizing the repository's cache policy
+ /// Gets an entity by the passed in Id utilizing the repository's cache policy
///
public TEntity Get(TId id)
=> CachePolicy.Get(id, PerformGet, PerformGetAll);
///
- /// Gets all entities of type TEntity or a list according to the passed in Ids
+ /// Gets all entities of type TEntity or a list according to the passed in Ids
///
public IEnumerable GetMany(params TId[] ids)
{
@@ -215,7 +159,7 @@ public IEnumerable GetMany(params TId[] ids)
}
var entities = new List();
- foreach (var group in ids.InGroupsOf(Constants.Sql.MaxParameterCount))
+ foreach (IEnumerable group in ids.InGroupsOf(Constants.Sql.MaxParameterCount))
{
entities.AddRange(CachePolicy.GetAll(group.ToArray(), PerformGetAll));
}
@@ -224,22 +168,79 @@ public IEnumerable GetMany(params TId[] ids)
}
///
- /// Gets a list of entities by the passed in query
+ /// Gets a list of entities by the passed in query
///
public IEnumerable Get(IQuery query)
=> PerformGetByQuery(query)
.WhereNotNull(); // ensure we don't include any null refs in the returned collection!
///
- /// Returns a boolean indicating whether an entity with the passed Id exists
+ /// Returns a boolean indicating whether an entity with the passed Id exists
///
public bool Exists(TId id)
=> CachePolicy.Exists(id, PerformExists, PerformGetAll);
///
- /// Returns an integer with the count of entities found with the passed in query
+ /// Returns an integer with the count of entities found with the passed in query
///
public int Count(IQuery query)
=> PerformCount(query);
+
+ ///
+ /// Get the entity id for the
+ ///
+ protected virtual TId GetEntityId(TEntity entity)
+ => (TId)(object)entity.Id;
+
+ ///
+ /// Create the repository cache policy
+ ///
+ protected virtual IRepositoryCachePolicy CreateCachePolicy()
+ => new DefaultRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, DefaultOptions);
+
+ protected abstract TEntity PerformGet(TId id);
+
+ protected abstract IEnumerable PerformGetAll(params TId[] ids);
+
+ protected abstract IEnumerable PerformGetByQuery(IQuery query);
+
+ protected abstract void PersistNewItem(TEntity item);
+
+ protected abstract void PersistUpdatedItem(TEntity item);
+
+ // TODO: obsolete, use QueryType instead everywhere like GetBaseQuery(QueryType queryType);
+ protected abstract Sql GetBaseQuery(bool isCount);
+
+ protected abstract string GetBaseWhereClause();
+
+ protected abstract IEnumerable GetDeleteClauses();
+
+ protected virtual bool PerformExists(TId id)
+ {
+ Sql sql = GetBaseQuery(true);
+ sql.Where(GetBaseWhereClause(), new { id });
+ var count = Database.ExecuteScalar(sql);
+ return count == 1;
+ }
+
+ protected virtual int PerformCount(IQuery query)
+ {
+ Sql sqlClause = GetBaseQuery(true);
+ var translator = new SqlTranslator(sqlClause, query);
+ Sql sql = translator.Translate();
+
+ return Database.ExecuteScalar(sql);
+ }
+
+ protected virtual void PersistDeletedItem(TEntity entity)
+ {
+ IEnumerable deletes = GetDeleteClauses();
+ foreach (var delete in deletes)
+ {
+ Database.Execute(delete, new { id = GetEntityId(entity) });
+ }
+
+ entity.DeleteDate = DateTime.Now;
+ }
}
}
diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs
index 6485dc286a6e..4031971ddcc7 100644
--- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs
+++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs
@@ -4,6 +4,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NPoco;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
@@ -26,17 +27,17 @@
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
{
///
- /// Represents a repository for doing CRUD operations for
+ /// Represents a repository for doing CRUD operations for
///
public class MemberRepository : ContentRepositoryBase, IMemberRepository
{
- private readonly MemberPasswordConfigurationSettings _passwordConfiguration;
- private readonly IMemberTypeRepository _memberTypeRepository;
- private readonly ITagRepository _tagRepository;
- private readonly IPasswordHasher _passwordHasher;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IMemberGroupRepository _memberGroupRepository;
private readonly IRepositoryCachePolicy _memberByUsernameCachePolicy;
+ private readonly IMemberGroupRepository _memberGroupRepository;
+ private readonly IMemberTypeRepository _memberTypeRepository;
+ private readonly MemberPasswordConfigurationSettings _passwordConfiguration;
+ private readonly IPasswordHasher _passwordHasher;
+ private readonly ITagRepository _tagRepository;
private bool _passwordConfigInitialized;
private string _passwordConfigJson;
@@ -57,19 +58,22 @@ public MemberRepository(
IJsonSerializer serializer,
IEventAggregator eventAggregator,
IOptions passwordConfiguration)
- : base(scopeAccessor, cache, logger, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferenceFactories, dataTypeService, eventAggregator)
+ : base(scopeAccessor, cache, logger, languageRepository, relationRepository, relationTypeRepository,
+ propertyEditors, dataValueReferenceFactories, dataTypeService, eventAggregator)
{
- _memberTypeRepository = memberTypeRepository ?? throw new ArgumentNullException(nameof(memberTypeRepository));
+ _memberTypeRepository =
+ memberTypeRepository ?? throw new ArgumentNullException(nameof(memberTypeRepository));
_tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository));
_passwordHasher = passwordHasher;
_jsonSerializer = serializer;
_memberGroupRepository = memberGroupRepository;
_passwordConfiguration = passwordConfiguration.Value;
- _memberByUsernameCachePolicy = new DefaultRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, DefaultOptions);
+ _memberByUsernameCachePolicy =
+ new DefaultRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, DefaultOptions);
}
///
- /// Returns a serialized dictionary of the password configuration that is stored against the member in the database
+ /// Returns a serialized dictionary of the password configuration that is stored against the member in the database
///
private string DefaultPasswordConfigJson
{
@@ -95,17 +99,341 @@ private string DefaultPasswordConfigJson
public override int RecycleBinId => throw new NotSupportedException();
+ public IEnumerable FindMembersInRole(string roleName, string usernameToMatch,
+ StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith)
+ {
+ //get the group id
+ IQuery grpQry = Query().Where(group => group.Name.Equals(roleName));
+ IMemberGroup memberGroup = _memberGroupRepository.Get(grpQry).FirstOrDefault();
+ if (memberGroup == null)
+ {
+ return Enumerable.Empty();
+ }
+
+ // get the members by username
+ IQuery query = Query();
+ switch (matchType)
+ {
+ case StringPropertyMatchType.Exact:
+ query.Where(member => member.Username.Equals(usernameToMatch));
+ break;
+ case StringPropertyMatchType.Contains:
+ query.Where(member => member.Username.Contains(usernameToMatch));
+ break;
+ case StringPropertyMatchType.StartsWith:
+ query.Where(member => member.Username.StartsWith(usernameToMatch));
+ break;
+ case StringPropertyMatchType.EndsWith:
+ query.Where(member => member.Username.EndsWith(usernameToMatch));
+ break;
+ case StringPropertyMatchType.Wildcard:
+ query.Where(member => member.Username.SqlWildcard(usernameToMatch, TextColumnType.NVarchar));
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(matchType));
+ }
+
+ IMember[] matchedMembers = Get(query).ToArray();
+
+ var membersInGroup = new List();
+
+ //then we need to filter the matched members that are in the role
+ foreach (IEnumerable group in matchedMembers.Select(x => x.Id)
+ .InGroupsOf(Constants.Sql.MaxParameterCount))
+ {
+ Sql sql = Sql().SelectAll().From()
+ .Where(dto => dto.MemberGroup == memberGroup.Id)
+ .WhereIn(dto => dto.Member, group);
+
+ var memberIdsInGroup = Database.Fetch(sql)
+ .Select(x => x.Member).ToArray();
+
+ membersInGroup.AddRange(matchedMembers.Where(x => memberIdsInGroup.Contains(x.Id)));
+ }
+
+ return membersInGroup;
+ }
+
+ ///
+ /// Get all members in a specific group
+ ///
+ ///
+ ///
+ public IEnumerable GetByMemberGroup(string groupName)
+ {
+ IQuery grpQry = Query().Where(group => group.Name.Equals(groupName));
+ IMemberGroup memberGroup = _memberGroupRepository.Get(grpQry).FirstOrDefault();
+ if (memberGroup == null)
+ {
+ return Enumerable.Empty();
+ }
+
+ Sql subQuery = Sql().Select("Member").From()
+ .Where(dto => dto.MemberGroup == memberGroup.Id);
+
+ Sql sql = GetBaseQuery(false)
+ // TODO: An inner join would be better, though I've read that the query optimizer will always turn a
+ // subquery with an IN clause into an inner join anyways.
+ .Append("WHERE umbracoNode.id IN (" + subQuery.SQL + ")", subQuery.Arguments)
+ .OrderByDescending(x => x.VersionDate)
+ .OrderBy(x => x.SortOrder);
+
+ return MapDtosToContent(Database.Fetch(sql));
+ }
+
+ public bool Exists(string username)
+ {
+ Sql sql = Sql()
+ .SelectCount()
+ .From()
+ .Where(x => x.LoginName == username);
+
+ return Database.ExecuteScalar(sql) > 0;
+ }
+
+ public int GetCountByQuery(IQuery query)
+ {
+ Sql sqlWithProps = GetNodeIdQueryWithPropertyData();
+ var translator = new SqlTranslator(sqlWithProps, query);
+ Sql sql = translator.Translate();
+
+ //get the COUNT base query
+ Sql fullSql = GetBaseQuery(true)
+ .Append(new Sql("WHERE umbracoNode.id IN (" + sql.SQL + ")", sql.Arguments));
+
+ return Database.ExecuteScalar(fullSql);
+ }
+
+ ///
+ public void SetLastLogin(string username, DateTime date)
+ {
+ // Important - these queries are designed to execute without an exclusive WriteLock taken in our distributed lock
+ // table. However due to the data that we are updating which relies on version data we cannot update this data
+ // without taking some locks, otherwise we'll end up with strange situations because when a member is updated, that operation
+ // deletes and re-inserts all property data. So if there are concurrent transactions, one deleting and re-inserting and another trying
+ // to update there can be problems. This is only an issue for cmsPropertyData, not umbracoContentVersion because that table just
+ // maintains a single row and it isn't deleted/re-inserted.
+ // So the important part here is the ForUpdate() call on the select to fetch the property data to update.
+
+ // Update the cms property value for the member
+
+ SqlTemplate sqlSelectTemplateProperty = SqlContext.Templates.Get(
+ "Umbraco.Core.MemberRepository.SetLastLogin1", s => s
+ .Select(x => x.Id)
+ .From()
+ .InnerJoin()
+ .On((l, r) => l.Id == r.PropertyTypeId)
+ .InnerJoin()
+ .On((l, r) => l.Id == r.VersionId)
+ .InnerJoin().On((l, r) => l.NodeId == r.NodeId)
+ .InnerJoin().On((l, r) => l.NodeId == r.NodeId)
+ .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType"))
+ .Where(x => x.Alias == SqlTemplate.Arg("propertyTypeAlias"))
+ .Where(x => x.LoginName == SqlTemplate.Arg("username"))
+ .ForUpdate());
+ Sql sqlSelectProperty = sqlSelectTemplateProperty.Sql(Constants.ObjectTypes.Member,
+ Constants.Conventions.Member.LastLoginDate, username);
+
+ Sql update = Sql()
+ .Update(u => u
+ .Set(x => x.DateValue, date))
+ .WhereIn(x => x.Id, sqlSelectProperty);
+
+ Database.Execute(update);
+
+ // Update the umbracoContentVersion value for the member
+
+ SqlTemplate sqlSelectTemplateVersion = SqlContext.Templates.Get(
+ "Umbraco.Core.MemberRepository.SetLastLogin2", s => s
+ .Select(x => x.Id)
+ .From()
+ .InnerJoin().On((l, r) => l.NodeId == r.NodeId)
+ .InnerJoin().On((l, r) => l.NodeId == r.NodeId)
+ .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType"))
+ .Where(x => x.LoginName == SqlTemplate.Arg("username")));
+ Sql sqlSelectVersion = sqlSelectTemplateVersion.Sql(Constants.ObjectTypes.Member, username);
+
+ Database.Execute(Sql()
+ .Update(u => u
+ .Set(x => x.VersionDate, date))
+ .WhereIn(x => x.Id, sqlSelectVersion));
+ }
+
+ ///
+ /// Gets paged member results.
+ ///
+ public override IEnumerable GetPage(IQuery query,
+ long pageIndex, int pageSize, out long totalRecords,
+ IQuery filter,
+ Ordering ordering)
+ {
+ Sql filterSql = null;
+
+ if (filter != null)
+ {
+ filterSql = Sql();
+ foreach (Tuple clause in filter.GetWhereClauses())
+ {
+ filterSql = filterSql.Append($"AND ({clause.Item1})", clause.Item2);
+ }
+ }
+
+ return GetPage(query, pageIndex, pageSize, out totalRecords,
+ x => MapDtosToContent(x),
+ filterSql,
+ ordering);
+ }
+
+ public IMember GetByUsername(string username) =>
+ _memberByUsernameCachePolicy.Get(username, PerformGetByUsername, PerformGetAllByUsername);
+
+ public int[] GetMemberIds(string[] usernames)
+ {
+ Guid memberObjectType = Constants.ObjectTypes.Member;
+
+ Sql memberSql = Sql()
+ .Select("umbracoNode.id")
+ .From()
+ .InnerJoin()
+ .On(dto => dto.NodeId, dto => dto.NodeId)
+ .Where