From c7351dfad7462bdbcbe90631e7115eea0aa17fbe Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Wed, 28 Aug 2013 17:27:29 +0200 Subject: [PATCH] Adding additional methods to the MemberRepository to support various types of lookups/queries, and experimenting with adding dummy properties to the model so a strongly typed query can be translated using mappers. GetByQuery needs to be refactored so the selection is done in a subquery, so we don't loose properties in the result set --- src/Umbraco.Core/Models/Membership/Member.cs | 13 ++ .../Models/Membership/MemberProfile.cs | 27 ++-- .../Factories/MemberReadOnlyFactory.cs | 2 +- .../Persistence/Mappers/MemberMapper.cs | 8 + .../Querying/ModelToSqlExpressionHelper.cs | 2 +- .../Repositories/MemberRepository.cs | 152 ++++++++++++++++-- 6 files changed, 178 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Core/Models/Membership/Member.cs b/src/Umbraco.Core/Models/Membership/Member.cs index 640c7aa8a9..eb21ea7bad 100644 --- a/src/Umbraco.Core/Models/Membership/Member.cs +++ b/src/Umbraco.Core/Models/Membership/Member.cs @@ -34,6 +34,9 @@ namespace Umbraco.Core.Models.Membership private static readonly PropertyInfo DefaultContentTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeId); private static readonly PropertyInfo DefaultContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeAlias); + public Member() + {} + /// /// Integer Id /// @@ -424,6 +427,16 @@ namespace Umbraco.Core.Models.Membership public object ProfileId { get; set; } public IEnumerable Groups { get; set; } + + /* Internal experiment - only used for mapping queries. + * Adding these to have first level properties instead of the Properties collection. + */ + internal string LongStringPropertyValue { get; set; } + internal string ShortStringPropertyValue { get; set; } + internal int IntegerropertyValue { get; set; } + internal bool BoolPropertyValue { get; set; } + internal DateTime DateTimePropertyValue { get; set; } + internal string PropertyTypeAlias { get; set; } #region Internal methods diff --git a/src/Umbraco.Core/Models/Membership/MemberProfile.cs b/src/Umbraco.Core/Models/Membership/MemberProfile.cs index 2148cd1ca2..185c292f06 100644 --- a/src/Umbraco.Core/Models/Membership/MemberProfile.cs +++ b/src/Umbraco.Core/Models/Membership/MemberProfile.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models.Membership { - internal abstract class MemberProfile : Profile, IUmbracoEntity + internal class MemberProfile : Profile, IUmbracoEntity { private Lazy _parentId; private int _sortOrder; @@ -16,12 +16,12 @@ namespace Umbraco.Core.Models.Membership private bool _trashed; private PropertyCollection _properties; - private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).ParentId); - private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).SortOrder); - private static readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).Level); - private static readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).Path); - private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => ((IUmbracoEntity)x).CreatorId); - private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed); + private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId); + private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); + private static readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo(x => x.Level); + private static readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo(x => x.Path); + private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => x.CreatorId); + private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed); private readonly static PropertyInfo PropertyCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); protected void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -29,11 +29,14 @@ namespace Umbraco.Core.Models.Membership OnPropertyChanged(PropertyCollectionSelector); } - public abstract new int Id { get; set; } - public abstract Guid Key { get; set; } - public abstract DateTime CreateDate { get; set; } - public abstract DateTime UpdateDate { get; set; } - public abstract bool HasIdentity { get; protected set; } + protected MemberProfile() + {} + + public virtual new int Id { get; set; } + public virtual Guid Key { get; set; } + public virtual DateTime CreateDate { get; set; } + public virtual DateTime UpdateDate { get; set; } + public virtual bool HasIdentity { get; protected set; } /// /// Profile of the user who created this Content diff --git a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs index fb8010a28d..37bfc7fa49 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberReadOnlyFactory.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Factories { public IMember BuildEntity(MemberReadOnlyDto dto) { - var member = new Member + var member = new Member() { Id = dto.NodeId, CreateDate = dto.CreateDate, diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs index cf32d1edd3..6e93d7c4aa 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs @@ -60,6 +60,14 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.LastLockoutDate, dto => dto.Date); CacheMap(src => src.LastLoginDate, dto => dto.Date); CacheMap(src => src.LastPasswordChangeDate, dto => dto.Date); + + /* Internal experiment */ + CacheMap(src => src.DateTimePropertyValue, dto => dto.Date); + CacheMap(src => src.IntegerropertyValue, dto => dto.Integer); + CacheMap(src => src.BoolPropertyValue, dto => dto.Integer); + CacheMap(src => src.LongStringPropertyValue, dto => dto.Text); + CacheMap(src => src.ShortStringPropertyValue, dto => dto.VarChar); + CacheMap(src => src.PropertyTypeAlias, dto => dto.Alias); } #endregion diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs index 3042510b05..76ca62139f 100644 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs @@ -250,7 +250,7 @@ namespace Umbraco.Core.Persistence.Querying case "EndsWith": return string.Format("upper({0}) like '%{1}'", r, RemoveQuote(args[0].ToString()).ToUpper()); case "Contains": - return string.Format("upper({0}) like '%{1}%'", r, RemoveQuote(args[0].ToString()).ToUpper()); + return string.Format("{0} like '%{1}%'", r, RemoveQuote(args[0].ToString()).ToUpper()); case "Substring": var startIndex = Int32.Parse(args[0].ToString()) + 1; if (args.Count == 2) diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index d18eb4fb87..34c69fe358 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -25,10 +25,10 @@ namespace Umbraco.Core.Persistence.Repositories * GetById - get a member by its integer Id * GetByKey - get a member by its unique guid Id (which should correspond to a membership provider user's id) * GetByUsername - get a member by its username + * * GetByPropertyValue - get members with a certain property value (supply both property alias and value?) * GetByMemberTypeAlias - get all members of a certain type * GetByMemberGroup - get all members in a specific group - * GetAllMembers */ #region Overrides of RepositoryBase @@ -43,23 +43,39 @@ namespace Umbraco.Core.Persistence.Repositories Database.Fetch( new PropertyDataRelator().Map, sql); - if (dto == null || dto.Any() == false) - return null; - - var factory = new MemberReadOnlyFactory(); - var member = factory.BuildEntity(dto.First()); - - return member; + return BuildFromDto(dto); } protected override IEnumerable PerformGetAll(params int[] ids) { - throw new NotImplementedException(); + var sql = GetBaseQuery(false); + if (ids.Any()) + { + var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.id='{0}'", x))); + sql.Where(statement); + } + sql.OrderByDescending(x => x.VersionDate); + + var dtos = + Database.Fetch( + new PropertyDataRelator().Map, sql); + + return BuildFromDtos(dtos); } protected override IEnumerable PerformGetByQuery(IQuery query) { - throw new NotImplementedException(); + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate() + .OrderByDescending(x => x.VersionDate) + .OrderBy(x => x.SortOrder); + + var dtos = + Database.Fetch( + new PropertyDataRelator().Map, sql); + + return BuildFromDtos(dtos); } #endregion @@ -68,7 +84,19 @@ namespace Umbraco.Core.Persistence.Repositories protected override Sql GetBaseQuery(bool isCount) { - //TODO Count + if (isCount) + { + var sqlCount = new Sql() + .Select("COUNT(*)") + .From() + .InnerJoin().On(left => left.NodeId, right => right.NodeId) + .InnerJoin().On(left => left.NodeId, right => right.ContentTypeId) + .InnerJoin().On(left => left.NodeId, right => right.NodeId) + .InnerJoin().On(left => left.NodeId, right => right.NodeId) + .Where(x => x.NodeObjectType == NodeObjectTypeId); + return sqlCount; + } + var sql = new Sql(); sql.Select("umbracoNode.*", "cmsContent.contentType", "cmsContentType.alias AS ContentTypeAlias", "cmsContentVersion.VersionId", "cmsContentVersion.VersionDate", "cmsContentVersion.LanguageLocale", "cmsMember.Email", @@ -98,7 +126,21 @@ namespace Umbraco.Core.Persistence.Repositories protected override IEnumerable GetDeleteClauses() { - throw new NotImplementedException(); + var list = new List + { + "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", + "DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id", + "DELETE FROM umbracoRelation WHERE parentId = @Id", + "DELETE FROM umbracoRelation WHERE childId = @Id", + "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", + "DELETE FROM cmsPropertyData WHERE contentNodeId = @Id", + "DELETE FROM cmsMember WHERE nodeId = @Id", + "DELETE FROM cmsContentVersion WHERE ContentId = @Id", + "DELETE FROM cmsContentXml WHERE nodeID = @Id", + "DELETE FROM cmsContent WHERE NodeId = @Id", + "DELETE FROM umbracoNode WHERE id = @Id" + }; + return list; } protected override Guid NodeObjectTypeId @@ -135,5 +177,91 @@ namespace Umbraco.Core.Persistence.Repositories } #endregion + + //NOTE Might be sufficient to use the GetByQuery method for this, as the mapping should cover it + public IMember GetByKey(Guid key) + { + var sql = GetBaseQuery(false); + sql.Where(x => x.UniqueId == key); + sql.OrderByDescending(x => x.VersionDate); + + var dto = + Database.Fetch( + new PropertyDataRelator().Map, sql); + + return BuildFromDto(dto); + } + + //NOTE Might be sufficient to use the GetByQuery method for this, as the mapping should cover it + public IMember GetByUsername(string username) + { + var sql = GetBaseQuery(false); + sql.Where(x => x.LoginName == username); + sql.OrderByDescending(x => x.VersionDate); + + var dto = + Database.Fetch( + new PropertyDataRelator().Map, sql); + + return BuildFromDto(dto); + } + + //NOTE Might be sufficient to use the GetByQuery method for this, as the mapping should cover it + public IEnumerable GetByMemberTypeAlias(string memberTypeAlias) + { + var sql = GetBaseQuery(false); + sql.Where(x => x.Alias == memberTypeAlias); + sql.OrderByDescending(x => x.VersionDate); + + var dtos = + Database.Fetch( + new PropertyDataRelator().Map, sql); + + return BuildFromDtos(dtos); + } + + /*public IEnumerable GetByPropertyValue(string value) + {} + + public IEnumerable GetByPropertyValue(bool value) + { } + + public IEnumerable GetByPropertyValue(int value) + { } + + public IEnumerable GetByPropertyValue(DateTime value) + { } + + public IEnumerable GetByPropertyValue(string propertyTypeAlias, string value) + { } + + public IEnumerable GetByPropertyValue(string propertyTypeAlias, bool value) + { } + + public IEnumerable GetByPropertyValue(string propertyTypeAlias, int value) + { } + + public IEnumerable GetByPropertyValue(string propertyTypeAlias, DateTime value) + { }*/ + + private IMember BuildFromDto(List dtos) + { + if (dtos == null || dtos.Any() == false) + return null; + + var factory = new MemberReadOnlyFactory(); + var member = factory.BuildEntity(dtos.First()); + + return member; + } + + private IEnumerable BuildFromDtos(List dtos) + { + if (dtos == null || dtos.Any() == false) + return Enumerable.Empty(); + + var factory = new MemberReadOnlyFactory(); + return dtos.Select(factory.BuildEntity); + } } } \ No newline at end of file