From 9105c08a4cc3dbb5aa1612020f26b7141b4b5cd2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 12:20:38 +1100 Subject: [PATCH 1/8] getting membership provider to the same state as v7 --- .../businesslogic/member/Member.cs | 29 +- .../members/MembersMembershipProvider.cs | 590 +++++++++--------- .../members/MembershipEventHandler.cs | 35 ++ .../umbraco.providers.csproj | 1 + 4 files changed, 342 insertions(+), 313 deletions(-) create mode 100644 src/umbraco.providers/members/MembershipEventHandler.cs diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index aa6b71f46c..9b051aea7b 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -466,7 +466,7 @@ namespace umbraco.cms.businesslogic.member /// /// The members password, used when logging in on the public website /// - [Obsolete("Do not use this property, use GetPassword and ChangePassword instead, if using ChangePassword ensure that the password is encrypted or hashed based on the active membership provider")] + [Obsolete("Do not use this property, use GetPassword and ChangePassword instead, if using ChangePassword ensure that the password is encrypted or hashed based on the active membership provider")] public string Password { get @@ -479,7 +479,7 @@ namespace umbraco.cms.businesslogic.member } return _password; - } + } set { // We need to use the provider for this in order for hashing, etc. support @@ -745,10 +745,10 @@ namespace umbraco.cms.businesslogic.member } } - /// - /// Sets the password for the user - ensure it is encrypted or hashed based on the active membership provider. - /// - /// + /// + /// Sets the password for the user - ensure it is encrypted or hashed based on the active membership provider. + /// + /// public void ChangePassword(string newPassword) { SqlHelper.ExecuteNonQuery( @@ -774,23 +774,8 @@ namespace umbraco.cms.businesslogic.member } return _password; } - + /// - /// Returns the currently stored password - this may be encrypted or hashed string depending on the active membership provider - /// - /// - public string GetPassword() - { - if (string.IsNullOrEmpty(m_Password)) - { - m_Password = SqlHelper.ExecuteScalar( - "select Password from cmsMember where nodeId = @id", - SqlHelper.CreateParameter("@id", Id)); - } - return m_Password; - } - - /// /// Adds the member to group with the specified id /// /// The id of the group which the member is being added to diff --git a/src/umbraco.providers/members/MembersMembershipProvider.cs b/src/umbraco.providers/members/MembersMembershipProvider.cs index b6b90b47b8..a14b47d84d 100644 --- a/src/umbraco.providers/members/MembersMembershipProvider.cs +++ b/src/umbraco.providers/members/MembersMembershipProvider.cs @@ -7,13 +7,12 @@ using System.Web.Security; using System.Configuration; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Security; +using Umbraco.Core.Security; using umbraco.BusinessLogic; using System.Security.Cryptography; using System.Web.Util; using System.Collections.Specialized; using System.Configuration.Provider; -using umbraco.cms.businesslogic; using System.Security; using System.Security.Permissions; using System.Runtime.CompilerServices; @@ -28,7 +27,7 @@ namespace umbraco.providers.members /// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS) /// - public class UmbracoMembershipProvider : MembershipProviderBase + public class UmbracoMembershipProvider : MembershipProviderBase { #region Fields @@ -37,15 +36,34 @@ namespace umbraco.providers.members private string _lockPropertyTypeAlias = Constants.Conventions.Member.IsLockedOut; private string _lastLockedOutPropertyTypeAlias = Constants.Conventions.Member.LastLockoutDate; private string _failedPasswordAttemptsPropertyTypeAlias = Constants.Conventions.Member.FailedPasswordAttempts; - private string _approvedPropertyTypeAlias = Constants.Conventions.Member.IsApproved; + private string _approvedPropertyTypeAlias = Constants.Conventions.Member.IsApproved; private string _commentPropertyTypeAlias = Constants.Conventions.Member.Comments; private string _lastLoginPropertyTypeAlias = Constants.Conventions.Member.LastLoginDate; private string _lastPasswordChangedPropertyTypeAlias = Constants.Conventions.Member.LastPasswordChangeDate; - private string _passwordRetrievalQuestionPropertyTypeAlias = Constants.Conventions.Member.PasswordQuestion; - private string _passwordRetrievalAnswerPropertyTypeAlias = Constants.Conventions.Member.PasswordAnswer; - - private string _providerName = Member.UmbracoMemberProviderName; + private string _passwordRetrievalQuestionPropertyTypeAlias = Constants.Conventions.Member.PasswordQuestion; + private string _passwordRetrievalAnswerPropertyTypeAlias = Constants.Conventions.Member.PasswordAnswer; + private string _providerName = Member.UmbracoMemberProviderName; + + //Need to expose these publicly so we know what field aliases to use in the editor, we only care about these 3 fields + // because they are the only 'settable' provider properties that are not stored against the IMember directly (i.e. they are + // property type properties). + + public string LockPropertyTypeAlias + { + get { return _lockPropertyTypeAlias; } + } + + public string ApprovedPropertyTypeAlias + { + get { return _approvedPropertyTypeAlias; } + } + + public string CommentPropertyTypeAlias + { + get { return _commentPropertyTypeAlias; } + } + #endregion /// @@ -97,28 +115,28 @@ namespace umbraco.providers.members if (config == null) throw new ArgumentNullException("config"); if (string.IsNullOrEmpty(name)) name = "UmbracoMembershipProvider"; - + base.Initialize(name, config); - - _providerName = name; - + + _providerName = name; + // test for membertype (if not specified, choose the first member type available) if (config["defaultMemberTypeAlias"] != null) - _defaultMemberTypeAlias = config["defaultMemberTypeAlias"]; + _defaultMemberTypeAlias = config["defaultMemberTypeAlias"]; else if (MemberType.GetAll.Length == 1) - _defaultMemberTypeAlias = MemberType.GetAll[0].Alias; + _defaultMemberTypeAlias = MemberType.GetAll[0].Alias; else throw new ProviderException("No default MemberType alias is specified in the web.config string. Please add a 'defaultMemberTypeAlias' to the add element in the provider declaration in web.config"); // test for approve status if (config["umbracoApprovePropertyTypeAlias"] != null) { - _approvedPropertyTypeAlias = config["umbracoApprovePropertyTypeAlias"]; + _approvedPropertyTypeAlias = config["umbracoApprovePropertyTypeAlias"]; } // test for lock attempts if (config["umbracoLockPropertyTypeAlias"] != null) { - _lockPropertyTypeAlias = config["umbracoLockPropertyTypeAlias"]; + _lockPropertyTypeAlias = config["umbracoLockPropertyTypeAlias"]; } if (config["umbracoLastLockedPropertyTypeAlias"] != null) { @@ -130,26 +148,26 @@ namespace umbraco.providers.members } if (config["umbracoFailedPasswordAttemptsPropertyTypeAlias"] != null) { - _failedPasswordAttemptsPropertyTypeAlias = config["umbracoFailedPasswordAttemptsPropertyTypeAlias"]; + _failedPasswordAttemptsPropertyTypeAlias = config["umbracoFailedPasswordAttemptsPropertyTypeAlias"]; } // comment property if (config["umbracoCommentPropertyTypeAlias"] != null) { - _commentPropertyTypeAlias = config["umbracoCommentPropertyTypeAlias"]; + _commentPropertyTypeAlias = config["umbracoCommentPropertyTypeAlias"]; } // last login date if (config["umbracoLastLoginPropertyTypeAlias"] != null) { - _lastLoginPropertyTypeAlias = config["umbracoLastLoginPropertyTypeAlias"]; + _lastLoginPropertyTypeAlias = config["umbracoLastLoginPropertyTypeAlias"]; } // password retrieval if (config["umbracoPasswordRetrievalQuestionPropertyTypeAlias"] != null) { - _passwordRetrievalQuestionPropertyTypeAlias = config["umbracoPasswordRetrievalQuestionPropertyTypeAlias"]; + _passwordRetrievalQuestionPropertyTypeAlias = config["umbracoPasswordRetrievalQuestionPropertyTypeAlias"]; } if (config["umbracoPasswordRetrievalAnswerPropertyTypeAlias"] != null) { - _passwordRetrievalAnswerPropertyTypeAlias = config["umbracoPasswordRetrievalAnswerPropertyTypeAlias"]; + _passwordRetrievalAnswerPropertyTypeAlias = config["umbracoPasswordRetrievalAnswerPropertyTypeAlias"]; } } @@ -173,29 +191,29 @@ namespace umbraco.providers.members // This is allowed based on the overridden AllowManuallyChangingPassword option. // in order to support updating passwords from the umbraco core, we can't validate the old password - var m = Member.GetMemberFromLoginName(username); + var m = Member.GetMemberFromLoginName(username); if (m == null) return false; - var args = new ValidatePasswordEventArgs(username, newPassword, false); + var args = new ValidatePasswordEventArgs(username, newPassword, false); OnValidatingPassword(args); if (args.Cancel) { if (args.FailureInformation != null) throw args.FailureInformation; - throw new MembershipPasswordException("Change password canceled due to password validation failure."); + throw new MembershipPasswordException("Change password canceled due to password validation failure."); } - string salt; - var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); - m.ChangePassword( + string salt; + var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); + m.ChangePassword( FormatPasswordForStorage(encodedPassword, salt)); UpdateMemberProperty(m, _lastPasswordChangedPropertyTypeAlias, DateTime.Now); - m.Save(); - - return true; + m.Save(); + + return true; } /// @@ -210,15 +228,15 @@ namespace umbraco.providers.members /// public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) { - if (!String.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) && !String.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias)) + if (!String.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) && !String.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias)) { if (ValidateUser(username, password)) { Member m = Member.GetMemberFromLoginName(username); if (m != null) { - UpdateMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, newPasswordQuestion); - UpdateMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, newPasswordAnswer); + UpdateMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, newPasswordQuestion); + UpdateMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, newPasswordAnswer); m.Save(); return true; } @@ -256,23 +274,23 @@ namespace umbraco.providers.members public MembershipUser CreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { - - var args = new ValidatePasswordEventArgs(username, password, true); - OnValidatingPassword(args); - if (args.Cancel) - { - status = MembershipCreateStatus.InvalidPassword; - return null; - } - + + var args = new ValidatePasswordEventArgs(username, password, true); + OnValidatingPassword(args); + if (args.Cancel) + { + status = MembershipCreateStatus.InvalidPassword; + return null; + } + if (Member.GetMemberFromLoginName(username) != null) - { + { status = MembershipCreateStatus.DuplicateUserName; - } + } else if (Member.GetMemberFromEmail(email) != null && RequiresUniqueEmail) - { + { status = MembershipCreateStatus.DuplicateEmail; - } + } else { var memberType = MemberType.GetByAlias(memberTypeAlias); @@ -281,30 +299,33 @@ namespace umbraco.providers.members throw new InvalidOperationException("Could not find a member type with alias " + memberTypeAlias + ". Ensure your membership provider configuration is up to date and that the default member type exists."); } - var m = Member.MakeNew(username, email, memberType, User.GetUser(0)); - - string salt; - var encodedPassword = EncryptOrHashNewPassword(password, out salt); - //set the password on the member - m.ChangePassword( - FormatPasswordForStorage(encodedPassword, salt)); - - var mUser = ConvertToMembershipUser(m); + var m = Member.MakeNew(username, email, memberType, User.GetUser(0)); + string salt; + var encodedPassword = EncryptOrHashNewPassword(password, out salt); + //set the password on the member + m.ChangePassword( + FormatPasswordForStorage(encodedPassword, salt)); + // custom fields - if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) - UpdateMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, passwordQuestion); - - if (string.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias) == false) - UpdateMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, passwordAnswer); - - if (string.IsNullOrEmpty(_approvedPropertyTypeAlias) == false) - UpdateMemberProperty(m, _approvedPropertyTypeAlias, isApproved ? 1 : 0); - - if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) { - mUser.LastActivityDate = DateTime.Now; - UpdateMemberProperty(m, _lastLoginPropertyTypeAlias, mUser.LastActivityDate); + UpdateMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, passwordQuestion); + } + + if (string.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, passwordAnswer); + } + + if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, ApprovedPropertyTypeAlias, isApproved ? 1 : 0); + } + + if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, _lastLoginPropertyTypeAlias, DateTime.Now); } if (string.IsNullOrEmpty(_lastPasswordChangedPropertyTypeAlias) == false) @@ -312,6 +333,8 @@ namespace umbraco.providers.members UpdateMemberProperty(m, _lastPasswordChangedPropertyTypeAlias, DateTime.Now); } + var mUser = ConvertToMembershipUser(m); + // save m.Save(); @@ -339,7 +362,7 @@ namespace umbraco.providers.members public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { - return CreateUser(_defaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + return CreateUser(_defaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); } /// @@ -377,7 +400,7 @@ namespace umbraco.providers.members var collection = new MembershipUserCollection(); foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) { - collection.Add(ConvertToMembershipUser(m)); + collection.Add(ConvertToMembershipUser(m)); } return collection; } @@ -473,13 +496,13 @@ namespace umbraco.providers.members if (RequiresQuestionAndAnswer) { // check if password answer property alias is set - if (string.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias) == false) { // check if user is locked out - if (string.IsNullOrEmpty(_lockPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) { var isLockedOut = false; - bool.TryParse(GetMemberProperty(m, _lockPropertyTypeAlias, true), out isLockedOut); + bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLockedOut); if (isLockedOut) { throw new MembershipPasswordException("The supplied user is locked out"); @@ -487,7 +510,7 @@ namespace umbraco.providers.members } // match password answer - if (GetMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, false) != answer) + if (GetMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, false) != answer) { throw new MembershipPasswordException("Incorrect password answer"); } @@ -532,10 +555,20 @@ namespace umbraco.providers.members /// public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) { - if (String.IsNullOrEmpty(providerUserKey.ToString())) - return null; - var m = new Member(Convert.ToInt32(providerUserKey)); - return ConvertToMembershipUser(m); + var asGuid = providerUserKey.TryConvertTo(); + if (asGuid.Success) + { + var m = new Member(asGuid.Result); + return ConvertToMembershipUser(m); + } + var asInt = providerUserKey.TryConvertTo(); + if (asInt.Success) + { + var m = new Member(asInt.Result); + return ConvertToMembershipUser(m); + } + throw new InvalidOperationException("The " + GetType() + " provider only supports GUID or Int as a providerUserKey"); + } @@ -549,7 +582,7 @@ namespace umbraco.providers.members public override string GetUserNameByEmail(string email) { Member m = Member.GetMemberFromEmail(email); - return m == null ? null : m.LoginName; + return m == null ? null : m.LoginName; } /// @@ -560,66 +593,66 @@ namespace umbraco.providers.members /// The new password for the specified user. public override string ResetPassword(string username, string answer) { - if (EnablePasswordReset == false) + if (EnablePasswordReset == false) { throw new NotSupportedException("Password reset is not supported"); } - //TODO: This should be here - but how do we update failure count in this provider?? - //if (answer == null && RequiresQuestionAndAnswer) - //{ - // UpdateFailureCount(username, "passwordAnswer"); - - // throw new ProviderException("Password answer required for password reset."); - //} - - var newPassword = Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters); - - var args = new ValidatePasswordEventArgs(username, newPassword, true); - OnValidatingPassword(args); - if (args.Cancel) - { - if (args.FailureInformation != null) - throw args.FailureInformation; - throw new MembershipPasswordException("Reset password canceled due to password validation failure."); - } - - var m = Member.GetMemberFromLoginName(username); + //TODO: This should be here - but how do we update failure count in this provider?? + //if (answer == null && RequiresQuestionAndAnswer) + //{ + // UpdateFailureCount(username, "passwordAnswer"); + + // throw new ProviderException("Password answer required for password reset."); + //} + + var newPassword = Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters); + + var args = new ValidatePasswordEventArgs(username, newPassword, true); + OnValidatingPassword(args); + if (args.Cancel) + { + if (args.FailureInformation != null) + throw args.FailureInformation; + throw new MembershipPasswordException("Reset password canceled due to password validation failure."); + } + + var m = Member.GetMemberFromLoginName(username); if (m == null) throw new MembershipPasswordException("The supplied user is not found"); - - if (RequiresQuestionAndAnswer) + + if (RequiresQuestionAndAnswer) { - // check if password answer property alias is set - if (string.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias) == false) + // check if password answer property alias is set + if (string.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias) == false) { - // check if user is locked out - if (string.IsNullOrEmpty(_lockPropertyTypeAlias) == false) + // check if user is locked out + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) { - var isLockedOut = false; - bool.TryParse(GetMemberProperty(m, _lockPropertyTypeAlias, true), out isLockedOut); - if (isLockedOut) + var isLockedOut = false; + bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLockedOut); + if (isLockedOut) { - throw new MembershipPasswordException("The supplied user is locked out"); + throw new MembershipPasswordException("The supplied user is locked out"); } } - - // match password answer - if (GetMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, false) != answer) + + // match password answer + if (GetMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, false) != answer) { - throw new MembershipPasswordException("Incorrect password answer"); + throw new MembershipPasswordException("Incorrect password answer"); } } - else - { - throw new ProviderException("Password retrieval answer property alias is not set! To automatically support password question/answers you'll need to add references to the membertype properties in the 'Member' element in web.config by adding their aliases to the 'umbracoPasswordRetrievalQuestionPropertyTypeAlias' and 'umbracoPasswordRetrievalAnswerPropertyTypeAlias' attributes"); - } + else + { + throw new ProviderException("Password retrieval answer property alias is not set! To automatically support password question/answers you'll need to add references to the membertype properties in the 'Member' element in web.config by adding their aliases to the 'umbracoPasswordRetrievalQuestionPropertyTypeAlias' and 'umbracoPasswordRetrievalAnswerPropertyTypeAlias' attributes"); + } } - - string salt; - var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); - //set the password on the member - m.ChangePassword( + + string salt; + var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); + //set the password on the member + m.ChangePassword( FormatPasswordForStorage(encodedPassword, salt)); if (string.IsNullOrEmpty(_lastPasswordChangedPropertyTypeAlias) == false) @@ -627,9 +660,9 @@ namespace umbraco.providers.members UpdateMemberProperty(m, _lastPasswordChangedPropertyTypeAlias, DateTime.Now); } - m.Save(); - - return newPassword; + m.Save(); + + return newPassword; } /// @@ -641,18 +674,18 @@ namespace umbraco.providers.members /// public override bool UnlockUser(string userName) { - if (string.IsNullOrEmpty(_lockPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) { - var m = Member.GetMemberFromLoginName(userName); + var m = Member.GetMemberFromLoginName(userName); if (m != null) { - UpdateMemberProperty(m, _lockPropertyTypeAlias, 0); - m.Save(); + UpdateMemberProperty(m, LockPropertyTypeAlias, 0); + m.Save(); return true; } - throw new Exception(String.Format("No member with the username '{0}' found", userName)); + throw new Exception(String.Format("No member with the username '{0}' found", userName)); } - throw new ProviderException("To enable lock/unlocking, you need to add a 'bool' property on your membertype and add the alias of the property in the 'umbracoLockPropertyTypeAlias' attribute of the membership element in the web.config."); + throw new ProviderException("To enable lock/unlocking, you need to add a 'bool' property on your membertype and add the alias of the property in the 'umbracoLockPropertyTypeAlias' attribute of the membership element in the web.config."); } /// @@ -661,28 +694,28 @@ namespace umbraco.providers.members /// A object that represents the user to update and the updated information for the user. public override void UpdateUser(MembershipUser user) { - var m = Member.GetMemberFromLoginName(user.UserName); + var m = Member.GetMemberFromLoginName(user.UserName); m.Email = user.Email; // if supported, update approve status - UpdateMemberProperty(m, _approvedPropertyTypeAlias, user.IsApproved ? 1 : 0); + UpdateMemberProperty(m, ApprovedPropertyTypeAlias, user.IsApproved ? 1 : 0); // if supported, update lock status - UpdateMemberProperty(m, _lockPropertyTypeAlias, user.IsLockedOut ? 1 : 0); + UpdateMemberProperty(m, LockPropertyTypeAlias, user.IsLockedOut ? 1 : 0); if (user.IsLockedOut) { UpdateMemberProperty(m, _lastLockedOutPropertyTypeAlias, DateTime.Now); - } + } // if supported, update comment - UpdateMemberProperty(m, _commentPropertyTypeAlias, user.Comment); + UpdateMemberProperty(m, CommentPropertyTypeAlias, user.Comment); m.Save(); } private static void UpdateMemberProperty(Member m, string propertyAlias, object propertyValue) { - if (string.IsNullOrEmpty(propertyAlias) == false) + if (string.IsNullOrEmpty(propertyAlias) == false) { if (m.getProperty(propertyAlias) != null) { @@ -693,7 +726,7 @@ namespace umbraco.providers.members private static string GetMemberProperty(Member m, string propertyAlias, bool isBool) { - if (string.IsNullOrEmpty(propertyAlias) == false) + if (string.IsNullOrEmpty(propertyAlias) == false) { if (m.getProperty(propertyAlias) != null && m.getProperty(propertyAlias).Value != null) @@ -709,26 +742,26 @@ namespace umbraco.providers.members return null; } - - private static string GetMemberProperty(IMember m, string propertyAlias, bool isBool) - { - if (string.IsNullOrEmpty(propertyAlias) == false) - { - if (m.Properties[propertyAlias] != null && - m.Properties[propertyAlias].Value != null) - { - if (isBool) - { - // Umbraco stored true as 1, which means it can be bool.tryParse'd - return m.Properties[propertyAlias].Value.ToString().Replace("1", "true").Replace("0", "false"); - } - return m.Properties[propertyAlias].Value.ToString(); - } - } - - return null; - } - + + private static string GetMemberProperty(IMember m, string propertyAlias, bool isBool) + { + if (string.IsNullOrEmpty(propertyAlias) == false) + { + if (m.Properties[propertyAlias] != null && + m.Properties[propertyAlias].Value != null) + { + if (isBool) + { + // Umbraco stored true as 1, which means it can be bool.tryParse'd + return m.Properties[propertyAlias].Value.ToString().Replace("1", "true").Replace("0", "false"); + } + return m.Properties[propertyAlias].Value.ToString(); + } + } + + return null; + } + /// /// Verifies that the specified user name and password exist in the data source. /// @@ -739,16 +772,16 @@ namespace umbraco.providers.members /// public override bool ValidateUser(string username, string password) { - var m = Member.GetMemberFromLoginAndEncodedPassword(username, EncryptOrHashExistingPassword(password)); + var m = Member.GetMemberFromLoginAndEncodedPassword(username, EncryptOrHashExistingPassword(password)); if (m != null) { // check for lock status. If locked, then set the member property to null - if (string.IsNullOrEmpty(_lockPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) { - string lockedStatus = GetMemberProperty(m, _lockPropertyTypeAlias, true); - if (string.IsNullOrEmpty(lockedStatus) == false) + string lockedStatus = GetMemberProperty(m, LockPropertyTypeAlias, true); + if (string.IsNullOrEmpty(lockedStatus) == false) { - var isLocked = false; + var isLocked = false; if (bool.TryParse(lockedStatus, out isLocked)) { if (isLocked) @@ -759,44 +792,44 @@ namespace umbraco.providers.members } } - // check for approve status. If not approved, then set the member property to null - if (!CheckApproveStatus(m)) { + //check for approve status. If not approved, then set the member property to null + if (m != null && !CheckApproveStatus(m)) { m = null; } // maybe update login date - if (m != null && string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) + if (m != null && string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) { - UpdateMemberProperty(m, _lastLoginPropertyTypeAlias, DateTime.Now); + UpdateMemberProperty(m, _lastLoginPropertyTypeAlias, DateTime.Now); } // maybe reset password attempts - if (m != null && string.IsNullOrEmpty(_failedPasswordAttemptsPropertyTypeAlias) == false) + if (m != null && string.IsNullOrEmpty(_failedPasswordAttemptsPropertyTypeAlias) == false) { - UpdateMemberProperty(m, _failedPasswordAttemptsPropertyTypeAlias, 0); + UpdateMemberProperty(m, _failedPasswordAttemptsPropertyTypeAlias, 0); } // persist data if (m != null) m.Save(); } - else if (string.IsNullOrEmpty(_lockPropertyTypeAlias) == false - && string.IsNullOrEmpty(_failedPasswordAttemptsPropertyTypeAlias) == false) + else if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false + && string.IsNullOrEmpty(_failedPasswordAttemptsPropertyTypeAlias) == false) { - var updateMemberDataObject = Member.GetMemberFromLoginName(username); + var updateMemberDataObject = Member.GetMemberFromLoginName(username); // update fail rate if it's approved if (updateMemberDataObject != null && CheckApproveStatus(updateMemberDataObject)) { int failedAttempts = 0; - int.TryParse(GetMemberProperty(updateMemberDataObject, _failedPasswordAttemptsPropertyTypeAlias, false), out failedAttempts); + int.TryParse(GetMemberProperty(updateMemberDataObject, _failedPasswordAttemptsPropertyTypeAlias, false), out failedAttempts); failedAttempts = failedAttempts+1; - UpdateMemberProperty(updateMemberDataObject, _failedPasswordAttemptsPropertyTypeAlias, failedAttempts); + UpdateMemberProperty(updateMemberDataObject, _failedPasswordAttemptsPropertyTypeAlias, failedAttempts); // lock user? if (failedAttempts >= MaxInvalidPasswordAttempts) { - UpdateMemberProperty(updateMemberDataObject, _lockPropertyTypeAlias, 1); - UpdateMemberProperty(updateMemberDataObject, _lastLockedOutPropertyTypeAlias, DateTime.Now); + UpdateMemberProperty(updateMemberDataObject, LockPropertyTypeAlias, 1); + UpdateMemberProperty(updateMemberDataObject, _lastLockedOutPropertyTypeAlias, DateTime.Now); } updateMemberDataObject.Save(); } @@ -807,13 +840,13 @@ namespace umbraco.providers.members private bool CheckApproveStatus(Member m) { - var isApproved = false; - if (string.IsNullOrEmpty(_approvedPropertyTypeAlias) == false) + var isApproved = false; + if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) { if (m != null) { - var approveStatus = GetMemberProperty(m, _approvedPropertyTypeAlias, true); - if (string.IsNullOrEmpty(approveStatus) == false) + var approveStatus = GetMemberProperty(m, ApprovedPropertyTypeAlias, true); + if (string.IsNullOrEmpty(approveStatus) == false) { //try parsing as bool first (just in case) if (bool.TryParse(approveStatus, out isApproved) == false) @@ -826,6 +859,11 @@ namespace umbraco.providers.members } } } + else + { + //There is no property so we shouldn't use the approve status + isApproved = true; + } } } else { @@ -837,7 +875,7 @@ namespace umbraco.providers.members #endregion #region Helper Methods - + /// /// Checks the password. /// @@ -852,10 +890,10 @@ namespace umbraco.providers.members switch (PasswordFormat) { case MembershipPasswordFormat.Encrypted: - pass2 = DecodePassword(dbPassword); + pass2 = DecodePassword(dbPassword); break; case MembershipPasswordFormat.Hashed: - pass1 = EncryptOrHashExistingPassword(password); + pass1 = EncryptOrHashExistingPassword(password); break; default: break; @@ -869,10 +907,10 @@ namespace umbraco.providers.members /// /// The password. /// The encoded password. - [Obsolete("Do not use this, it is the legacy way to encode a password")] + [Obsolete("Do not use this, it is the legacy way to encode a password")] public string EncodePassword(string password) { - return LegacyEncodePassword(password); + return LegacyEncodePassword(password); } /// @@ -880,10 +918,10 @@ namespace umbraco.providers.members /// /// The encoded password. /// The unencoded password. - [Obsolete("Do not use this, it is the legacy way to decode a password")] + [Obsolete("Do not use this, it is the legacy way to decode a password")] public string UnEncodePassword(string encodedPassword) { - return LegacyUnEncodePassword(encodedPassword); + return LegacyUnEncodePassword(encodedPassword); } /// @@ -894,130 +932,100 @@ namespace umbraco.providers.members private MembershipUser ConvertToMembershipUser(Member m) { if (m == null) return null; - + var lastLogin = DateTime.Now; - var lastLocked = DateTime.MinValue; - var isApproved = true; - var isLocked = false; - var comment = ""; - var passwordQuestion = ""; - - // last login - if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) - { - DateTime.TryParse(GetMemberProperty(m, _lastLoginPropertyTypeAlias, false), out lastLogin); - } - // approved - if (string.IsNullOrEmpty(_approvedPropertyTypeAlias) == false) - { - bool.TryParse(GetMemberProperty(m, _approvedPropertyTypeAlias, true), out isApproved); - } - // locked - if (string.IsNullOrEmpty(_lockPropertyTypeAlias) == false) - { - bool.TryParse(GetMemberProperty(m, _lockPropertyTypeAlias, true), out isLocked); + var lastLocked = DateTime.MinValue; + var isApproved = true; + var isLocked = false; + var comment = ""; + var passwordQuestion = ""; + + // last login + if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) + { + DateTime.TryParse(GetMemberProperty(m, _lastLoginPropertyTypeAlias, false), out lastLogin); + } + // approved + if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) + { + bool.TryParse(GetMemberProperty(m, ApprovedPropertyTypeAlias, true), out isApproved); + } + // locked + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) + { + bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLocked); } // last locked if (string.IsNullOrEmpty(_lastLockedOutPropertyTypeAlias) == false) { DateTime.TryParse(GetMemberProperty(m, _lastLockedOutPropertyTypeAlias, false), out lastLocked); - } - // comment - if (string.IsNullOrEmpty(_commentPropertyTypeAlias) == false) - { - comment = GetMemberProperty(m, _commentPropertyTypeAlias, false); - } - // password question - if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) + } + // comment + if (string.IsNullOrEmpty(CommentPropertyTypeAlias) == false) { - passwordQuestion = GetMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, false); - } + comment = GetMemberProperty(m, CommentPropertyTypeAlias, false); + } + // password question + if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) + { + passwordQuestion = GetMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, false); + } return new MembershipUser(_providerName, m.LoginName, m.Id, m.Email, passwordQuestion, comment, isApproved, isLocked, m.CreateDateTime, lastLogin, - DateTime.Now, DateTime.Now, lastLocked); - } + DateTime.Now, DateTime.Now, lastLocked); + } + + /// + /// Converts to membership user. + /// + /// The m. + /// + private MembershipUser ConvertToMembershipUser(IMember m) + { + if (m == null) return null; - /// - /// Converts to membership user. - /// - /// The m. - /// - private MembershipUser ConvertToMembershipUser(IMember m) - { - if (m == null) return null; - var lastLogin = DateTime.Now; - var lastLocked = DateTime.MinValue; - var isApproved = true; - var isLocked = false; - var comment = ""; - var passwordQuestion = ""; - - // last login - if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) - { - DateTime.TryParse(GetMemberProperty(m, _lastLoginPropertyTypeAlias, false), out lastLogin); - } - // approved - if (string.IsNullOrEmpty(_approvedPropertyTypeAlias) == false) - { - bool.TryParse(GetMemberProperty(m, _approvedPropertyTypeAlias, true), out isApproved); - } - // locked - if (string.IsNullOrEmpty(_lockPropertyTypeAlias) == false) - { - bool.TryParse(GetMemberProperty(m, _lockPropertyTypeAlias, true), out isLocked); + var lastLocked = DateTime.MinValue; + var isApproved = true; + var isLocked = false; + var comment = ""; + var passwordQuestion = ""; + + // last login + if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) + { + DateTime.TryParse(GetMemberProperty(m, _lastLoginPropertyTypeAlias, false), out lastLogin); + } + // approved + if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) + { + bool.TryParse(GetMemberProperty(m, ApprovedPropertyTypeAlias, true), out isApproved); + } + // locked + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) + { + bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLocked); } // last locked if (string.IsNullOrEmpty(_lastLockedOutPropertyTypeAlias) == false) { DateTime.TryParse(GetMemberProperty(m, _lastLockedOutPropertyTypeAlias, false), out lastLocked); - } - // comment - if (string.IsNullOrEmpty(_commentPropertyTypeAlias) == false) - { - comment = GetMemberProperty(m, _commentPropertyTypeAlias, false); } - // password question - if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) - { - passwordQuestion = GetMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, false); - } - + // comment + if (string.IsNullOrEmpty(CommentPropertyTypeAlias) == false) + { + comment = GetMemberProperty(m, CommentPropertyTypeAlias, false); + } + // password question + if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) + { + passwordQuestion = GetMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, false); + } + return new MembershipUser(_providerName, m.Username, m.Id, m.Email, passwordQuestion, comment, isApproved, isLocked, m.CreateDate, lastLogin, - DateTime.Now, DateTime.Now, lastLocked); + DateTime.Now, DateTime.Now, lastLocked); } #endregion } - - //TODO: We need to re-enable this in 6.2, but need to back port most of the membership provider changes (still in a custom branch atm) - - ///// - ///// Adds some event handling - ///// - //public class MembershipEventHandler : ApplicationEventHandler - //{ - // protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) - // { - // Member.New += Member_New; - // } - - // void Member_New(Member sender, NewEventArgs e) - // { - // //This is a bit of a hack to ensure that the member is approved when created since many people will be using - // // this old api to create members on the front-end and they need to be approved - which is based on whether or not - // // the Umbraco membership provider is configured. - // var provider = Membership.Provider as UmbracoMembershipProvider; - // if (provider != null) - // { - // var approvedField = provider.ApprovedPropertyTypeAlias; - // var property = sender.getProperty(approvedField); - // if (property != null) - // { - // property.Value = 1; - // } - // } - // } - //} } diff --git a/src/umbraco.providers/members/MembershipEventHandler.cs b/src/umbraco.providers/members/MembershipEventHandler.cs new file mode 100644 index 0000000000..71330dd8e0 --- /dev/null +++ b/src/umbraco.providers/members/MembershipEventHandler.cs @@ -0,0 +1,35 @@ +using System.Web.Security; +using Umbraco.Core; +using umbraco.cms.businesslogic; +using umbraco.cms.businesslogic.member; + +namespace umbraco.providers.members +{ + /// + /// Adds some event handling + /// + public class MembershipEventHandler : ApplicationEventHandler + { + protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) + { + Member.New += Member_New; + } + + void Member_New(Member sender, NewEventArgs e) + { + //This is a bit of a hack to ensure that the member is approved when created since many people will be using + // this old api to create members on the front-end and they need to be approved - which is based on whether or not + // the Umbraco membership provider is configured. + var provider = Membership.Provider as UmbracoMembershipProvider; + if (provider != null) + { + var approvedField = provider.ApprovedPropertyTypeAlias; + var property = sender.getProperty(approvedField); + if (property != null) + { + property.Value = 1; + } + } + } + } +} \ No newline at end of file diff --git a/src/umbraco.providers/umbraco.providers.csproj b/src/umbraco.providers/umbraco.providers.csproj index 2a8c4503be..de33276993 100644 --- a/src/umbraco.providers/umbraco.providers.csproj +++ b/src/umbraco.providers/umbraco.providers.csproj @@ -79,6 +79,7 @@ Properties\SolutionInfo.cs + From f11d4fbeddfd58ac51c080d7e23c2c3eb5a0038a Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 12:51:02 +1100 Subject: [PATCH 2/8] Worked on the application cache, added request cache, changed some stuff over to be interfaces, added unit tests suite (need mroe) to test all caching providers. Conflicts: src/Umbraco.Core/Cache/CacheProviderBase.cs src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs src/Umbraco.Core/Cache/NullCacheProvider.cs src/Umbraco.Core/Cache/StaticCacheProvider.cs --- src/Umbraco.Core/Cache/CacheProviderBase.cs | 25 --- .../Cache/DictionaryCacheProdiverBase.cs | 178 ++++++++++++++++ .../Cache/DictionaryCacheWrapper.cs | 45 ++++ .../Cache/DictionaryItemWrapper.cs | 14 ++ .../Cache/HttpRequestCacheProvider.cs | 50 +++++ .../Cache/HttpRuntimeCacheProvider.cs | 177 +++------------- src/Umbraco.Core/Cache/ICacheProvider.cs | 24 +++ .../Cache/IRuntimeCacheProvider.cs | 24 +++ src/Umbraco.Core/Cache/NullCacheProvider.cs | 34 +-- .../Cache/ObjectCacheRuntimeCacheProvider.cs | 198 ++++++++++++++++++ .../Cache/RuntimeCacheProviderBase.cs | 24 --- src/Umbraco.Core/Cache/StaticCacheProvider.cs | 75 ++++--- src/Umbraco.Core/CacheHelper.cs | 172 ++++++++++++++- src/Umbraco.Core/Umbraco.Core.csproj | 9 +- src/Umbraco.Tests/Cache/CacheHelperTests.cs | 86 -------- src/Umbraco.Tests/Cache/CacheProviderTests.cs | 63 ++++++ .../Cache/HttpRequestCacheProviderTests.cs | 52 +++++ .../Cache/HttpRuntimeCacheProviderTests.cs | 33 +++ .../Cache/ObjectCacheProviderTests.cs | 36 ++++ .../Cache/RuntimeCacheProviderTests.cs | 30 +++ src/Umbraco.Tests/Umbraco.Tests.csproj | 7 +- 21 files changed, 1012 insertions(+), 344 deletions(-) delete mode 100644 src/Umbraco.Core/Cache/CacheProviderBase.cs create mode 100644 src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs create mode 100644 src/Umbraco.Core/Cache/DictionaryCacheWrapper.cs create mode 100644 src/Umbraco.Core/Cache/DictionaryItemWrapper.cs create mode 100644 src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs create mode 100644 src/Umbraco.Core/Cache/ICacheProvider.cs create mode 100644 src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs create mode 100644 src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs delete mode 100644 src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs delete mode 100644 src/Umbraco.Tests/Cache/CacheHelperTests.cs create mode 100644 src/Umbraco.Tests/Cache/CacheProviderTests.cs create mode 100644 src/Umbraco.Tests/Cache/HttpRequestCacheProviderTests.cs create mode 100644 src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs create mode 100644 src/Umbraco.Tests/Cache/ObjectCacheProviderTests.cs create mode 100644 src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs diff --git a/src/Umbraco.Core/Cache/CacheProviderBase.cs b/src/Umbraco.Core/Cache/CacheProviderBase.cs deleted file mode 100644 index 012d8b23b2..0000000000 --- a/src/Umbraco.Core/Cache/CacheProviderBase.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Core.Cache -{ - /// - /// An abstract class for implementing a basic cache provider - /// - /// - /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, ETC... - /// - internal abstract class CacheProviderBase - { - public abstract void ClearAllCache(); - public abstract void ClearCacheItem(string key); - public abstract void ClearCacheObjectTypes(string typeName); - public abstract void ClearCacheObjectTypes(); - public abstract void ClearCacheObjectTypes(Func predicate); - public abstract void ClearCacheByKeySearch(string keyStartsWith); - public abstract void ClearCacheByKeyExpression(string regexString); - public abstract IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); - public abstract T GetCacheItem(string cacheKey); - public abstract T GetCacheItem(string cacheKey, Func getCacheItem); - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs b/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs new file mode 100644 index 0000000000..adc02f9104 --- /dev/null +++ b/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Umbraco.Core.Cache +{ + internal abstract class DictionaryCacheProdiverBase : ICacheProvider + { + private static readonly object Locker = new object(); + protected abstract DictionaryCacheWrapper DictionaryCache { get; } + + /// + /// Clears everything in umbraco's runtime cache + /// + /// + /// Does not clear other stuff the user has put in httpruntime.cache! + /// + public virtual void ClearAllCache() + { + lock (Locker) + { + var keysToRemove = DictionaryCache.Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => c.Key is string && ((string)c.Key).StartsWith(CacheItemPrefix) && DictionaryCache[c.Key.ToString()] != null) + .Select(c => c.Key) + .ToList(); + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + } + } + + /// + /// Clears the item in umbraco's runtime cache with the given key + /// + /// Key + public virtual void ClearCacheItem(string key) + { + lock (Locker) + { + if (DictionaryCache[GetCacheKey(key)] == null) return; + DictionaryCache.Remove(GetCacheKey(key)); ; + } + } + + /// + /// Clears all objects in the System.Web.Cache with the System.Type name as the + /// input parameter. (using [object].GetType()) + /// + /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" + public virtual void ClearCacheObjectTypes(string typeName) + { + lock (Locker) + { + var keysToRemove = DictionaryCache.Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => DictionaryCache[c.Key.ToString()] != null && DictionaryCache[c.Key.ToString()].GetType().ToString().InvariantEquals(typeName)) + .Select(c => c.Key) + .ToList(); + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + } + } + + /// + /// Clears all objects in the System.Web.Cache with the System.Type specified + /// + public virtual void ClearCacheObjectTypes() + { + lock (Locker) + { + var keysToRemove = DictionaryCache.Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => DictionaryCache[c.Key.ToString()] != null && DictionaryCache[c.Key.ToString()].GetType() == typeof (T)) + .Select(c => c.Key) + .ToList(); + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + + } + } + + /// + /// Clears all cache items that starts with the key passed. + /// + /// The start of the key + public virtual void ClearCacheByKeySearch(string keyStartsWith) + { + var keysToRemove = DictionaryCache.Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => c.Key is string && ((string)c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))) + .Select(c => c.Key) + .ToList(); + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + } + + /// + /// Clears all cache items that have a key that matches the regular expression + /// + /// + public virtual void ClearCacheByKeyExpression(string regexString) + { + var keysToRemove = new List(); + foreach (var item in DictionaryCache) + { + var c = new DictionaryItemWrapper(item); + var s = c.Key as string; + if (s != null) + { + var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix)); + if (Regex.IsMatch(withoutPrefix, regexString)) + { + keysToRemove.Add(c.Key); + } + } + } + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + } + + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + { + return (from object item in DictionaryCache + select new DictionaryItemWrapper(item) + into c + where c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)) + select c.Value.TryConvertTo() + into converted + where converted.Success + select converted.Result).ToList(); + } + + /// + /// Returns a cache item by key, does not update the cache if it isn't there. + /// + /// + /// + /// + public virtual TT GetCacheItem(string cacheKey) + { + var result = DictionaryCache.Get(GetCacheKey(cacheKey)); + if (result == null) + { + return default(TT); + } + return result.TryConvertTo().Result; + } + + public abstract T GetCacheItem(string cacheKey, Func getCacheItem); + + /// + /// We prefix all cache keys with this so that we know which ones this class has created when + /// using the HttpRuntime cache so that when we clear it we don't clear other entries we didn't create. + /// + protected const string CacheItemPrefix = "umbrtmche"; + + protected string GetCacheKey(string key) + { + return string.Format("{0}-{1}", CacheItemPrefix, key); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DictionaryCacheWrapper.cs b/src/Umbraco.Core/Cache/DictionaryCacheWrapper.cs new file mode 100644 index 0000000000..8203b6e481 --- /dev/null +++ b/src/Umbraco.Core/Cache/DictionaryCacheWrapper.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; + +namespace Umbraco.Core.Cache +{ + internal class DictionaryCacheWrapper : IEnumerable + { + private readonly IEnumerable _inner; + private readonly Func _get; + private readonly Action _remove; + + public DictionaryCacheWrapper( + IEnumerable inner, + Func get, + Action remove) + { + _inner = inner; + _get = get; + _remove = remove; + } + + public object this[object key] + { + get + { + return Get(key); + } + } + + public object Get(object key) + { + return _get(key); + } + + public void Remove(object key) + { + _remove(key); + } + + public IEnumerator GetEnumerator() + { + return _inner.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DictionaryItemWrapper.cs b/src/Umbraco.Core/Cache/DictionaryItemWrapper.cs new file mode 100644 index 0000000000..4a1ecaf9ec --- /dev/null +++ b/src/Umbraco.Core/Cache/DictionaryItemWrapper.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Core.Cache +{ + internal class DictionaryItemWrapper + { + public DictionaryItemWrapper(dynamic item) + { + Key = item.Key; + Value = item.Value; + } + + public object Key { get; private set; } + public object Value { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs new file mode 100644 index 0000000000..134b780f2b --- /dev/null +++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Web; + +namespace Umbraco.Core.Cache +{ + /// + /// A cache provider that caches items in the HttpContext.Items + /// + internal class HttpRequestCacheProvider : DictionaryCacheProdiverBase + { + private readonly Func _context; + + public HttpRequestCacheProvider(HttpContext context) + { + _context = () => new HttpContextWrapper(context); + } + + public HttpRequestCacheProvider(Func context) + { + _context = context; + } + + protected override DictionaryCacheWrapper DictionaryCache + { + get + { + var ctx = _context(); + return new DictionaryCacheWrapper( + ctx.Items, + o => ctx.Items[o], + o => ctx.Items.Remove(o)); + } + } + + public override T GetCacheItem(string cacheKey, Func getCacheItem) + { + var ctx = _context(); + var ck = GetCacheKey(cacheKey); + if (ctx.Items[ck] == null) + { + ctx.Items[ck] = getCacheItem(); + } + return (T)ctx.Items[ck]; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index f5fc0e237f..02b4aa46d2 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -1,109 +1,27 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; +using System.Web; using System.Web.Caching; using Umbraco.Core.Logging; +using CacheItemPriority = System.Web.Caching.CacheItemPriority; namespace Umbraco.Core.Cache { /// /// A CacheProvider that wraps the logic of the HttpRuntime.Cache /// - internal class HttpRuntimeCacheProvider : RuntimeCacheProviderBase + internal class HttpRuntimeCacheProvider : DictionaryCacheProdiverBase, IRuntimeCacheProvider { private readonly System.Web.Caching.Cache _cache; + private readonly DictionaryCacheWrapper _wrapper; private static readonly object Locker = new object(); - + public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache) { _cache = cache; + _wrapper = new DictionaryCacheWrapper(_cache, s => _cache.Get(s.ToString()), o => _cache.Remove(o.ToString())); } - /// - /// Clears everything in umbraco's runtime cache, which means that not only - /// umbraco content is removed, but also other cache items from pages running in - /// the same application / website. Use with care :-) - /// - public override void ClearAllCache() - { - var cacheEnumerator = _cache.GetEnumerator(); - while (cacheEnumerator.MoveNext()) - { - _cache.Remove(cacheEnumerator.Key.ToString()); - } - } - - /// - /// Clears the item in umbraco's runtime cache with the given key - /// - /// Key - public override void ClearCacheItem(string key) - { - // NH 10 jan 2012 - // Patch by the always wonderful Stéphane Gay to avoid cache null refs - lock (Locker) - { - if (_cache[key] == null) return; - _cache.Remove(key); ; - } - } - - - /// - /// Clears all objects in the System.Web.Cache with the System.Type name as the - /// input parameter. (using [object].GetType()) - /// - /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" - public override void ClearCacheObjectTypes(string typeName) - { - try - { - lock (Locker) - { - foreach (DictionaryEntry c in _cache) - { - if (_cache[c.Key.ToString()] != null - && _cache[c.Key.ToString()].GetType().ToString().InvariantEquals(typeName)) - { - _cache.Remove(c.Key.ToString()); - } - } - } - } - catch (Exception e) - { - LogHelper.Error("Cache clearing error", e); - } - } - - /// - /// Clears all objects in the System.Web.Cache with the System.Type specified - /// - public override void ClearCacheObjectTypes() - { - try - { - lock (Locker) - { - foreach (DictionaryEntry c in _cache) - { - if (_cache[c.Key.ToString()] != null - && _cache[c.Key.ToString()].GetType() == typeof(T)) - { - _cache.Remove(c.Key.ToString()); - } - } - } - } - catch (Exception e) - { - LogHelper.Error("Cache clearing error", e); - } - } - - /// + protected override DictionaryCacheWrapper DictionaryCache /// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate /// public override void ClearCacheObjectTypes(Func predicate) @@ -131,69 +49,18 @@ namespace Umbraco.Core.Cache } /// - /// Clears all cache items that starts with the key passed. - /// - /// The start of the key - public override void ClearCacheByKeySearch(string keyStartsWith) { - foreach (DictionaryEntry c in _cache) - { - if (c.Key is string && ((string)c.Key).InvariantStartsWith(keyStartsWith)) - { - ClearCacheItem((string)c.Key); - } - } - } - - /// - /// Clears all cache items that have a key that matches the regular expression - /// - /// - public override void ClearCacheByKeyExpression(string regexString) - { - foreach (DictionaryEntry c in _cache) - { - if (c.Key is string && Regex.IsMatch(((string)c.Key), regexString)) - { - ClearCacheItem((string)c.Key); - } - } - } - - public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) - { - return (from DictionaryEntry c in _cache - where c.Key is string && ((string)c.Key).InvariantStartsWith(keyStartsWith) - select c.Value.TryConvertTo() - into attempt - where attempt.Success - select attempt.Result).ToList(); - } - - /// - /// Returns a cache item by key, does not update the cache if it isn't there. - /// - /// - /// - /// - public override TT GetCacheItem(string cacheKey) - { - var result = _cache.Get(cacheKey); - if (result == null) - { - return default(TT); - } - return result.TryConvertTo().Result; + get { return _wrapper; } } /// /// Gets (and adds if necessary) an item from the cache with all of the default parameters /// - /// + /// /// /// /// - public override TT GetCacheItem(string cacheKey, Func getCacheItem) + public override T GetCacheItem(string cacheKey, Func getCacheItem) { return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem, Locker); } @@ -206,7 +73,7 @@ namespace Umbraco.Core.Cache /// This will set an absolute expiration from now until the timeout /// /// - public override TT GetCacheItem(string cacheKey, + public virtual TT GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) { return GetCacheItem(cacheKey, null, timeout, getCacheItem); @@ -221,7 +88,7 @@ namespace Umbraco.Core.Cache /// This will set an absolute expiration from now until the timeout /// /// - public override TT GetCacheItem(string cacheKey, + public virtual TT GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) { @@ -238,7 +105,7 @@ namespace Umbraco.Core.Cache /// This will set an absolute expiration from now until the timeout /// /// - public override TT GetCacheItem(string cacheKey, + public virtual TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) { @@ -256,7 +123,7 @@ namespace Umbraco.Core.Cache /// This will set an absolute expiration from now until the timeout /// /// - public override TT GetCacheItem(string cacheKey, + public virtual TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, @@ -282,12 +149,14 @@ namespace Umbraco.Core.Cache CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem, object syncLock) { - var result = _cache.Get(cacheKey); + cacheKey = GetCacheKey(cacheKey); + + var result = DictionaryCache.Get(cacheKey); if (result == null) { lock (syncLock) { - result = _cache.Get(cacheKey); + result = DictionaryCache.Get(cacheKey); if (result == null) { result = getCacheItem(); @@ -311,7 +180,7 @@ namespace Umbraco.Core.Cache /// /// /// - public override void InsertCacheItem(string cacheKey, + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) { @@ -326,7 +195,7 @@ namespace Umbraco.Core.Cache /// /// This will set an absolute expiration from now until the timeout /// - public override void InsertCacheItem(string cacheKey, + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) @@ -343,7 +212,7 @@ namespace Umbraco.Core.Cache /// /// This will set an absolute expiration from now until the timeout /// - public override void InsertCacheItem(string cacheKey, + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, @@ -362,7 +231,7 @@ namespace Umbraco.Core.Cache /// /// This will set an absolute expiration from now until the timeout /// - public override void InsertCacheItem(string cacheKey, + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, @@ -372,6 +241,8 @@ namespace Umbraco.Core.Cache object result = getCacheItem(); if (result != null) { + cacheKey = GetCacheKey(cacheKey); + //we use Insert instead of add if for some crazy reason there is now a cache with the cache key in there, it will just overwrite it. _cache.Insert(cacheKey, result, cacheDependency, timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value), diff --git a/src/Umbraco.Core/Cache/ICacheProvider.cs b/src/Umbraco.Core/Cache/ICacheProvider.cs new file mode 100644 index 0000000000..ab70cf63c0 --- /dev/null +++ b/src/Umbraco.Core/Cache/ICacheProvider.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Cache +{ + /// + /// An abstract class for implementing a basic cache provider + /// + /// + /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, ETC... + /// + internal interface ICacheProvider + { + void ClearAllCache(); + void ClearCacheItem(string key); + void ClearCacheObjectTypes(string typeName); + void ClearCacheObjectTypes(); + void ClearCacheByKeySearch(string keyStartsWith); + void ClearCacheByKeyExpression(string regexString); + IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); + T GetCacheItem(string cacheKey); + T GetCacheItem(string cacheKey, Func getCacheItem); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs new file mode 100644 index 0000000000..8dd1d31af0 --- /dev/null +++ b/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs @@ -0,0 +1,24 @@ +using System; +using System.Text; +using System.Web.Caching; + +namespace Umbraco.Core.Cache +{ + /// + /// An abstract class for implementing a runtime cache provider + /// + /// + /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, REQUEST CACHE, ETC... + /// + internal interface IRuntimeCacheProvider : ICacheProvider + { + T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem); + T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); + T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); + T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); + void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem); + void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem); + void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); + void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); + } +} diff --git a/src/Umbraco.Core/Cache/NullCacheProvider.cs b/src/Umbraco.Core/Cache/NullCacheProvider.cs index 0ffecf59e3..ff993bdbc0 100644 --- a/src/Umbraco.Core/Cache/NullCacheProvider.cs +++ b/src/Umbraco.Core/Cache/NullCacheProvider.cs @@ -5,21 +5,21 @@ using System.Web.Caching; namespace Umbraco.Core.Cache { - internal class NullCacheProvider : RuntimeCacheProviderBase + internal class NullCacheProvider : IRuntimeCacheProvider { - public override void ClearAllCache() + public virtual void ClearAllCache() { } - public override void ClearCacheItem(string key) + public virtual void ClearCacheItem(string key) { } - public override void ClearCacheObjectTypes(string typeName) + public virtual void ClearCacheObjectTypes(string typeName) { } - public override void ClearCacheObjectTypes() + public virtual void ClearCacheObjectTypes() { } @@ -31,58 +31,58 @@ namespace Umbraco.Core.Cache { } - public override void ClearCacheByKeyExpression(string regexString) + public virtual void ClearCacheByKeyExpression(string regexString) { } - public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { return Enumerable.Empty(); } - public override T GetCacheItem(string cacheKey) + public virtual T GetCacheItem(string cacheKey) { return default(T); } - public override T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, Func getCacheItem) { return getCacheItem(); } - public override T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) { return getCacheItem(); } - public override T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) { return getCacheItem(); } - public override T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) { return getCacheItem(); } - public override T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) { return getCacheItem(); } - public override void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) { } - public override void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) { } - public override void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) { } - public override void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) { } } diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs new file mode 100644 index 0000000000..c9a37d830d --- /dev/null +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.Caching; +using System.Text.RegularExpressions; +using System.Threading; +using System.Web.Caching; +using Umbraco.Core.Logging; +using CacheItemPriority = System.Web.Caching.CacheItemPriority; + +namespace Umbraco.Core.Cache +{ + /// + /// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache + /// + internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider + { + private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim(); + internal ObjectCache MemoryCache; + + public ObjectCacheRuntimeCacheProvider() + { + MemoryCache = new MemoryCache("in-memory"); + } + + public virtual void ClearAllCache() + { + using (new WriteLock(ClearLock)) + { + MemoryCache.DisposeIfDisposable(); + MemoryCache = new MemoryCache("in-memory"); + } + } + + public virtual void ClearCacheItem(string key) + { + using (new WriteLock(ClearLock)) + { + if (MemoryCache[key] == null) return; + MemoryCache.Remove(key); + } + } + + public virtual void ClearCacheObjectTypes(string typeName) + { + using (new WriteLock(ClearLock)) + { + var keysToRemove = (from c in MemoryCache where c.Value.GetType().ToString().InvariantEquals(typeName) select c.Key).ToList(); + foreach (var k in keysToRemove) + { + MemoryCache.Remove(k); + } + } + } + + public virtual void ClearCacheObjectTypes() + { + using (new WriteLock(ClearLock)) + { + var keysToRemove = (from c in MemoryCache where c.Value.GetType() == typeof (T) select c.Key).ToList(); + foreach (var k in keysToRemove) + { + MemoryCache.Remove(k); + } + } + } + + public virtual void ClearCacheByKeySearch(string keyStartsWith) + { + using (new WriteLock(ClearLock)) + { + var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList(); + foreach (var k in keysToRemove) + { + MemoryCache.Remove(k); + } + } + } + + public virtual void ClearCacheByKeyExpression(string regexString) + { + using (new WriteLock(ClearLock)) + { + var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList(); + foreach (var k in keysToRemove) + { + MemoryCache.Remove(k); + } + } + } + + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + { + return (from c in MemoryCache + where c.Key.InvariantStartsWith(keyStartsWith) + select c.Value.TryConvertTo() + into attempt + where attempt.Success + select attempt.Result).ToList(); + } + + public virtual T GetCacheItem(string cacheKey) + { + var result = MemoryCache.Get(cacheKey); + if (result == null) + { + return default(T); + } + return result.TryConvertTo().Result; + } + + public virtual T GetCacheItem(string cacheKey, Func getCacheItem) + { + return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem); + } + + public virtual T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) + { + return GetCacheItem(cacheKey, null, timeout, getCacheItem); + } + + public virtual T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) + { + return GetCacheItem(cacheKey, CacheItemPriority.Normal, refreshAction, timeout, getCacheItem); + } + + public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) + { + return GetCacheItem(cacheKey, priority, refreshAction, null, timeout, getCacheItem); + } + + public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + { + using (var lck = new UpgradeableReadLock(ClearLock)) + { + var result = MemoryCache.Get(cacheKey); + if (result == null) + { + lck.UpgradeToWriteLock(); + + result = getCacheItem(); + if (result != null) + { + var policy = new CacheItemPolicy + { + AbsoluteExpiration = timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value), + SlidingExpiration = TimeSpan.Zero + }; + + //TODO: CUrrently we cannot implement this in this provider, we'll have to change the underlying interface + // to accept an array of files instead of CacheDependency. + //policy.ChangeMonitors.Add(new HostFileChangeMonitor(cacheDependency.)); + + MemoryCache.Set(cacheKey, result, policy); + } + } + return result.TryConvertTo().Result; + } + } + + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) + { + InsertCacheItem(cacheKey, priority, null, null, null, getCacheItem); + } + + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) + { + InsertCacheItem(cacheKey, priority, null, null, timeout, getCacheItem); + } + + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + { + InsertCacheItem(cacheKey, priority, null, cacheDependency, timeout, getCacheItem); + } + + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + { + object result = getCacheItem(); + if (result != null) + { + + var policy = new CacheItemPolicy + { + AbsoluteExpiration = timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value), + SlidingExpiration = TimeSpan.Zero + }; + + //TODO: CUrrently we cannot implement this in this provider, we'll have to change the underlying interface + // to accept an array of files instead of CacheDependency. + //policy.ChangeMonitors.Add(new HostFileChangeMonitor(cacheDependency.)); + + MemoryCache.Set(cacheKey, result, policy); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs b/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs deleted file mode 100644 index d88a3922bb..0000000000 --- a/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Text; -using System.Web.Caching; - -namespace Umbraco.Core.Cache -{ - /// - /// An abstract class for implementing a runtime cache provider - /// - /// - /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, REQUEST CACHE, ETC... - /// - internal abstract class RuntimeCacheProviderBase : CacheProviderBase - { - public abstract T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem); - public abstract T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - public abstract T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - public abstract T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - public abstract void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem); - public abstract void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem); - public abstract void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - public abstract void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - } -} diff --git a/src/Umbraco.Core/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs index 765e84fd7c..67c0527393 100644 --- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs +++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs @@ -10,51 +10,72 @@ namespace Umbraco.Core.Cache /// /// A cache provider that statically caches everything in an in memory dictionary /// - internal class StaticCacheProvider : CacheProviderBase + internal class StaticCacheProvider : ICacheProvider { - private readonly ConcurrentDictionary _staticCache = new ConcurrentDictionary(); + internal readonly ConcurrentDictionary StaticCache = new ConcurrentDictionary(); - public override void ClearAllCache() + public virtual void ClearAllCache() { - _staticCache.Clear(); + StaticCache.Clear(); } - public override void ClearCacheItem(string key) + public virtual void ClearCacheItem(string key) { object val; - _staticCache.TryRemove(key, out val); + StaticCache.TryRemove(key, out val); } - public override void ClearCacheObjectTypes(string typeName) + public virtual void ClearCacheObjectTypes(string typeName) { - _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName)); + foreach (var key in StaticCache.Keys) + { + if (StaticCache[key] != null + && StaticCache[key].GetType().ToString().InvariantEquals(typeName)) + { + object val; + StaticCache.TryRemove(key, out val); + } + } } - public override void ClearCacheObjectTypes() + public virtual void ClearCacheObjectTypes() { - var typeOfT = typeof (T); - _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT); + foreach (var key in StaticCache.Keys) + { + if (StaticCache[key] != null + && StaticCache[key].GetType() == typeof(T)) + { + object val; + StaticCache.TryRemove(key, out val); + } + } } - public override void ClearCacheObjectTypes(Func predicate) + public virtual void ClearCacheByKeySearch(string keyStartsWith) { - var typeOfT = typeof(T); - _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value)); + foreach (var key in StaticCache.Keys) + { + if (key.InvariantStartsWith(keyStartsWith)) + { + ClearCacheItem(key); + } + } } - public override void ClearCacheByKeySearch(string keyStartsWith) + public virtual void ClearCacheByKeyExpression(string regexString) { - _staticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)); + foreach (var key in StaticCache.Keys) + { + if (Regex.IsMatch(key, regexString)) + { + ClearCacheItem(key); + } + } } - public override void ClearCacheByKeyExpression(string regexString) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { - _staticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString)); - } - - public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) - { - return (from KeyValuePair c in _staticCache + return (from KeyValuePair c in StaticCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Value.TryConvertTo() into attempt @@ -62,9 +83,9 @@ namespace Umbraco.Core.Cache select attempt.Result).ToList(); } - public override T GetCacheItem(string cacheKey) + public virtual T GetCacheItem(string cacheKey) { - var result = _staticCache[cacheKey]; + var result = StaticCache[cacheKey]; if (result == null) { return default(T); @@ -72,9 +93,9 @@ namespace Umbraco.Core.Cache return result.TryConvertTo().Result; } - public override T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, Func getCacheItem) { - return (T)_staticCache.GetOrAdd(cacheKey, key => getCacheItem()); + return (T)StaticCache.GetOrAdd(cacheKey, getCacheItem()); } } diff --git a/src/Umbraco.Core/CacheHelper.cs b/src/Umbraco.Core/CacheHelper.cs index 8f7edeb777..7ec76f165c 100644 --- a/src/Umbraco.Core/CacheHelper.cs +++ b/src/Umbraco.Core/CacheHelper.cs @@ -18,10 +18,12 @@ namespace Umbraco.Core public class CacheHelper { private readonly bool _enableCache; - private readonly CacheProviderBase _staticCache; - private readonly CacheProviderBase _nullStaticCache = new NullCacheProvider(); - private readonly RuntimeCacheProviderBase _httpCache; - private readonly RuntimeCacheProviderBase _nullHttpCache = new NullCacheProvider(); + private readonly ICacheProvider _requestCache; + private readonly ICacheProvider _nullRequestCache = new NullCacheProvider(); + private readonly ICacheProvider _staticCache; + private readonly ICacheProvider _nullStaticCache = new NullCacheProvider(); + private readonly IRuntimeCacheProvider _httpCache; + private readonly IRuntimeCacheProvider _nullHttpCache = new NullCacheProvider(); public CacheHelper(System.Web.Caching.Cache cache) : this(cache, true) @@ -33,18 +35,171 @@ namespace Umbraco.Core { } - internal CacheHelper(RuntimeCacheProviderBase httpCacheProvider, bool enableCache) - : this(httpCacheProvider, new StaticCacheProvider(), enableCache) + internal CacheHelper(IRuntimeCacheProvider httpCacheProvider, bool enableCache) + : this(httpCacheProvider, new StaticCacheProvider(), new HttpRequestCacheProvider(HttpContext.Current), enableCache) { } - internal CacheHelper(RuntimeCacheProviderBase httpCacheProvider, CacheProviderBase staticCacheProvider, bool enableCache) + internal CacheHelper( + IRuntimeCacheProvider httpCacheProvider, + ICacheProvider staticCacheProvider, + ICacheProvider requestCacheProvider, + bool enableCache) { _httpCache = httpCacheProvider; _staticCache = staticCacheProvider; _enableCache = enableCache; + _requestCache = requestCacheProvider; } + #region Request cache + + /// + /// Clears the item in umbraco's request cache + /// + internal void ClearAllRequestCache() + { + if (!_enableCache) + { + _nullRequestCache.ClearAllCache(); + } + else + { + _requestCache.ClearAllCache(); + } + } + + /// + /// Clears the item in umbraco's request cache with the given key + /// + /// Key + internal void ClearRequestCacheItem(string key) + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheItem(key); + } + else + { + _requestCache.ClearCacheItem(key); + } + } + + /// + /// Clears all objects in the request cache with the System.Type name as the + /// input parameter. (using [object].GetType()) + /// + /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" + internal void ClearRequestCacheObjectTypes(string typeName) + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheObjectTypes(typeName); + } + else + { + _requestCache.ClearCacheObjectTypes(typeName); + } + } + + /// + /// Clears all objects in the request cache with the System.Type specified + /// + internal void ClearRequestCacheObjectTypes() + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheObjectTypes(); + } + else + { + _requestCache.ClearCacheObjectTypes(); + } + } + + /// + /// Clears all request cache items that starts with the key passed. + /// + /// The start of the key + internal void ClearRequestCacheByKeySearch(string keyStartsWith) + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheByKeySearch(keyStartsWith); + } + else + { + _requestCache.ClearCacheByKeySearch(keyStartsWith); + } + } + + /// + /// Clears all cache items that have a key that matches the regular expression + /// + /// + internal void ClearRequestCacheByKeyExpression(string regexString) + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheByKeyExpression(regexString); + } + else + { + _requestCache.ClearCacheByKeyExpression(regexString); + } + } + + internal IEnumerable GetRequestCacheItemsByKeySearch(string keyStartsWith) + { + if (!_enableCache) + { + return _nullRequestCache.GetCacheItemsByKeySearch(keyStartsWith); + } + else + { + return _requestCache.GetCacheItemsByKeySearch(keyStartsWith); + } + } + + /// + /// Returns a request cache item by key, does not update the cache if it isn't there. + /// + /// + /// + /// + internal TT GetRequestCacheItem(string cacheKey) + { + if (!_enableCache) + { + return _nullRequestCache.GetCacheItem(cacheKey); + } + else + { + return _requestCache.GetCacheItem(cacheKey); + } + } + + /// + /// Gets (and adds if necessary) an item from the request cache with all of the default parameters + /// + /// + /// + /// + /// + internal TT GetRequestCacheItem(string cacheKey, Func getCacheItem) + { + if (!_enableCache) + { + return _nullRequestCache.GetCacheItem(cacheKey, getCacheItem); + } + else + { + return _requestCache.GetCacheItem(cacheKey, getCacheItem); + } + } + + #endregion + #region Static cache /// @@ -200,8 +355,7 @@ namespace Umbraco.Core } #endregion - - + #region Runtime/Http Cache /// /// Clears the item in umbraco's runtime cache diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 98e9c17650..84a52c2947 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -113,10 +113,15 @@ - + - + + + + + + diff --git a/src/Umbraco.Tests/Cache/CacheHelperTests.cs b/src/Umbraco.Tests/Cache/CacheHelperTests.cs deleted file mode 100644 index afe296dd8b..0000000000 --- a/src/Umbraco.Tests/Cache/CacheHelperTests.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Web; -using System.Web.Caching; -using System.Web.UI; -using NUnit.Framework; -using Umbraco.Core; -using umbraco; - -namespace Umbraco.Tests.Cache -{ - [TestFixture] - public class CacheHelperTests - { - - private CacheHelper _helper; - - [SetUp] - public void Setup() - { - _helper = new CacheHelper(HttpRuntime.Cache); - } - - [TearDown] - public void TearDown() - { - _helper.ClearAllCache(); - } - - [Test] - public void Can_Remove_By_Type_Name() - { - var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); - var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); - var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); - var cacheContent4 = new MacroCacheContent(new LiteralControl(), "Test4"); - _helper.InsertCacheItem("Test1", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent1); - _helper.InsertCacheItem("Test2", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent2); - _helper.InsertCacheItem("Test3", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent3); - _helper.InsertCacheItem("Test4", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent4); - - Assert.AreEqual(4, HttpRuntime.Cache.Count); - - _helper.ClearCacheObjectTypes("umbraco.MacroCacheContent"); - - Assert.AreEqual(0, HttpRuntime.Cache.Count); - } - - [Test] - public void Can_Remove_By_Strong_Type() - { - var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); - var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); - var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); - var cacheContent4 = new MacroCacheContent(new LiteralControl(), "Test4"); - _helper.InsertCacheItem("Test1", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent1); - _helper.InsertCacheItem("Test2", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent2); - _helper.InsertCacheItem("Test3", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent3); - _helper.InsertCacheItem("Test4", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent4); - - Assert.AreEqual(4, HttpRuntime.Cache.Count); - - _helper.ClearCacheObjectTypes(); - - Assert.AreEqual(0, HttpRuntime.Cache.Count); - } - - [Test] - public void Can_Add_Remove_Struct_Strongly_Typed_With_Null() - { - var now = DateTime.Now; - _helper.InsertCacheItem("DateTimeTest", CacheItemPriority.Default, new TimeSpan(0, 0, 0, 0, 200), () => now); - Assert.AreEqual(now, _helper.GetCacheItem("DateTimeTest")); - Assert.AreEqual(now, _helper.GetCacheItem("DateTimeTest")); - - Thread.Sleep(300); //sleep longer than the cache expiration - - Assert.AreEqual(default(DateTime), _helper.GetCacheItem("DateTimeTest")); - Assert.AreEqual(null, _helper.GetCacheItem("DateTimeTest")); - } - - } -} diff --git a/src/Umbraco.Tests/Cache/CacheProviderTests.cs b/src/Umbraco.Tests/Cache/CacheProviderTests.cs new file mode 100644 index 0000000000..b7ed588bb8 --- /dev/null +++ b/src/Umbraco.Tests/Cache/CacheProviderTests.cs @@ -0,0 +1,63 @@ +using System.Web.UI; +using NUnit.Framework; +using Umbraco.Core.Cache; +using umbraco; + +namespace Umbraco.Tests.Cache +{ + public abstract class CacheProviderTests + { + internal abstract ICacheProvider Provider { get; } + protected abstract int GetTotalItemCount { get; } + + [SetUp] + public virtual void Setup() + { + + } + + [TearDown] + public virtual void TearDown() + { + Provider.ClearAllCache(); + } + + [Test] + public void Can_Remove_By_Type_Name() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new MacroCacheContent(new LiteralControl(), "Test4"); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Test2", () => cacheContent2); + Provider.GetCacheItem("Test3", () => cacheContent3); + Provider.GetCacheItem("Test4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearCacheObjectTypes("umbraco.MacroCacheContent"); + + Assert.AreEqual(0, GetTotalItemCount); + } + + [Test] + public void Can_Remove_By_Strong_Type() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new MacroCacheContent(new LiteralControl(), "Test4"); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Test2", () => cacheContent2); + Provider.GetCacheItem("Test3", () => cacheContent3); + Provider.GetCacheItem("Test4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearCacheObjectTypes(); + + Assert.AreEqual(0, GetTotalItemCount); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Cache/HttpRequestCacheProviderTests.cs b/src/Umbraco.Tests/Cache/HttpRequestCacheProviderTests.cs new file mode 100644 index 0000000000..dcbddbb44e --- /dev/null +++ b/src/Umbraco.Tests/Cache/HttpRequestCacheProviderTests.cs @@ -0,0 +1,52 @@ +using NUnit.Framework; +using Umbraco.Core.Cache; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Cache +{ + [TestFixture] + public class HttpRequestCacheProviderTests : CacheProviderTests + { + private HttpRequestCacheProvider _provider; + private FakeHttpContextFactory _ctx; + + public override void Setup() + { + base.Setup(); + _ctx = new FakeHttpContextFactory("http://localhost/test"); + _provider = new HttpRequestCacheProvider(() => _ctx.HttpContext); + } + + internal override ICacheProvider Provider + { + get { return _provider; } + } + + protected override int GetTotalItemCount + { + get { return _ctx.HttpContext.Items.Count; } + } + } + + [TestFixture] + public class StaticCacheProviderTests : CacheProviderTests + { + private StaticCacheProvider _provider; + + public override void Setup() + { + base.Setup(); + _provider = new StaticCacheProvider(); + } + + internal override ICacheProvider Provider + { + get { return _provider; } + } + + protected override int GetTotalItemCount + { + get { return _provider.StaticCache.Count; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs b/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs new file mode 100644 index 0000000000..9cbbc926f8 --- /dev/null +++ b/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs @@ -0,0 +1,33 @@ +using System.Web; +using NUnit.Framework; +using Umbraco.Core.Cache; + +namespace Umbraco.Tests.Cache +{ + [TestFixture] + public class HttpRuntimeCacheProviderTests : RuntimeCacheProviderTests + { + private HttpRuntimeCacheProvider _provider; + + protected override int GetTotalItemCount + { + get { return HttpRuntime.Cache.Count; } + } + + public override void Setup() + { + base.Setup(); + _provider = new HttpRuntimeCacheProvider(HttpRuntime.Cache); + } + + internal override ICacheProvider Provider + { + get { return _provider; } + } + + internal override IRuntimeCacheProvider RuntimeProvider + { + get { return _provider; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Cache/ObjectCacheProviderTests.cs b/src/Umbraco.Tests/Cache/ObjectCacheProviderTests.cs new file mode 100644 index 0000000000..e373fdda4d --- /dev/null +++ b/src/Umbraco.Tests/Cache/ObjectCacheProviderTests.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Cache; + +namespace Umbraco.Tests.Cache +{ + [TestFixture] + public class ObjectCacheProviderTests : RuntimeCacheProviderTests + { + private ObjectCacheRuntimeCacheProvider _provider; + + protected override int GetTotalItemCount + { + get { return _provider.MemoryCache.Count(); } + } + + public override void Setup() + { + base.Setup(); + _provider = new ObjectCacheRuntimeCacheProvider(); + } + + internal override ICacheProvider Provider + { + get { return _provider; } + } + + internal override IRuntimeCacheProvider RuntimeProvider + { + get { return _provider; } + } + } +} diff --git a/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs b/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs new file mode 100644 index 0000000000..03ad355f42 --- /dev/null +++ b/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using System.Web.Caching; +using NUnit.Framework; +using Umbraco.Core.Cache; + +namespace Umbraco.Tests.Cache +{ + public abstract class RuntimeCacheProviderTests : CacheProviderTests + { + + internal abstract IRuntimeCacheProvider RuntimeProvider { get; } + + + [Test] + public void Can_Add_And_Expire_Struct_Strongly_Typed_With_Null() + { + var now = DateTime.Now; + RuntimeProvider.InsertCacheItem("DateTimeTest", CacheItemPriority.Default, new TimeSpan(0, 0, 0, 0, 200), () => now); + Assert.AreEqual(now, Provider.GetCacheItem("DateTimeTest")); + Assert.AreEqual(now, Provider.GetCacheItem("DateTimeTest")); + + Thread.Sleep(300); //sleep longer than the cache expiration + + Assert.AreEqual(default(DateTime), Provider.GetCacheItem("DateTimeTest")); + Assert.AreEqual(null, Provider.GetCacheItem("DateTimeTest")); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f36ac5008a..c04eb1a868 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -101,6 +101,7 @@ ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll + @@ -151,7 +152,11 @@ - + + + + + From a42b660648da4b8f1066ae972e43f39f544d953b Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 13:19:10 +1100 Subject: [PATCH 3/8] Backporting the cachehelper and associated cache classes. --- src/Umbraco.Core/ApplicationContext.cs | 292 +++++----- .../Cache/CacheProviderExtensions.cs | 64 +++ .../Cache/DictionaryCacheProdiverBase.cs | 96 ++-- .../Cache/HttpRequestCacheProvider.cs | 9 +- .../Cache/HttpRuntimeCacheProvider.cs | 240 +++------ src/Umbraco.Core/Cache/ICacheProvider.cs | 12 +- .../Cache/IRuntimeCacheProvider.cs | 31 +- src/Umbraco.Core/Cache/NullCacheProvider.cs | 49 +- .../Cache/ObjectCacheRuntimeCacheProvider.cs | 177 +++--- src/Umbraco.Core/Cache/StaticCacheProvider.cs | 62 +-- src/Umbraco.Core/CacheHelper.cs | 508 ++++++------------ src/Umbraco.Core/CoreBootManager.cs | 29 +- .../PublishedContent/PublishedContentType.cs | 14 +- src/Umbraco.Core/Services/ServiceContext.cs | 93 +++- .../Standalone/ServiceContextManager.cs | 10 +- src/Umbraco.Core/Umbraco.Core.csproj | 9 +- src/Umbraco.Tests/BusinessLogic/BaseTest.cs | 2 +- src/Umbraco.Tests/Cache/CacheProviderTests.cs | 128 ++++- .../Cache/RuntimeCacheProviderTests.cs | 2 +- src/Umbraco.Tests/Macros/MacroTests.cs | 13 +- src/Umbraco.Tests/MockTests.cs | 28 +- src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs | 2 +- .../Persistence/BaseTableByTableTest.cs | 16 +- .../Persistence/DatabaseContextTests.cs | 22 +- .../PublishedContentMoreTests.cs | 2 +- .../PublishedContent/PublishedContentTests.cs | 2 +- .../Services/ThreadSafetyServiceTest.cs | 41 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 38 +- .../TestHelpers/BaseDatabaseTest.cs | 47 +- .../TestHelpers/BaseUmbracoApplicationTest.cs | 18 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 14 +- .../Standalone/ServiceContextManager.cs | 13 +- 32 files changed, 1034 insertions(+), 1049 deletions(-) create mode 100644 src/Umbraco.Core/Cache/CacheProviderExtensions.cs diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 33748ae46b..a3f069ac7a 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -12,7 +12,7 @@ using Umbraco.Core.Services; namespace Umbraco.Core { - /// + /// /// the Umbraco Application context /// /// @@ -20,61 +20,45 @@ namespace Umbraco.Core /// public class ApplicationContext : IDisposable { - /// - /// Constructor - /// - internal ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext) - : this(dbContext, serviceContext, true) - { - - } /// /// Constructor /// /// /// - /// - public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache) - : this(enableCache) + /// + public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache) { + if (dbContext == null) throw new ArgumentNullException("dbContext"); + if (serviceContext == null) throw new ArgumentNullException("serviceContext"); + if (cache == null) throw new ArgumentNullException("cache"); _databaseContext = dbContext; - _services = serviceContext; + _services = serviceContext; + ApplicationCache = cache; } - /// - /// Empty constructor normally reserved for unit tests when a DatabaseContext or a ServiceContext is not - /// necessarily required or needs to be set after construction. - /// - internal ApplicationContext() : this(true) - { - } - /// - /// Constructor used to specify if we will enable application cache or not + /// Creates a basic app context /// - /// - public ApplicationContext(bool enableCache) + /// + public ApplicationContext(CacheHelper cache) { - //create a new application cache from the HttpRuntime.Cache - ApplicationCache = HttpRuntime.Cache == null - ? new CacheHelper(new System.Web.Caching.Cache(), enableCache) - : new CacheHelper(HttpRuntime.Cache, enableCache); + ApplicationCache = cache; } - /// - /// A method used to set and/or ensure that a global ApplicationContext singleton is created. - /// - /// - /// The instance to set on the global application singleton - /// - /// If set to true and the singleton is already set, it will be replaced - /// - /// - /// This is NOT thread safe - /// - public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext) - { + /// + /// A method used to set and/or ensure that a global ApplicationContext singleton is created. + /// + /// + /// The instance to set on the global application singleton + /// + /// If set to true and the singleton is already set, it will be replaced + /// + /// + /// This is NOT thread safe + /// + public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext) + { if (ApplicationContext.Current != null) { if (!replaceContext) @@ -82,56 +66,56 @@ namespace Umbraco.Core } ApplicationContext.Current = appContext; return ApplicationContext.Current; - } + } - /// - /// A method used to create and ensure that a global ApplicationContext singleton is created. - /// - /// - /// - /// If set to true will replace the current singleton instance - This should only be used for unit tests or on app - /// startup if for some reason the boot manager is not the umbraco boot manager. - /// - /// - /// - /// - /// - /// This is NOT thread safe - /// - public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache, bool replaceContext) + /// + /// A method used to create and ensure that a global ApplicationContext singleton is created. + /// + /// + /// + /// If set to true will replace the current singleton instance - This should only be used for unit tests or on app + /// startup if for some reason the boot manager is not the umbraco boot manager. + /// + /// + /// + /// + /// + /// This is NOT thread safe + /// + public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext) { if (ApplicationContext.Current != null) { if (!replaceContext) return ApplicationContext.Current; } - var ctx = new ApplicationContext(dbContext, serviceContext, enableCache); + var ctx = new ApplicationContext(dbContext, serviceContext, cache); ApplicationContext.Current = ctx; return ApplicationContext.Current; } - /// - /// Singleton accessor - /// - public static ApplicationContext Current { get; internal set; } + /// + /// Singleton accessor + /// + public static ApplicationContext Current { get; internal set; } - /// - /// Returns the application wide cache accessor - /// - /// - /// Any caching that is done in the application (app wide) should be done through this property - /// - public CacheHelper ApplicationCache { get; private set; } + /// + /// Returns the application wide cache accessor + /// + /// + /// Any caching that is done in the application (app wide) should be done through this property + /// + public CacheHelper ApplicationCache { get; private set; } - // IsReady is set to true by the boot manager once it has successfully booted + // IsReady is set to true by the boot manager once it has successfully booted // note - the original umbraco module checks on content.Instance in umbraco.dll // now, the boot task that setup the content store ensures that it is ready bool _isReady = false; - readonly System.Threading.ManualResetEventSlim _isReadyEvent = new System.Threading.ManualResetEventSlim(false); - private DatabaseContext _databaseContext; - private ServiceContext _services; + readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false); + private DatabaseContext _databaseContext; + private ServiceContext _services; - public bool IsReady + public bool IsReady { get { @@ -141,14 +125,14 @@ namespace Umbraco.Core { AssertIsNotReady(); _isReady = value; - _isReadyEvent.Set(); + _isReadyEvent.Set(); } } - public bool WaitForReady(int timeout) - { - return _isReadyEvent.WaitHandle.WaitOne(timeout); - } + public bool WaitForReady(int timeout) + { + return _isReadyEvent.WaitHandle.WaitOne(timeout); + } // notes @@ -157,14 +141,14 @@ namespace Umbraco.Core // the system is configured if they match // if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus // - // then there is Application["umbracoNeedConfiguration"] which makes no sense... getting rid of it... + // then there is Application["umbracoNeedConfiguration"] which makes no sense... getting rid of it... SD: I have actually remove that now! // public bool IsConfigured { // todo - we should not do this - ok for now get { - return Configured; + return Configured; } } @@ -180,45 +164,45 @@ namespace Umbraco.Core /// internal string OriginalRequestUrl { get; set; } - private bool Configured - { - get - { - try - { - string configStatus = ConfigurationStatus; - string currentVersion = UmbracoVersion.Current.ToString(3); + private bool Configured + { + get + { + try + { + string configStatus = ConfigurationStatus; + string currentVersion = UmbracoVersion.Current.ToString(3); - if (currentVersion != configStatus) - { - LogHelper.Info("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'"); - } - + if (currentVersion != configStatus) + { + LogHelper.Info("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'"); + } - return (configStatus == currentVersion); - } - catch - { - return false; - } - } - } - private string ConfigurationStatus - { - get - { - try - { - return ConfigurationManager.AppSettings["umbracoConfigurationStatus"]; - } - catch - { - return String.Empty; - } - } - } + return (configStatus == currentVersion); + } + catch + { + return false; + } + } + } + + private string ConfigurationStatus + { + get + { + try + { + return ConfigurationManager.AppSettings["umbracoConfigurationStatus"]; + } + catch + { + return String.Empty; + } + } + } private void AssertIsReady() { @@ -232,39 +216,39 @@ namespace Umbraco.Core throw new Exception("ApplicationContext has already been initialized."); } - /// - /// Gets the current DatabaseContext - /// - /// - /// Internal set is generally only used for unit tests - /// - public DatabaseContext DatabaseContext - { - get - { - if (_databaseContext == null) - throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext"); - return _databaseContext; - } - internal set { _databaseContext = value; } - } - - /// - /// Gets the current ServiceContext - /// - /// - /// Internal set is generally only used for unit tests - /// - public ServiceContext Services - { - get - { - if (_services == null) - throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext"); - return _services; - } - internal set { _services = value; } - } + /// + /// Gets the current DatabaseContext + /// + /// + /// Internal set is generally only used for unit tests + /// + public DatabaseContext DatabaseContext + { + get + { + if (_databaseContext == null) + throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext"); + return _databaseContext; + } + internal set { _databaseContext = value; } + } + + /// + /// Gets the current ServiceContext + /// + /// + /// Internal set is generally only used for unit tests + /// + public ServiceContext Services + { + get + { + if (_services == null) + throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext"); + return _services; + } + internal set { _services = value; } + } private volatile bool _disposed; @@ -290,26 +274,26 @@ namespace Umbraco.Core //clear the cache if (ApplicationCache != null) { - ApplicationCache.ClearAllCache(); + ApplicationCache.ClearAllCache(); } //reset all resolvers ResolverCollection.ResetAll(); //reset resolution itself (though this should be taken care of by resetting any of the resolvers above) Resolution.Reset(); - + //reset the instance objects this.ApplicationCache = null; if (_databaseContext != null) //need to check the internal field here { if (DatabaseContext.IsDatabaseConfigured) { - DatabaseContext.Database.Dispose(); - } + DatabaseContext.Database.Dispose(); + } } this.DatabaseContext = null; this.Services = null; this._isReady = false; //set the internal field - + // Indicate that the instance has been disposed. _disposed = true; } diff --git a/src/Umbraco.Core/Cache/CacheProviderExtensions.cs b/src/Umbraco.Core/Cache/CacheProviderExtensions.cs new file mode 100644 index 0000000000..7b621d013f --- /dev/null +++ b/src/Umbraco.Core/Cache/CacheProviderExtensions.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Caching; + +namespace Umbraco.Core.Cache +{ + /// + /// Extensions for strongly typed access + /// + internal static class CacheProviderExtensions + { + public static T GetCacheItem(this IRuntimeCacheProvider provider, + string cacheKey, + Func getCacheItem, + TimeSpan? timeout, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null) + { + var result = provider.GetCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles); + return result == null ? default(T) : result.TryConvertTo().Result; + } + + public static void InsertCacheItem(this IRuntimeCacheProvider provider, + string cacheKey, + Func getCacheItem, + TimeSpan? timeout = null, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null) + { + provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles); + } + + public static IEnumerable GetCacheItemsByKeySearch(this ICacheProvider provider, string keyStartsWith) + { + var result = provider.GetCacheItemsByKeySearch(keyStartsWith); + return result.Select(x => x.TryConvertTo().Result); + } + + public static T GetCacheItem(this ICacheProvider provider, string cacheKey) + { + var result = provider.GetCacheItem(cacheKey); + if (result == null) + { + return default(T); + } + return result.TryConvertTo().Result; + } + + public static T GetCacheItem(this ICacheProvider provider, string cacheKey, Func getCacheItem) + { + var result = provider.GetCacheItem(cacheKey, () => getCacheItem()); + if (result == null) + { + return default(T); + } + return result.TryConvertTo().Result; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs b/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs index adc02f9104..c1a4d17592 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs @@ -2,12 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using System.Threading; namespace Umbraco.Core.Cache { internal abstract class DictionaryCacheProdiverBase : ICacheProvider { - private static readonly object Locker = new object(); + protected static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); protected abstract DictionaryCacheWrapper DictionaryCache { get; } /// @@ -18,7 +19,7 @@ namespace Umbraco.Core.Cache /// public virtual void ClearAllCache() { - lock (Locker) + using (new WriteLock(Locker)) { var keysToRemove = DictionaryCache.Cast() .Select(item => new DictionaryItemWrapper(item)) @@ -39,7 +40,7 @@ namespace Umbraco.Core.Cache /// Key public virtual void ClearCacheItem(string key) { - lock (Locker) + using (new WriteLock(Locker)) { if (DictionaryCache[GetCacheKey(key)] == null) return; DictionaryCache.Remove(GetCacheKey(key)); ; @@ -53,39 +54,66 @@ namespace Umbraco.Core.Cache /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" public virtual void ClearCacheObjectTypes(string typeName) { - lock (Locker) + using (new WriteLock(Locker)) { - var keysToRemove = DictionaryCache.Cast() - .Select(item => new DictionaryItemWrapper(item)) - .Where(c => DictionaryCache[c.Key.ToString()] != null && DictionaryCache[c.Key.ToString()].GetType().ToString().InvariantEquals(typeName)) - .Select(c => c.Key) - .ToList(); + var keysToRemove = DictionaryCache + .Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => + { + var k = c.Key.ToString(); + var v = DictionaryCache[k]; + return v != null && v.GetType().ToString().InvariantEquals(typeName); + }) + .Select(c => c.Key) + .ToList(); foreach (var k in keysToRemove) - { DictionaryCache.Remove(k); - } } } - /// - /// Clears all objects in the System.Web.Cache with the System.Type specified - /// public virtual void ClearCacheObjectTypes() - { - lock (Locker) + { + using (new WriteLock(Locker)) { - var keysToRemove = DictionaryCache.Cast() - .Select(item => new DictionaryItemWrapper(item)) - .Where(c => DictionaryCache[c.Key.ToString()] != null && DictionaryCache[c.Key.ToString()].GetType() == typeof (T)) - .Select(c => c.Key) - .ToList(); + var typeOfT = typeof(T); + var keysToRemove = DictionaryCache + .Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => + { + var k = c.Key.ToString(); + var v = DictionaryCache[k]; + return v != null && v.GetType() == typeOfT; + }) + .Select(c => c.Key) + .ToList(); foreach (var k in keysToRemove) - { - DictionaryCache.Remove(k); - } - + DictionaryCache.Remove(k); + } + } + + public virtual void ClearCacheObjectTypes(Func predicate) + { + using (new WriteLock(Locker)) + { + var typeOfT = typeof(T); + var keysToRemove = DictionaryCache + .Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => + { + var k = c.Key.ToString(); + var v = DictionaryCache[k]; + return v != null && v.GetType() == typeOfT && predicate(k, (T)v); + }) + .Select(c => c.Key) + .ToList(); + + foreach (var k in keysToRemove) + DictionaryCache.Remove(k); } } @@ -134,35 +162,27 @@ namespace Umbraco.Core.Cache } } - public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { return (from object item in DictionaryCache select new DictionaryItemWrapper(item) into c where c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)) - select c.Value.TryConvertTo() - into converted - where converted.Success - select converted.Result).ToList(); + select c.Value).ToList(); } /// /// Returns a cache item by key, does not update the cache if it isn't there. /// - /// /// /// - public virtual TT GetCacheItem(string cacheKey) + public virtual object GetCacheItem(string cacheKey) { var result = DictionaryCache.Get(GetCacheKey(cacheKey)); - if (result == null) - { - return default(TT); - } - return result.TryConvertTo().Result; + return result; } - public abstract T GetCacheItem(string cacheKey, Func getCacheItem); + public abstract object GetCacheItem(string cacheKey, Func getCacheItem); /// /// We prefix all cache keys with this so that we know which ones this class has created when diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs index 134b780f2b..26c6dc0af8 100644 --- a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs @@ -36,15 +36,12 @@ namespace Umbraco.Core.Cache } } - public override T GetCacheItem(string cacheKey, Func getCacheItem) + public override object GetCacheItem(string cacheKey, Func getCacheItem) { var ctx = _context(); var ck = GetCacheKey(cacheKey); - if (ctx.Items[ck] == null) - { - ctx.Items[ck] = getCacheItem(); - } - return (T)ctx.Items[ck]; + return ctx.Items[ck] ?? (ctx.Items[ck] = getCacheItem()); } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index 02b4aa46d2..9838f8d1e6 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -1,4 +1,7 @@ using System; +using System.Collections; +using System.Linq; +using System.Threading; using System.Web; using System.Web.Caching; using Umbraco.Core.Logging; @@ -13,7 +16,6 @@ namespace Umbraco.Core.Cache { private readonly System.Web.Caching.Cache _cache; private readonly DictionaryCacheWrapper _wrapper; - private static readonly object Locker = new object(); public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache) { @@ -22,6 +24,11 @@ namespace Umbraco.Core.Cache } protected override DictionaryCacheWrapper DictionaryCache + { + get { return _wrapper; } + } + + /// /// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate /// public override void ClearCacheObjectTypes(Func predicate) @@ -48,207 +55,94 @@ namespace Umbraco.Core.Cache } } - /// - { - get { return _wrapper; } - } - /// /// Gets (and adds if necessary) an item from the cache with all of the default parameters /// - /// /// /// /// - public override T GetCacheItem(string cacheKey, Func getCacheItem) + public override object GetCacheItem(string cacheKey, Func getCacheItem) { - return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem, Locker); + return GetCacheItem(cacheKey, getCacheItem, null, dependentFiles: null); } /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) + /// This overload is here for legacy purposes /// - /// /// - /// This will set an absolute expiration from now until the timeout /// - /// - public virtual TT GetCacheItem(string cacheKey, - TimeSpan? timeout, Func getCacheItem) - { - return GetCacheItem(cacheKey, null, timeout, getCacheItem); - } - - /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - /// - public virtual TT GetCacheItem(string cacheKey, - CacheItemRemovedCallback refreshAction, TimeSpan? timeout, - Func getCacheItem) - { - return GetCacheItem(cacheKey, CacheItemPriority.Normal, refreshAction, timeout, getCacheItem); - } - - /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) - /// - /// - /// + /// + /// /// - /// - /// This will set an absolute expiration from now until the timeout - /// + /// + /// /// - public virtual TT GetCacheItem(string cacheKey, - CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, - Func getCacheItem) - { - return GetCacheItem(cacheKey, priority, refreshAction, null, timeout, getCacheItem); - } - - /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) - /// - /// - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - /// - public virtual TT GetCacheItem(string cacheKey, - CacheItemPriority priority, - CacheItemRemovedCallback refreshAction, - CacheDependency cacheDependency, - TimeSpan? timeout, - Func getCacheItem) - { - return GetCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem, Locker); - } - - /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) - /// - /// - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - /// - /// - private TT GetCacheItem(string cacheKey, - CacheItemPriority priority, CacheItemRemovedCallback refreshAction, - CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem, object syncLock) + internal object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null) { cacheKey = GetCacheKey(cacheKey); - var result = DictionaryCache.Get(cacheKey); - if (result == null) + using (var lck = new UpgradeableReadLock(Locker)) { - lock (syncLock) + var result = DictionaryCache.Get(cacheKey); + if (result == null) { - result = DictionaryCache.Get(cacheKey); - if (result == null) - { - result = getCacheItem(); - if (result != null) - { - //we use Insert instead of add if for some crazy reason there is now a cache with the cache key in there, it will just overwrite it. - _cache.Insert(cacheKey, result, cacheDependency, - timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value), - TimeSpan.Zero, priority, refreshAction); - } + lck.UpgradeToWriteLock(); + + result = getCacheItem(); + if (result != null) + { + var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); + var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); + + _cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback); } + } + return result; } - return result.TryConvertTo().Result; } - /// - /// Inserts an item into the cache, if it already exists in the cache it will be replaced - /// - /// - /// - /// - /// - public virtual void InsertCacheItem(string cacheKey, - CacheItemPriority priority, - Func getCacheItem) + public object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { - InsertCacheItem(cacheKey, priority, null, null, null, getCacheItem); - } - - /// - /// Inserts an item into the cache, if it already exists in the cache it will be replaced - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - public virtual void InsertCacheItem(string cacheKey, - CacheItemPriority priority, - TimeSpan? timeout, - Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, null, timeout, getCacheItem); - } - - /// - /// Inserts an item into the cache, if it already exists in the cache it will be replaced - /// - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - public virtual void InsertCacheItem(string cacheKey, - CacheItemPriority priority, - CacheDependency cacheDependency, - TimeSpan? timeout, - Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, cacheDependency, timeout, getCacheItem); - } - - /// - /// Inserts an item into the cache, if it already exists in the cache it will be replaced - /// - /// - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - public virtual void InsertCacheItem(string cacheKey, - CacheItemPriority priority, - CacheItemRemovedCallback refreshAction, - CacheDependency cacheDependency, - TimeSpan? timeout, - Func getCacheItem) - { - object result = getCacheItem(); - if (result != null) + CacheDependency dependency = null; + if (dependentFiles != null && dependentFiles.Any()) { - cacheKey = GetCacheKey(cacheKey); - - //we use Insert instead of add if for some crazy reason there is now a cache with the cache key in there, it will just overwrite it. - _cache.Insert(cacheKey, result, cacheDependency, - timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value), - TimeSpan.Zero, priority, refreshAction); + dependency = new CacheDependency(dependentFiles); } + return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency); } + /// + /// This overload is here for legacy purposes + /// + /// + /// + /// + /// + /// + /// + /// + internal void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null) + { + var result = getCacheItem(); + if (result == null) return; + + cacheKey = GetCacheKey(cacheKey); + + var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); + var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); + + _cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback); + } + + public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) + { + CacheDependency dependency = null; + if (dependentFiles != null && dependentFiles.Any()) + { + dependency = new CacheDependency(dependentFiles); + } + InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/ICacheProvider.cs b/src/Umbraco.Core/Cache/ICacheProvider.cs index ab70cf63c0..e9bdde3ff2 100644 --- a/src/Umbraco.Core/Cache/ICacheProvider.cs +++ b/src/Umbraco.Core/Cache/ICacheProvider.cs @@ -6,19 +6,17 @@ namespace Umbraco.Core.Cache /// /// An abstract class for implementing a basic cache provider /// - /// - /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, ETC... - /// - internal interface ICacheProvider + public interface ICacheProvider { void ClearAllCache(); void ClearCacheItem(string key); void ClearCacheObjectTypes(string typeName); void ClearCacheObjectTypes(); + void ClearCacheObjectTypes(Func predicate); void ClearCacheByKeySearch(string keyStartsWith); void ClearCacheByKeyExpression(string regexString); - IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); - T GetCacheItem(string cacheKey); - T GetCacheItem(string cacheKey, Func getCacheItem); + IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); + object GetCacheItem(string cacheKey); + object GetCacheItem(string cacheKey, Func getCacheItem); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs index 8dd1d31af0..b0227ca114 100644 --- a/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs @@ -1,6 +1,8 @@ using System; +using System.Runtime.Caching; using System.Text; using System.Web.Caching; +using CacheItemPriority = System.Web.Caching.CacheItemPriority; namespace Umbraco.Core.Cache { @@ -8,17 +10,26 @@ namespace Umbraco.Core.Cache /// An abstract class for implementing a runtime cache provider /// /// - /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, REQUEST CACHE, ETC... /// - internal interface IRuntimeCacheProvider : ICacheProvider + public interface IRuntimeCacheProvider : ICacheProvider { - T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem); - T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem); - void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem); - void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); + object GetCacheItem( + string cacheKey, + Func getCacheItem, + TimeSpan? timeout, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null); + + void InsertCacheItem( + string cacheKey, + Func getCacheItem, + TimeSpan? timeout = null, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null); + } } diff --git a/src/Umbraco.Core/Cache/NullCacheProvider.cs b/src/Umbraco.Core/Cache/NullCacheProvider.cs index ff993bdbc0..ba88637661 100644 --- a/src/Umbraco.Core/Cache/NullCacheProvider.cs +++ b/src/Umbraco.Core/Cache/NullCacheProvider.cs @@ -23,11 +23,14 @@ namespace Umbraco.Core.Cache { } - public override void ClearCacheObjectTypes(Func predicate) + public virtual void ClearCacheObjectTypes(Func predicate) { } - public override void ClearCacheByKeySearch(string keyStartsWith) + + + + public virtual void ClearCacheByKeySearch(string keyStartsWith) { } @@ -35,55 +38,29 @@ namespace Umbraco.Core.Cache { } - public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { - return Enumerable.Empty(); + return Enumerable.Empty(); } - public virtual T GetCacheItem(string cacheKey) + public virtual object GetCacheItem(string cacheKey) { - return default(T); + return default(object); } - public virtual T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual object GetCacheItem(string cacheKey, Func getCacheItem) { return getCacheItem(); } - public virtual T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) + public object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { return getCacheItem(); } - public virtual T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) - { - return getCacheItem(); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) - { - return getCacheItem(); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) - { - return getCacheItem(); - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) - { - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) - { - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) - { - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs index c9a37d830d..aa6a5bbb22 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Cache /// internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider { - private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim(); + private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); internal ObjectCache MemoryCache; public ObjectCacheRuntimeCacheProvider() @@ -26,7 +26,7 @@ namespace Umbraco.Core.Cache public virtual void ClearAllCache() { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { MemoryCache.DisposeIfDisposable(); MemoryCache = new MemoryCache("in-memory"); @@ -35,7 +35,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheItem(string key) { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { if (MemoryCache[key] == null) return; MemoryCache.Remove(key); @@ -44,31 +44,48 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheObjectTypes(string typeName) { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { - var keysToRemove = (from c in MemoryCache where c.Value.GetType().ToString().InvariantEquals(typeName) select c.Key).ToList(); + var keysToRemove = MemoryCache + .Where(c => c.Value != null && c.Value.GetType().ToString().InvariantEquals(typeName)) + .Select(c => c.Key) + .ToArray(); foreach (var k in keysToRemove) - { MemoryCache.Remove(k); - } } } public virtual void ClearCacheObjectTypes() { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { - var keysToRemove = (from c in MemoryCache where c.Value.GetType() == typeof (T) select c.Key).ToList(); + var typeOfT = typeof (T); + var keysToRemove = MemoryCache + .Where(c => c.Value != null && c.Value.GetType() == typeOfT) + .Select(c => c.Key) + .ToArray(); + foreach (var k in keysToRemove) + MemoryCache.Remove(k); + } + } + + public virtual void ClearCacheObjectTypes(Func predicate) + { + using (new WriteLock(Locker)) + { + var typeOfT = typeof(T); + var keysToRemove = MemoryCache + .Where(c => c.Value != null && c.Value.GetType() == typeOfT && predicate(c.Key, (T)c.Value)) + .Select(c => c.Key) + .ToArray(); foreach (var k in keysToRemove) - { MemoryCache.Remove(k); - } } } public virtual void ClearCacheByKeySearch(string keyStartsWith) { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList(); foreach (var k in keysToRemove) @@ -80,7 +97,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheByKeyExpression(string regexString) { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList(); foreach (var k in keysToRemove) @@ -90,49 +107,34 @@ namespace Umbraco.Core.Cache } } - public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { return (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) - select c.Value.TryConvertTo() - into attempt - where attempt.Success - select attempt.Result).ToList(); + select c.Value).ToList(); } - public virtual T GetCacheItem(string cacheKey) + public virtual object GetCacheItem(string cacheKey) { var result = MemoryCache.Get(cacheKey); - if (result == null) - { - return default(T); - } - return result.TryConvertTo().Result; + return result; } - public virtual T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual object GetCacheItem(string cacheKey, Func getCacheItem) { - return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem); + return GetCacheItem(cacheKey, getCacheItem, null); } - public virtual T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) + public object GetCacheItem( + string cacheKey, + Func getCacheItem, + TimeSpan? timeout, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null) { - return GetCacheItem(cacheKey, null, timeout, getCacheItem); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) - { - return GetCacheItem(cacheKey, CacheItemPriority.Normal, refreshAction, timeout, getCacheItem); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) - { - return GetCacheItem(cacheKey, priority, refreshAction, null, timeout, getCacheItem); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) - { - using (var lck = new UpgradeableReadLock(ClearLock)) + using (var lck = new UpgradeableReadLock(Locker)) { var result = MemoryCache.Get(cacheKey); if (result == null) @@ -142,57 +144,70 @@ namespace Umbraco.Core.Cache result = getCacheItem(); if (result != null) { - var policy = new CacheItemPolicy - { - AbsoluteExpiration = timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value), - SlidingExpiration = TimeSpan.Zero - }; - - //TODO: CUrrently we cannot implement this in this provider, we'll have to change the underlying interface - // to accept an array of files instead of CacheDependency. - //policy.ChangeMonitors.Add(new HostFileChangeMonitor(cacheDependency.)); - + var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); MemoryCache.Set(cacheKey, result, policy); } } - return result.TryConvertTo().Result; + return result; } } - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, null, null, getCacheItem); - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, null, timeout, getCacheItem); - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, cacheDependency, timeout, getCacheItem); - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { object result = getCacheItem(); if (result != null) { - - var policy = new CacheItemPolicy - { - AbsoluteExpiration = timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value), - SlidingExpiration = TimeSpan.Zero - }; - - //TODO: CUrrently we cannot implement this in this provider, we'll have to change the underlying interface - // to accept an array of files instead of CacheDependency. - //policy.ChangeMonitors.Add(new HostFileChangeMonitor(cacheDependency.)); - + var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); MemoryCache.Set(cacheKey, result, policy); } } + + private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) + { + var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); + var sliding = isSliding == false ? ObjectCache.NoSlidingExpiration : (timeout ?? ObjectCache.NoSlidingExpiration); + + var policy = new CacheItemPolicy + { + AbsoluteExpiration = absolute, + SlidingExpiration = sliding + }; + + if (dependentFiles != null && dependentFiles.Any()) + { + policy.ChangeMonitors.Add(new HostFileChangeMonitor(dependentFiles.ToList())); + } + + if (removedCallback != null) + { + policy.RemovedCallback = arguments => + { + //convert the reason + var reason = CacheItemRemovedReason.Removed; + switch (arguments.RemovedReason) + { + case CacheEntryRemovedReason.Removed: + reason = CacheItemRemovedReason.Removed; + break; + case CacheEntryRemovedReason.Expired: + reason = CacheItemRemovedReason.Expired; + break; + case CacheEntryRemovedReason.Evicted: + reason = CacheItemRemovedReason.Underused; + break; + case CacheEntryRemovedReason.ChangeMonitorChanged: + reason = CacheItemRemovedReason.Expired; + break; + case CacheEntryRemovedReason.CacheSpecificEviction: + reason = CacheItemRemovedReason.Underused; + break; + } + //call the callback + removedCallback(arguments.CacheItem.Key, arguments.CacheItem.Value, reason); + }; + } + return policy; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs index 67c0527393..cd58fc3dde 100644 --- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs +++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs @@ -27,75 +27,47 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheObjectTypes(string typeName) { - foreach (var key in StaticCache.Keys) - { - if (StaticCache[key] != null - && StaticCache[key].GetType().ToString().InvariantEquals(typeName)) - { - object val; - StaticCache.TryRemove(key, out val); - } - } + StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName)); } public virtual void ClearCacheObjectTypes() { - foreach (var key in StaticCache.Keys) - { - if (StaticCache[key] != null - && StaticCache[key].GetType() == typeof(T)) - { - object val; - StaticCache.TryRemove(key, out val); - } - } + var typeOfT = typeof(T); + StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT); + } + + public virtual void ClearCacheObjectTypes(Func predicate) + { + var typeOfT = typeof(T); + StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value)); } public virtual void ClearCacheByKeySearch(string keyStartsWith) { - foreach (var key in StaticCache.Keys) - { - if (key.InvariantStartsWith(keyStartsWith)) - { - ClearCacheItem(key); - } - } + StaticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)); } public virtual void ClearCacheByKeyExpression(string regexString) { - foreach (var key in StaticCache.Keys) - { - if (Regex.IsMatch(key, regexString)) - { - ClearCacheItem(key); - } - } + StaticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString)); } - public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { return (from KeyValuePair c in StaticCache where c.Key.InvariantStartsWith(keyStartsWith) - select c.Value.TryConvertTo() - into attempt - where attempt.Success - select attempt.Result).ToList(); + select c.Value).ToList(); } - public virtual T GetCacheItem(string cacheKey) + public virtual object GetCacheItem(string cacheKey) { var result = StaticCache[cacheKey]; - if (result == null) - { - return default(T); - } - return result.TryConvertTo().Result; + return result; } - public virtual T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual object GetCacheItem(string cacheKey, Func getCacheItem) { - return (T)StaticCache.GetOrAdd(cacheKey, getCacheItem()); + return StaticCache.GetOrAdd(cacheKey, key => getCacheItem()); } } diff --git a/src/Umbraco.Core/CacheHelper.cs b/src/Umbraco.Core/CacheHelper.cs index 7ec76f165c..539ac796b7 100644 --- a/src/Umbraco.Core/CacheHelper.cs +++ b/src/Umbraco.Core/CacheHelper.cs @@ -12,357 +12,130 @@ using Umbraco.Core.Logging; namespace Umbraco.Core { - /// - /// Class that is exposed by the ApplicationContext for application wide caching purposes - /// + /// + /// Class that is exposed by the ApplicationContext for application wide caching purposes + /// public class CacheHelper - { - private readonly bool _enableCache; - private readonly ICacheProvider _requestCache; + { + private readonly bool _enableCache; + private readonly ICacheProvider _requestCache; private readonly ICacheProvider _nullRequestCache = new NullCacheProvider(); private readonly ICacheProvider _staticCache; private readonly ICacheProvider _nullStaticCache = new NullCacheProvider(); private readonly IRuntimeCacheProvider _httpCache; private readonly IRuntimeCacheProvider _nullHttpCache = new NullCacheProvider(); - public CacheHelper(System.Web.Caching.Cache cache) - : this(cache, true) - { - } + /// + /// Creates a cache helper with disabled caches + /// + /// + /// + /// Good for unit testing + /// + public static CacheHelper CreateDisabledCacheHelper() + { + return new CacheHelper(null, null, null, false); + } - internal CacheHelper(System.Web.Caching.Cache cache, bool enableCache) - : this(new HttpRuntimeCacheProvider(cache), enableCache) - { - } - - internal CacheHelper(IRuntimeCacheProvider httpCacheProvider, bool enableCache) - : this(httpCacheProvider, new StaticCacheProvider(), new HttpRequestCacheProvider(HttpContext.Current), enableCache) + /// + /// Initializes a new instance for use in the web + /// + public CacheHelper() + : this( + new HttpRuntimeCacheProvider(HttpRuntime.Cache), + new StaticCacheProvider(), + new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current))) { } - internal CacheHelper( - IRuntimeCacheProvider httpCacheProvider, - ICacheProvider staticCacheProvider, - ICacheProvider requestCacheProvider, + /// + /// Initializes a new instance for use in the web + /// + /// + public CacheHelper(System.Web.Caching.Cache cache) + : this( + new HttpRuntimeCacheProvider(cache), + new StaticCacheProvider(), + new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current))) + { + } + + /// + /// Initializes a new instance based on the provided providers + /// + /// + /// + /// + public CacheHelper( + IRuntimeCacheProvider httpCacheProvider, + ICacheProvider staticCacheProvider, + ICacheProvider requestCacheProvider) + : this(httpCacheProvider, staticCacheProvider, requestCacheProvider, true) + { + } + + /// + /// Private ctor used for creating a disabled cache helper + /// + /// + /// + /// + /// + private CacheHelper( + IRuntimeCacheProvider httpCacheProvider, + ICacheProvider staticCacheProvider, + ICacheProvider requestCacheProvider, bool enableCache) { - _httpCache = httpCacheProvider; - _staticCache = staticCacheProvider; + if (enableCache) + { + _httpCache = httpCacheProvider; + _staticCache = staticCacheProvider; + _requestCache = requestCacheProvider; + } + else + { + _httpCache = null; + _staticCache = null; + _requestCache = null; + } + _enableCache = enableCache; - _requestCache = requestCacheProvider; - } - - #region Request cache - - /// - /// Clears the item in umbraco's request cache - /// - internal void ClearAllRequestCache() - { - if (!_enableCache) - { - _nullRequestCache.ClearAllCache(); - } - else - { - _requestCache.ClearAllCache(); - } } /// - /// Clears the item in umbraco's request cache with the given key + /// Returns the current Request cache /// - /// Key - internal void ClearRequestCacheItem(string key) + public ICacheProvider RequestCache { - if (!_enableCache) - { - _nullRequestCache.ClearCacheItem(key); - } - else - { - _requestCache.ClearCacheItem(key); - } + get { return _enableCache ? _requestCache : _nullRequestCache; } } /// - /// Clears all objects in the request cache with the System.Type name as the - /// input parameter. (using [object].GetType()) + /// Returns the current Runtime cache /// - /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" - internal void ClearRequestCacheObjectTypes(string typeName) + public ICacheProvider StaticCache { - if (!_enableCache) - { - _nullRequestCache.ClearCacheObjectTypes(typeName); - } - else - { - _requestCache.ClearCacheObjectTypes(typeName); - } + get { return _enableCache ? _staticCache : _nullStaticCache; } } /// - /// Clears all objects in the request cache with the System.Type specified + /// Returns the current Runtime cache /// - internal void ClearRequestCacheObjectTypes() + public IRuntimeCacheProvider RuntimeCache { - if (!_enableCache) - { - _nullRequestCache.ClearCacheObjectTypes(); - } - else - { - _requestCache.ClearCacheObjectTypes(); - } + get { return _enableCache ? _httpCache : _nullHttpCache; } } - /// - /// Clears all request cache items that starts with the key passed. - /// - /// The start of the key - internal void ClearRequestCacheByKeySearch(string keyStartsWith) - { - if (!_enableCache) - { - _nullRequestCache.ClearCacheByKeySearch(keyStartsWith); - } - else - { - _requestCache.ClearCacheByKeySearch(keyStartsWith); - } - } + #region Legacy Runtime/Http Cache accessors - /// - /// Clears all cache items that have a key that matches the regular expression - /// - /// - internal void ClearRequestCacheByKeyExpression(string regexString) - { - if (!_enableCache) - { - _nullRequestCache.ClearCacheByKeyExpression(regexString); - } - else - { - _requestCache.ClearCacheByKeyExpression(regexString); - } - } - - internal IEnumerable GetRequestCacheItemsByKeySearch(string keyStartsWith) - { - if (!_enableCache) - { - return _nullRequestCache.GetCacheItemsByKeySearch(keyStartsWith); - } - else - { - return _requestCache.GetCacheItemsByKeySearch(keyStartsWith); - } - } - - /// - /// Returns a request cache item by key, does not update the cache if it isn't there. - /// - /// - /// - /// - internal TT GetRequestCacheItem(string cacheKey) - { - if (!_enableCache) - { - return _nullRequestCache.GetCacheItem(cacheKey); - } - else - { - return _requestCache.GetCacheItem(cacheKey); - } - } - - /// - /// Gets (and adds if necessary) an item from the request cache with all of the default parameters - /// - /// - /// - /// - /// - internal TT GetRequestCacheItem(string cacheKey, Func getCacheItem) - { - if (!_enableCache) - { - return _nullRequestCache.GetCacheItem(cacheKey, getCacheItem); - } - else - { - return _requestCache.GetCacheItem(cacheKey, getCacheItem); - } - } - - #endregion - - #region Static cache - - /// - /// Clears the item in umbraco's static cache - /// - internal void ClearAllStaticCache() - { - if (!_enableCache) - { - _nullStaticCache.ClearAllCache(); - } - else - { - _staticCache.ClearAllCache(); - } - } - - /// - /// Clears the item in umbraco's static cache with the given key - /// - /// Key - internal void ClearStaticCacheItem(string key) - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheItem(key); - } - else - { - _staticCache.ClearCacheItem(key); - } - } - - /// - /// Clears all objects in the static cache with the System.Type name as the - /// input parameter. (using [object].GetType()) - /// - /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" - internal void ClearStaticCacheObjectTypes(string typeName) - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheObjectTypes(typeName); - } - else - { - _staticCache.ClearCacheObjectTypes(typeName); - } - } - - /// - /// Clears all objects in the static cache with the System.Type specified - /// - internal void ClearStaticCacheObjectTypes() - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheObjectTypes(); - } - else - { - _staticCache.ClearCacheObjectTypes(); - } - } - - internal void ClearStaticCacheObjectTypes(Func predicate) - { - if (_enableCache) - _staticCache.ClearCacheObjectTypes(predicate); - else - _nullStaticCache.ClearCacheObjectTypes(predicate); - } - - /// - /// Clears all static cache items that starts with the key passed. - /// - /// The start of the key - internal void ClearStaticCacheByKeySearch(string keyStartsWith) - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheByKeySearch(keyStartsWith); - } - else - { - _staticCache.ClearCacheByKeySearch(keyStartsWith); - } - } - - /// - /// Clears all cache items that have a key that matches the regular expression - /// - /// - internal void ClearStaticCacheByKeyExpression(string regexString) - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheByKeyExpression(regexString); - } - else - { - _staticCache.ClearCacheByKeyExpression(regexString); - } - } - - internal IEnumerable GetStaticCacheItemsByKeySearch(string keyStartsWith) - { - if (!_enableCache) - { - return _nullStaticCache.GetCacheItemsByKeySearch(keyStartsWith); - } - else - { - return _staticCache.GetCacheItemsByKeySearch(keyStartsWith); - } - } - - /// - /// Returns a static cache item by key, does not update the cache if it isn't there. - /// - /// - /// - /// - internal TT GetStaticCacheItem(string cacheKey) - { - if (!_enableCache) - { - return _nullStaticCache.GetCacheItem(cacheKey); - } - else - { - return _staticCache.GetCacheItem(cacheKey); - } - } - - /// - /// Gets (and adds if necessary) an item from the static cache with all of the default parameters - /// - /// - /// - /// - /// - internal TT GetStaticCacheItem(string cacheKey, Func getCacheItem) - { - if (!_enableCache) - { - return _nullStaticCache.GetCacheItem(cacheKey, getCacheItem); - } - else - { - return _staticCache.GetCacheItem(cacheKey, getCacheItem); - } - } - - #endregion - - #region Runtime/Http Cache /// /// Clears the item in umbraco's runtime cache /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearAllCache() { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearAllCache(); } @@ -376,9 +149,10 @@ namespace Umbraco.Core /// Clears the item in umbraco's runtime cache with the given key /// /// Key + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheItem(string key) { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheItem(key); } @@ -394,9 +168,10 @@ namespace Umbraco.Core /// input parameter. (using [object].GetType()) /// /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheObjectTypes(string typeName) { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheObjectTypes(typeName); } @@ -409,9 +184,10 @@ namespace Umbraco.Core /// /// Clears all objects in the System.Web.Cache with the System.Type specified /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheObjectTypes() { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheObjectTypes(); } @@ -425,9 +201,10 @@ namespace Umbraco.Core /// Clears all cache items that starts with the key passed. /// /// The start of the key + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheByKeySearch(string keyStartsWith) { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheByKeySearch(keyStartsWith); } @@ -441,9 +218,10 @@ namespace Umbraco.Core /// Clears all cache items that have a key that matches the regular expression /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheByKeyExpression(string regexString) { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheByKeyExpression(regexString); } @@ -453,9 +231,10 @@ namespace Umbraco.Core } } + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { - if (!_enableCache) + if (_enableCache == false) { return _nullHttpCache.GetCacheItemsByKeySearch(keyStartsWith); } @@ -471,9 +250,10 @@ namespace Umbraco.Core /// /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey) { - if (!_enableCache) + if (_enableCache == false) { return _nullHttpCache.GetCacheItem(cacheKey); } @@ -490,9 +270,10 @@ namespace Umbraco.Core /// /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem); } @@ -510,16 +291,17 @@ namespace Umbraco.Core /// This will set an absolute expiration from now until the timeout /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey, TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - return _nullHttpCache.GetCacheItem(cacheKey, timeout, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout); } else { - return _httpCache.GetCacheItem(cacheKey, timeout, getCacheItem); + return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout); } } @@ -532,17 +314,18 @@ namespace Umbraco.Core /// This will set an absolute expiration from now until the timeout /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan timeout, Func getCacheItem) { if (!_enableCache) { - return _nullHttpCache.GetCacheItem(cacheKey, refreshAction, timeout, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, removedCallback: refreshAction); } else { - return _httpCache.GetCacheItem(cacheKey, refreshAction, timeout, getCacheItem); + return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout, removedCallback: refreshAction); } } @@ -556,17 +339,18 @@ namespace Umbraco.Core /// This will set an absolute expiration from now until the timeout /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - return _nullHttpCache.GetCacheItem(cacheKey, priority, refreshAction, timeout, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction); } else { - return _httpCache.GetCacheItem(cacheKey, priority, refreshAction, timeout, getCacheItem); + return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction); } } @@ -581,6 +365,7 @@ namespace Umbraco.Core /// This will set an absolute expiration from now until the timeout /// /// + [Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")] public TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, @@ -588,13 +373,19 @@ namespace Umbraco.Core TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - return _nullHttpCache.GetCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null); } else { - return _httpCache.GetCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem); + var cache = _httpCache as HttpRuntimeCacheProvider; + if (cache != null) + { + var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency); + return result == null ? default(TT) : result.TryConvertTo().Result; + } + throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider)); } } @@ -607,6 +398,7 @@ namespace Umbraco.Core /// /// /// + [Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")] public TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, @@ -614,11 +406,17 @@ namespace Umbraco.Core { if (!_enableCache) { - return _nullHttpCache.GetCacheItem(cacheKey, priority, null, cacheDependency, null, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, null, false, priority, null, null); } else { - return _httpCache.GetCacheItem(cacheKey, priority, null, cacheDependency, null, getCacheItem); + var cache = _httpCache as HttpRuntimeCacheProvider; + if (cache != null) + { + var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency); + return result == null ? default(TT) : result.TryConvertTo().Result; + } + throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider)); } } @@ -633,13 +431,13 @@ namespace Umbraco.Core CacheItemPriority priority, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - _nullHttpCache.InsertCacheItem(cacheKey, priority, getCacheItem); + _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, priority: priority); } else { - _httpCache.InsertCacheItem(cacheKey, priority, getCacheItem); + _httpCache.InsertCacheItem(cacheKey, getCacheItem, priority: priority); } } @@ -656,13 +454,13 @@ namespace Umbraco.Core TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - _nullHttpCache.InsertCacheItem(cacheKey, priority, timeout, getCacheItem); + _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority); } else { - _httpCache.InsertCacheItem(cacheKey, priority, timeout, getCacheItem); + _httpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority); } } @@ -675,19 +473,25 @@ namespace Umbraco.Core /// /// This will set an absolute expiration from now until the timeout /// + [Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")] public void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - _nullHttpCache.InsertCacheItem(cacheKey, priority, cacheDependency, timeout, getCacheItem); + _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority, dependentFiles: null); } else { - _httpCache.InsertCacheItem(cacheKey, priority, cacheDependency, timeout, getCacheItem); + var cache = _httpCache as HttpRuntimeCacheProvider; + if (cache != null) + { + cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency); + } + throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider)); } } @@ -701,6 +505,7 @@ namespace Umbraco.Core /// /// This will set an absolute expiration from now until the timeout /// + [Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")] public void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, @@ -708,17 +513,22 @@ namespace Umbraco.Core TimeSpan? timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - _nullHttpCache.InsertCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem); + _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null); } else { - _httpCache.InsertCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem); + var cache = _httpCache as HttpRuntimeCacheProvider; + if (cache != null) + { + cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency); + } + throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider)); } - } + } #endregion - } + } } diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 91ee5db40f..0ed62f720a 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; @@ -38,6 +39,7 @@ namespace Umbraco.Core private bool _isComplete = false; private readonly UmbracoApplicationBase _umbracoApplication; protected ApplicationContext ApplicationContext { get; private set; } + protected CacheHelper ApplicationCache { get; set; } protected UmbracoApplicationBase UmbracoApplication { @@ -57,16 +59,19 @@ namespace Umbraco.Core InitializeProfilerResolver(); + CreateApplicationCache(); + _timer = DisposableTimer.DebugDuration("Umbraco application starting", "Umbraco application startup complete"); //create database and service contexts for the app context var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName); Database.Mapper = new PetaPocoMapper(); var dbContext = new DatabaseContext(dbFactory); - var serviceContext = new ServiceContext( - new PetaPocoUnitOfWorkProvider(dbFactory), - new FileUnitOfWorkProvider(), - new PublishingStrategy()); + var serviceContext = new ServiceContext( + new PetaPocoUnitOfWorkProvider(dbFactory), + new FileUnitOfWorkProvider(), + new PublishingStrategy(), + ApplicationCache); CreateApplicationContext(dbContext, serviceContext); @@ -94,7 +99,21 @@ namespace Umbraco.Core protected virtual void CreateApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext) { //create the ApplicationContext - ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext); + ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext, ApplicationCache); + } + + /// + /// Creates and assigns the ApplicationCache based on a new instance of System.Web.Caching.Cache + /// + protected virtual void CreateApplicationCache() + { + var cacheHelper = new CacheHelper( + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + //we have no request based cache when not running in web-based context + new NullCacheProvider()); + + ApplicationCache = cacheHelper; } /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 91532fdaad..eb53215896 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -102,7 +102,7 @@ namespace Umbraco.Core.Models.PublishedContent Logging.LogHelper.Debug("Clear all."); // ok and faster to do it by types, assuming noone else caches PublishedContentType instances //ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_"); - ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes(); + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes(); } internal static void ClearContentType(int id) @@ -110,7 +110,7 @@ namespace Umbraco.Core.Models.PublishedContent Logging.LogHelper.Debug("Clear content type w/id {0}.", () => id); // requires a predicate because the key does not contain the ID // faster than key strings comparisons anyway - ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes( + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes( (key, value) => value.Id == id); } @@ -121,7 +121,7 @@ namespace Umbraco.Core.Models.PublishedContent // properties ie both its own properties and those that were inherited (it's based upon an // IContentTypeComposition) and so every PublishedContentType having a property based upon // the cleared data type, be it local or inherited, will be cleared. - ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes( + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes( (key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id)); } @@ -130,7 +130,7 @@ namespace Umbraco.Core.Models.PublishedContent var key = string.Format("PublishedContentType_{0}_{1}", itemType == PublishedItemType.Content ? "content" : "media", alias.ToLowerInvariant()); - var type = ApplicationContext.Current.ApplicationCache.GetStaticCacheItem(key, + var type = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItem(key, () => CreatePublishedContentType(itemType, alias)); return type; @@ -142,8 +142,8 @@ namespace Umbraco.Core.Models.PublishedContent return GetPublishedContentTypeCallback(alias); var contentType = itemType == PublishedItemType.Content - ? (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias) - : (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); + ? (IContentTypeComposition)ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias) + : (IContentTypeComposition)ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); if (contentType == null) throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".", @@ -161,7 +161,7 @@ namespace Umbraco.Core.Models.PublishedContent { // see note above //ClearAll(); - ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_"); + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheByKeySearch("PublishedContentType_"); _getPublishedContentTypeCallBack = value; } diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index b6ccfc3d81..725697fa26 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -23,7 +23,10 @@ namespace Umbraco.Core.Services private Lazy _packagingService; private Lazy _serverRegistrationService; private Lazy _entityService; - private Lazy _relationService; + //private Lazy _relationService; + //private Lazy _treeService; + //private Lazy _sectionService; + //private Lazy _macroService; private Lazy _memberTypeService; /// @@ -38,9 +41,23 @@ namespace Umbraco.Core.Services /// /// /// - public ServiceContext(IContentService contentService, IMediaService mediaService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, PackagingService packagingService, IEntityService entityService, RelationService relationService) + /// + /// + /// + public ServiceContext( + IContentService contentService, + IMediaService mediaService, + IContentTypeService contentTypeService, + IDataTypeService dataTypeService, + IFileService fileService, + ILocalizationService localizationService, + PackagingService packagingService, + IEntityService entityService/*, + IRelationService relationService, + ISectionService sectionService, + IApplicationTreeService treeService*/) { - _contentService = new Lazy(() => contentService); + _contentService = new Lazy(() => contentService); _mediaService = new Lazy(() => mediaService); _contentTypeService = new Lazy(() => contentTypeService); _dataTypeService = new Lazy(() => dataTypeService); @@ -48,7 +65,9 @@ namespace Umbraco.Core.Services _localizationService = new Lazy(() => localizationService); _packagingService = new Lazy(() => packagingService); _entityService = new Lazy(() => entityService); - _relationService = new Lazy(() => relationService); + //_relationService = new Lazy(() => relationService); + //_sectionService = new Lazy(() => sectionService); + //_treeService = new Lazy(() => treeService); } /// @@ -57,11 +76,12 @@ namespace Umbraco.Core.Services /// /// /// - internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy) + /// + internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy, CacheHelper cache) { - BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, - //this needs to be lazy because when we create the service context it's generally before the - //resolvers have been initialized! + BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, cache, + //this needs to be lazy because when we create the service context it's generally before the + //resolvers have been initialized! new Lazy(() => RepositoryResolver.Current.Factory)); } @@ -72,6 +92,7 @@ namespace Umbraco.Core.Services IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy, + CacheHelper cache, Lazy repositoryFactory) { var provider = dbUnitOfWorkProvider; @@ -110,11 +131,21 @@ namespace Umbraco.Core.Services if (_entityService == null) _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); - if (_relationService == null) - _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); + //if (_relationService == null) + // _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); + + //if (_treeService == null) + // _treeService = new Lazy(() => new ApplicationTreeService(cache)); + + //if (_sectionService == null) + // _sectionService = new Lazy(() => new SectionService(_userService.Value, _treeService.Value, cache)); + + //if (_macroService == null) + // _macroService = new Lazy(() => new MacroService(provider, repositoryFactory.Value)); if (_memberTypeService == null) _memberTypeService = new Lazy(() => new MemberTypeService(provider, repositoryFactory.Value)); + } /// @@ -124,22 +155,30 @@ namespace Umbraco.Core.Services { get { return _serverRegistrationService.Value; } } + + ///// + ///// Gets the + ///// + //public IMacroService MacroService + //{ + // get { return _macroService.Value; } + //} /// - /// Gets the + /// Gets the /// public IEntityService EntityService { get { return _entityService.Value; } } - /// - /// Gets the - /// - public RelationService RelationService - { - get { return _relationService.Value; } - } + ///// + ///// Gets the + ///// + //public IRelationService RelationService + //{ + // get { return _relationService.Value; } + //} /// /// Gets the @@ -212,7 +251,23 @@ namespace Umbraco.Core.Services { get { return _memberService.Value; } } - + + ///// + ///// Gets the + ///// + //public ISectionService SectionService + //{ + // get { return _sectionService.Value; } + //} + + ///// + ///// Gets the + ///// + //public IApplicationTreeService ApplicationTreeService + //{ + // get { return _treeService.Value; } + //} + /// /// Gets the MemberTypeService /// diff --git a/src/Umbraco.Core/Standalone/ServiceContextManager.cs b/src/Umbraco.Core/Standalone/ServiceContextManager.cs index 579bde0ff2..955c7a25df 100644 --- a/src/Umbraco.Core/Standalone/ServiceContextManager.cs +++ b/src/Umbraco.Core/Standalone/ServiceContextManager.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using Umbraco.Core.Cache; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.UnitOfWork; @@ -45,13 +46,20 @@ namespace Umbraco.Core.Standalone { if (_serviceContext == null) { + var cacheHelper = new CacheHelper( + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + //we have no request based cache when running standalone + new NullCacheProvider()); + var dbFactory = new DefaultDatabaseFactory(_connectionString, _providerName); var dbContext = new DatabaseContext(dbFactory); Database.Mapper = new PetaPocoMapper(); _serviceContext = new ServiceContext( new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), - new PublishingStrategy()); + new PublishingStrategy(), + cacheHelper); //initialize the DatabaseContext dbContext.Initialize(_providerName); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 84a52c2947..8498e39e4d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -113,19 +113,20 @@ - + - - + + + @@ -725,7 +726,7 @@ - + diff --git a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs index 7fe009ffd7..9e4e6ccea7 100644 --- a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs +++ b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.BusinessLogic [SetUp] public void Initialize() { - ApplicationContext.Current = new ApplicationContext(false){IsReady = true}; + ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; InitializeDatabase(); InitializeApps(); InitializeAppConfigFile(); diff --git a/src/Umbraco.Tests/Cache/CacheProviderTests.cs b/src/Umbraco.Tests/Cache/CacheProviderTests.cs index b7ed588bb8..5140995c38 100644 --- a/src/Umbraco.Tests/Cache/CacheProviderTests.cs +++ b/src/Umbraco.Tests/Cache/CacheProviderTests.cs @@ -1,4 +1,5 @@ -using System.Web.UI; +using System.Linq; +using System.Web.UI; using NUnit.Framework; using Umbraco.Core.Cache; using umbraco; @@ -23,12 +24,69 @@ namespace Umbraco.Tests.Cache } [Test] - public void Can_Remove_By_Type_Name() + public void Can_Get_By_Search() { var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); - var cacheContent4 = new MacroCacheContent(new LiteralControl(), "Test4"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Tester2", () => cacheContent2); + Provider.GetCacheItem("Tes3", () => cacheContent3); + Provider.GetCacheItem("different4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + var result = Provider.GetCacheItemsByKeySearch("Tes"); + + Assert.AreEqual(3, result.Count()); + } + + [Test] + public void Can_Clear_By_Expression() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("TTes1t", () => cacheContent1); + Provider.GetCacheItem("Tester2", () => cacheContent2); + Provider.GetCacheItem("Tes3", () => cacheContent3); + Provider.GetCacheItem("different4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearCacheByKeyExpression("^\\w+es\\d.*"); + + Assert.AreEqual(2, GetTotalItemCount); + } + + [Test] + public void Can_Clear_By_Search() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Tester2", () => cacheContent2); + Provider.GetCacheItem("Tes3", () => cacheContent3); + Provider.GetCacheItem("different4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearCacheByKeySearch("Test"); + + Assert.AreEqual(2, GetTotalItemCount); + } + + [Test] + public void Can_Clear_By_Key() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); Provider.GetCacheItem("Test1", () => cacheContent1); Provider.GetCacheItem("Test2", () => cacheContent2); Provider.GetCacheItem("Test3", () => cacheContent3); @@ -36,18 +94,76 @@ namespace Umbraco.Tests.Cache Assert.AreEqual(4, GetTotalItemCount); - Provider.ClearCacheObjectTypes("umbraco.MacroCacheContent"); + Provider.ClearCacheItem("Test1"); + Provider.ClearCacheItem("Test2"); + + Assert.AreEqual(2, GetTotalItemCount); + } + + [Test] + public void Can_Clear_All_Items() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Test2", () => cacheContent2); + Provider.GetCacheItem("Test3", () => cacheContent3); + Provider.GetCacheItem("Test4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearAllCache(); Assert.AreEqual(0, GetTotalItemCount); } + [Test] + public void Can_Add_When_Not_Available() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + Provider.GetCacheItem("Test1", () => cacheContent1); + Assert.AreEqual(1, GetTotalItemCount); + } + + [Test] + public void Can_Get_When_Available() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var result = Provider.GetCacheItem("Test1", () => cacheContent1); + var result2 = Provider.GetCacheItem("Test1", () => cacheContent1); + Assert.AreEqual(1, GetTotalItemCount); + Assert.AreEqual(result, result2); + } + + [Test] + public void Can_Remove_By_Type_Name() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Test2", () => cacheContent2); + Provider.GetCacheItem("Test3", () => cacheContent3); + Provider.GetCacheItem("Test4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + //Provider.ClearCacheObjectTypes("umbraco.MacroCacheContent"); + Provider.ClearCacheObjectTypes(typeof(MacroCacheContent).ToString()); + + Assert.AreEqual(1, GetTotalItemCount); + } + [Test] public void Can_Remove_By_Strong_Type() { var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); - var cacheContent4 = new MacroCacheContent(new LiteralControl(), "Test4"); + var cacheContent4 = new LiteralControl(); Provider.GetCacheItem("Test1", () => cacheContent1); Provider.GetCacheItem("Test2", () => cacheContent2); Provider.GetCacheItem("Test3", () => cacheContent3); @@ -57,7 +173,7 @@ namespace Umbraco.Tests.Cache Provider.ClearCacheObjectTypes(); - Assert.AreEqual(0, GetTotalItemCount); + Assert.AreEqual(1, GetTotalItemCount); } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs b/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs index 03ad355f42..d123d2dc31 100644 --- a/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs +++ b/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache public void Can_Add_And_Expire_Struct_Strongly_Typed_With_Null() { var now = DateTime.Now; - RuntimeProvider.InsertCacheItem("DateTimeTest", CacheItemPriority.Default, new TimeSpan(0, 0, 0, 0, 200), () => now); + RuntimeProvider.InsertCacheItem("DateTimeTest", () => now, new TimeSpan(0, 0, 0, 0, 200)); Assert.AreEqual(now, Provider.GetCacheItem("DateTimeTest")); Assert.AreEqual(now, Provider.GetCacheItem("DateTimeTest")); diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index c4f8977b35..0d822f05a9 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -5,6 +5,7 @@ using System.Web.UI; using System.Web.UI.WebControls; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Profiling; using umbraco; using umbraco.cms.businesslogic.macro; @@ -19,11 +20,15 @@ namespace Umbraco.Tests.Macros public void Setup() { //we DO want cache enabled for these tests - ApplicationContext.Current = new ApplicationContext(true); + var cacheHelper = new CacheHelper( + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + new NullCacheProvider()); + ApplicationContext.Current = new ApplicationContext(cacheHelper); ProfilerResolver.Current = new ProfilerResolver(new LogProfiler()) - { - CanResolveBeforeFrozen = true - }; + { + CanResolveBeforeFrozen = true + }; } [TearDown] diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index be31d2dbf4..126fad79d8 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests [Test] public void Can_Create_Empty_App_Context() { - var appCtx = new ApplicationContext(false); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); Assert.Pass(); } @@ -43,11 +43,13 @@ namespace Umbraco.Tests new Mock().Object, new RepositoryFactory(true), new Mock().Object), - new Mock().Object, + new Mock().Object/*, new RelationService( new Mock().Object, new RepositoryFactory(true), - new Mock().Object)); + new Mock().Object), + new Mock().Object, + new Mock().Object*/); Assert.Pass(); } @@ -79,19 +81,19 @@ namespace Umbraco.Tests new Mock().Object, new RepositoryFactory(true), new Mock().Object), - new Mock().Object, + new Mock().Object/*, new RelationService( new Mock().Object, new RepositoryFactory(true), - new Mock().Object)), - false); + new Mock().Object)*/), + CacheHelper.CreateDisabledCacheHelper()); Assert.Pass(); } - + [Test] public void Can_Assign_App_Context_Singleton() { - var appCtx = new ApplicationContext(false); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); var result = ApplicationContext.EnsureContext(appCtx, true); Assert.AreEqual(appCtx, result); } @@ -99,8 +101,8 @@ namespace Umbraco.Tests [Test] public void Does_Not_Overwrite_App_Context_Singleton() { - ApplicationContext.EnsureContext(new ApplicationContext(false), true); - var appCtx = new ApplicationContext(false); + ApplicationContext.EnsureContext(new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()), true); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); var result = ApplicationContext.EnsureContext(appCtx, false); Assert.AreNotEqual(appCtx, result); } @@ -109,14 +111,14 @@ namespace Umbraco.Tests [Test] public void Can_Get_Umbraco_Context() { - var appCtx = new ApplicationContext(false); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); ApplicationContext.EnsureContext(appCtx, true); - + var umbCtx = UmbracoContext.EnsureContext( new Mock().Object, appCtx, true); - + Assert.AreEqual(umbCtx, UmbracoContext.Current); } diff --git a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs index 462936c26e..1d33949df2 100644 --- a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs @@ -402,7 +402,7 @@ namespace Umbraco.Tests.Mvc //PublishedContentCache.UnitTesting = true; // ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; - var appCtx = new ApplicationContext(false) { IsReady = true }; + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; var ctx = new UmbracoContext( GetHttpContextFactory(url, routeData).HttpContext, diff --git a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs index f8f56d6dcb..bb0ac581bc 100644 --- a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs @@ -27,17 +27,19 @@ namespace Umbraco.Tests.Persistence RepositoryResolver.Current = new RepositoryResolver( new RepositoryFactory()); - + + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + ApplicationContext.Current = new ApplicationContext( //assign the db context new DatabaseContext(new DefaultDatabaseFactory()), //assign the service context - new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy()), - //disable cache - false) - { - IsReady = true - }; + new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper), + cacheHelper) + { + IsReady = true + }; Resolution.Freeze(); } diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs index 25fe36cbf7..5bf2264c5c 100644 --- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs +++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs @@ -15,18 +15,18 @@ namespace Umbraco.Tests.Persistence { private DatabaseContext _dbContext; - [SetUp] - public void Setup() - { - _dbContext = new DatabaseContext(new DefaultDatabaseFactory()); + [SetUp] + public void Setup() + { + _dbContext = new DatabaseContext(new DefaultDatabaseFactory()); - //unfortunately we have to set this up because the PetaPocoExtensions require singleton access - ApplicationContext.Current = new ApplicationContext(false) - { - DatabaseContext = _dbContext, - IsReady = true - }; - } + //unfortunately we have to set this up because the PetaPocoExtensions require singleton access + ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) + { + DatabaseContext = _dbContext, + IsReady = true + }; + } [TearDown] public void TearDown() diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 18cf4bc278..6d5c9065d7 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.PublishedContent var caches = CreatePublishedContent(); - ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; + ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; var factory = new FakeHttpContextFactory("http://umbraco.local/"); StateHelper.HttpContext = factory.HttpContext; var context = new UmbracoContext( diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2806b1a931..610d135870 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.PublishedContent .Union(new[] { typeof(PublishedContentTests).Assembly }) }; - ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; + ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; // need to specify a custom callback for unit tests // AutoPublishedContentTypes generates properties automatically diff --git a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs index 1c7f2a39e6..eaa3c9c65c 100644 --- a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs +++ b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs @@ -24,27 +24,30 @@ namespace Umbraco.Tests.Services private PerThreadPetaPocoUnitOfWorkProvider _uowProvider; private PerThreadDatabaseFactory _dbFactory; - [SetUp] - public override void Initialize() - { - base.Initialize(); - - //we need to use our own custom IDatabaseFactory for the DatabaseContext because we MUST ensure that - //a Database instance is created per thread, whereas the default implementation which will work in an HttpContext - //threading environment, or a single apartment threading environment will not work for this test because - //it is multi-threaded. - _dbFactory = new PerThreadDatabaseFactory(); - //overwrite the local object - ApplicationContext.DatabaseContext = new DatabaseContext(_dbFactory); + [SetUp] + public override void Initialize() + { + base.Initialize(); - //here we are going to override the ServiceContext because normally with our test cases we use a - //global Database object but this is NOT how it should work in the web world or in any multi threaded scenario. - //we need a new Database object for each thread. - _uowProvider = new PerThreadPetaPocoUnitOfWorkProvider(_dbFactory); - ApplicationContext.Services = new ServiceContext(_uowProvider, new FileUnitOfWorkProvider(), new PublishingStrategy()); + //we need to use our own custom IDatabaseFactory for the DatabaseContext because we MUST ensure that + //a Database instance is created per thread, whereas the default implementation which will work in an HttpContext + //threading environment, or a single apartment threading environment will not work for this test because + //it is multi-threaded. + _dbFactory = new PerThreadDatabaseFactory(); + //overwrite the local object + ApplicationContext.DatabaseContext = new DatabaseContext(_dbFactory); - CreateTestData(); - } + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + + //here we are going to override the ServiceContext because normally with our test cases we use a + //global Database object but this is NOT how it should work in the web world or in any multi threaded scenario. + //we need a new Database object for each thread. + _uowProvider = new PerThreadPetaPocoUnitOfWorkProvider(_dbFactory); + ApplicationContext.Services = new ServiceContext(_uowProvider, new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper); + + CreateTestData(); + } [TearDown] public override void TearDown() diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 755c6e5714..6bd46836ae 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -53,34 +53,38 @@ namespace Umbraco.Tests.TestHelpers public override void Initialize() { InitializeFirstRunFlags(); - + var path = TestHelper.CurrentAssemblyDirectory; AppDomain.CurrentDomain.SetData("DataDirectory", path); + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + var dbFactory = new DefaultDatabaseFactory( GetDbConnectionString(), GetDbProviderName()); + _appContext = new ApplicationContext( - //assign the db context - new DatabaseContext(dbFactory), - //assign the service context - new ServiceContext(new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy()), - //disable cache - false) - { - IsReady = true - }; + //assign the db context + new DatabaseContext(dbFactory), + //assign the service context + new ServiceContext(new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper), + cacheHelper) + { + IsReady = true + }; base.Initialize(); - DatabaseContext.Initialize(dbFactory.ProviderName, dbFactory.ConnectionString); + using (DisposableTimer.TraceDuration("init")) + { + DatabaseContext.Initialize(dbFactory.ProviderName, dbFactory.ConnectionString); + CreateSqlCeDatabase(); + InitializeDatabase(); - CreateSqlCeDatabase(); - - InitializeDatabase(); - - //ensure the configuration matches the current version for tests - SettingsForTests.ConfigurationStatus = UmbracoVersion.Current.ToString(3); + //ensure the configuration matches the current version for tests + SettingsForTests.ConfigurationStatus = UmbracoVersion.Current.ToString(3); + } } protected override void SetupApplicationContext() diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseTest.cs index ffe068e5ce..3142394db4 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseTest.cs @@ -34,24 +34,24 @@ namespace Umbraco.Tests.TestHelpers string path = TestHelper.CurrentAssemblyDirectory; AppDomain.CurrentDomain.SetData("DataDirectory", path); - - try + + try + { + //Delete database file before continueing + string filePath = string.Concat(path, "\\test.sdf"); + if (File.Exists(filePath)) { - //Delete database file before continueing - string filePath = string.Concat(path, "\\test.sdf"); - if (File.Exists(filePath)) - { - File.Delete(filePath); - } + File.Delete(filePath); } - catch (Exception) - { - //if this doesn't work we have to make sure everything is reset! otherwise - // well run into issues because we've already set some things up - TearDown(); - throw; - } - + } + catch (Exception) + { + //if this doesn't work we have to make sure everything is reset! otherwise + // well run into issues because we've already set some things up + TearDown(); + throw; + } + RepositoryResolver.Current = new RepositoryResolver( new RepositoryFactory(true)); //disable all repo caches for tests! @@ -60,16 +60,19 @@ namespace Umbraco.Tests.TestHelpers new List { typeof(MySqlSyntaxProvider), typeof(SqlCeSyntaxProvider), typeof(SqlServerSyntaxProvider) }) { CanResolveBeforeFrozen = true }; Resolution.Freeze(); + + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + ApplicationContext.Current = new ApplicationContext( //assign the db context new DatabaseContext(new DefaultDatabaseFactory()), //assign the service context - new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy()), - //disable cache - false) - { - IsReady = true - }; + new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper), + cacheHelper) + { + IsReady = true + }; SqlSyntaxContext.SqlSyntaxProvider = SyntaxProvider; diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index a3e93bfc07..22d39d4559 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -2,6 +2,10 @@ using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.ObjectResolution; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Publishing; +using Umbraco.Core.Services; using Umbraco.Web; namespace Umbraco.Tests.TestHelpers @@ -71,8 +75,18 @@ namespace Umbraco.Tests.TestHelpers /// protected virtual void SetupApplicationContext() { - //DO NOT ENABLE CACHE - ApplicationContext.Current = new ApplicationContext(false) {IsReady = true}; + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + + ApplicationContext.Current = new ApplicationContext( + //assign the db context + new DatabaseContext(new DefaultDatabaseFactory()), + //assign the service context + new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper), + cacheHelper) + { + IsReady = true + }; } /// diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c04eb1a868..dbe4915ef1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -104,7 +104,7 @@ - + True ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll @@ -146,17 +146,17 @@ - + + + + + + - - - - - diff --git a/src/Umbraco.Web/Standalone/ServiceContextManager.cs b/src/Umbraco.Web/Standalone/ServiceContextManager.cs index aa53b586ee..561fadfc2c 100644 --- a/src/Umbraco.Web/Standalone/ServiceContextManager.cs +++ b/src/Umbraco.Web/Standalone/ServiceContextManager.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.UnitOfWork; @@ -34,13 +35,23 @@ namespace Umbraco.Web.Standalone { if (_serviceContext == null) { + var cacheHelper = new CacheHelper( + //SD: Not sure if this is correct? Should we be using HttpRuntime.Cache here since this is for 'Web' ? + // just not quite sure what this standalone stuff is. + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + //SD: Not sure if this is correct? Should we be using request cache here since this is for 'Web' ? + // just not quite sure what this standalone stuff is. + new NullCacheProvider()); + var dbFactory = new DefaultDatabaseFactory(_connectionString, _providerName); var dbContext = new DatabaseContext(dbFactory); Database.Mapper = new PetaPocoMapper(); _serviceContext = new ServiceContext( new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), - new PublishingStrategy()); + new PublishingStrategy(), + cacheHelper); //initialize the DatabaseContext dbContext.Initialize(_providerName); From 8210702474d45359ae84ab08d88fb67b691b6786 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 13:22:52 +1100 Subject: [PATCH 4/8] removes the xquery tests - we don't need these and r# keeps wanting to run them --- .../CodeMirror/js/mode/xquery/test/index.html | 27 ------ .../js/mode/xquery/test/testBase.js | 42 --------- .../xquery/test/testEmptySequenceKeyword.js | 16 ---- .../js/mode/xquery/test/testMultiAttr.js | 16 ---- .../js/mode/xquery/test/testNamespaces.js | 91 ------------------- .../xquery/test/testProcessingInstructions.js | 16 ---- .../js/mode/xquery/test/testQuotes.js | 19 ---- 7 files changed, 227 deletions(-) delete mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/index.html delete mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testBase.js delete mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testEmptySequenceKeyword.js delete mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testMultiAttr.js delete mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testNamespaces.js delete mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testProcessingInstructions.js delete mode 100644 src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testQuotes.js diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/index.html b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/index.html deleted file mode 100644 index ba82e54f37..0000000000 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - -

XQuery CodeMirror Mode

-

-

-
    -
-
- - diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testBase.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testBase.js deleted file mode 100644 index d40e9eeabc..0000000000 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testBase.js +++ /dev/null @@ -1,42 +0,0 @@ - $(document).ready(function(){ - module("testBase"); - test("eviltest", function() { - expect(1); - - var input = 'xquery version "1.0-ml";\ - (: this is\ - : a \ - "comment" :)\ - let $let := <x attr="value">"test"<func>function() $var {function()} {$var}</func></x>\ - let $joe:=1\ - return element element {\ - attribute attribute { 1 },\ - element test { 'a' }, \ - attribute foo { "bar" },\ - fn:doc()[ foo/@bar eq $let ],\ - //x } \ - \ - (: a more \'evil\' test :)\ - (: Modified Blakeley example (: with nested comment :) ... :)\ - declare private function local:declare() {()};\ - declare private function local:private() {()};\ - declare private function local:function() {()};\ - declare private function local:local() {()};\ - let $let := <let>let $let := "let"</let>\ - return element element {\ - attribute attribute { try { xdmp:version() } catch($e) { xdmp:log($e) } },\ - attribute fn:doc { "bar" castable as xs:string },\ - element text { text { "text" } },\ - fn:doc()[ child::eq/(@bar | attribute::attribute) eq $let ],\ - //fn:doc\ - }'; - var expected = 'xquery version "1.0-ml"; (: this is : a "comment" :) let $let := <x attr="value">"test"<func>function() $var {function()} {$var}</func></x> let $joe:=1 return element element { attribute attribute { 1 }, element test { \'a\' }, attribute foo { "bar" }, fn:doc()[ foo/@bar eq $let ], //x } (: a more \'evil\' test :) (: Modified Blakeley example (: with nested comment :) ... :) declare private function local:declare() {()}; declare private function local:private() {()}; declare private function local:function() {()}; declare private function local:local() {()}; let $let := <let>let $let := "let"</let> return element element { attribute attribute { try { xdmp:version() } catch($e) { xdmp:log($e) } }, attribute fn:doc { "bar" castable as xs:string }, element text { text { "text" } }, fn:doc()[ child::eq/(@bar | attribute::attribute) eq $let ], //fn:doc }'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - }); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testEmptySequenceKeyword.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testEmptySequenceKeyword.js deleted file mode 100644 index 39ed090500..0000000000 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testEmptySequenceKeyword.js +++ /dev/null @@ -1,16 +0,0 @@ -$(document).ready(function(){ - module("testEmptySequenceKeyword"); - test("testEmptySequenceKeyword", function() { - expect(1); - - var input = '"foo" instance of empty-sequence()'; - var expected = '"foo" instance of empty-sequence()'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); -}); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testMultiAttr.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testMultiAttr.js deleted file mode 100644 index 8e98c47d62..0000000000 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testMultiAttr.js +++ /dev/null @@ -1,16 +0,0 @@ - $(document).ready(function(){ - module("testMultiAttr"); - test("test1", function() { - expect(1); - - var expected = '<p a1="foo" a2="bar">hello world</p>'; - - $("#sandbox").html(''); - $("#editor").html('

hello world

'); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testNamespaces.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testNamespaces.js deleted file mode 100644 index 4efea63e0d..0000000000 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testNamespaces.js +++ /dev/null @@ -1,91 +0,0 @@ -$(document).ready(function(){ - module("test namespaces"); - -// -------------------------------------------------------------------------------- -// this test is based on this: -//http://mbrevoort.github.com/CodeMirror2/#!exprSeqTypes/PrologExpr/VariableProlog/ExternalVariablesWith/K2-ExternalVariablesWith-10.xq -// -------------------------------------------------------------------------------- - test("test namespaced variable", function() { - expect(1); - - var input = 'declare namespace e = "http://example.com/ANamespace";\ -declare variable $e:exampleComThisVarIsNotRecognized as element(*) external;'; - - var expected = 'declare namespace e = "http://example.com/ANamespace";declare variable $e:exampleComThisVarIsNotRecognized as element(*) external;'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - - -// -------------------------------------------------------------------------------- -// this test is based on: -// http://mbrevoort.github.com/CodeMirror2/#!Basics/EQNames/eqname-002.xq -// -------------------------------------------------------------------------------- - test("test EQName variable", function() { - expect(1); - - var input = 'declare variable $"http://www.example.com/ns/my":var := 12;\ -{$"http://www.example.com/ns/my":var}'; - - var expected = 'declare variable $"http://www.example.com/ns/my":var := 12;<out>{$"http://www.example.com/ns/my":var}</out>'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - -// -------------------------------------------------------------------------------- -// this test is based on: -// http://mbrevoort.github.com/CodeMirror2/#!Basics/EQNames/eqname-003.xq -// -------------------------------------------------------------------------------- - test("test EQName function", function() { - expect(1); - - var input = 'declare function "http://www.example.com/ns/my":fn ($a as xs:integer) as xs:integer {\ - $a + 2\ -};\ -{"http://www.example.com/ns/my":fn(12)}'; - - var expected = 'declare function "http://www.example.com/ns/my":fn ($a as xs:integer) as xs:integer { $a + 2};<out>{"http://www.example.com/ns/my":fn(12)}</out>'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - -// -------------------------------------------------------------------------------- -// this test is based on: -// http://mbrevoort.github.com/CodeMirror2/#!Basics/EQNames/eqname-003.xq -// -------------------------------------------------------------------------------- - test("test EQName function with single quotes", function() { - expect(1); - - var input = 'declare function \'http://www.example.com/ns/my\':fn ($a as xs:integer) as xs:integer {\ - $a + 2\ -};\ -{\'http://www.example.com/ns/my\':fn(12)}'; - - var expected = 'declare function \'http://www.example.com/ns/my\':fn ($a as xs:integer) as xs:integer { $a + 2};<out>{\'http://www.example.com/ns/my\':fn(12)}</out>'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - -}); - - diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testProcessingInstructions.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testProcessingInstructions.js deleted file mode 100644 index 9b75305283..0000000000 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testProcessingInstructions.js +++ /dev/null @@ -1,16 +0,0 @@ -$(document).ready(function(){ - module("testProcessingInstructions"); - test("testProcessingInstructions", function() { - expect(1); - - var input = 'data() instance of xs:string'; - var expected = 'data(<?target content?>) instance of xs:string'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); -}); diff --git a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testQuotes.js b/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testQuotes.js deleted file mode 100644 index 79e5142d9f..0000000000 --- a/src/Umbraco.Web.UI/umbraco_client/CodeMirror/js/mode/xquery/test/testQuotes.js +++ /dev/null @@ -1,19 +0,0 @@ - $(document).ready(function(){ - module("testQuoteEscape"); - test("testQuoteEscapeDouble", function() { - expect(1); - - var input = 'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"\ -let $keysfolder := concat($rootfolder, "keys\\")\ -return\ -$keysfolder'; - var expected = 'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"let $keysfolder := concat($rootfolder, "keys\\")return$keysfolder'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - }); From 11103c0bc500577aa0f0091b8168112c3e7d8767 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 13:23:07 +1100 Subject: [PATCH 5/8] proj file --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index a11c30a444..83884f6950 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -867,13 +867,6 @@ - - - - - - - From b86cddda94678ff0642f52ba1038d276ff440d46 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 13:55:46 +1100 Subject: [PATCH 6/8] fixes up unit tests --- .../Models/Rdbms/PropertyTypeGroupDto.cs | 2 +- src/Umbraco.Tests/unit-test-log4net.config | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs index 0bbf3a891f..51cb9872fe 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Models.Rdbms internal class PropertyTypeGroupDto { [Column("id")] - [PrimaryKeyColumn(IdentitySeed = 6)] + [PrimaryKeyColumn(IdentitySeed = 12)] public int Id { get; set; } [Column("parentGroupId")] diff --git a/src/Umbraco.Tests/unit-test-log4net.config b/src/Umbraco.Tests/unit-test-log4net.config index 0922e4d932..dbc6eb1677 100644 --- a/src/Umbraco.Tests/unit-test-log4net.config +++ b/src/Umbraco.Tests/unit-test-log4net.config @@ -14,16 +14,23 @@ - + + + + + + + + - + - + From 4bac3e19fe8e1e51199297fa473fb271b0969d31 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 14:18:58 +1100 Subject: [PATCH 7/8] Fixes up webbootmanager with new cachehelper updates. --- src/Umbraco.Web/WebBootManager.cs | 9 +++++++++ src/umbraco.businesslogic/ui.cs | 19 ++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index a75b9a2bcd..5be034c791 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -148,6 +148,15 @@ namespace Umbraco.Web return this; } + /// + /// Creates the application cache based on the HttpRuntime cache + /// + protected override void CreateApplicationCache() + { + //create a web-based cache helper + ApplicationCache = new CacheHelper(); + } + /// /// Creates the routes /// diff --git a/src/umbraco.businesslogic/ui.cs b/src/umbraco.businesslogic/ui.cs index 6eb950495e..c22984df11 100644 --- a/src/umbraco.businesslogic/ui.cs +++ b/src/umbraco.businesslogic/ui.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Text.RegularExpressions; using System.Threading; using System.Web; @@ -248,11 +249,14 @@ namespace umbraco { var cacheKey = "uitext_" + language; - return ApplicationContext.Current.ApplicationCache.GetCacheItem( - cacheKey, - CacheItemPriority.Default, - new CacheDependency(IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml")), - () => + var file = IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml"); + if (File.Exists(file)) + { + return ApplicationContext.Current.ApplicationCache.GetCacheItem( + cacheKey, + CacheItemPriority.Default, + new CacheDependency(IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml")), + () => { using (var langReader = new XmlTextReader(IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml"))) { @@ -269,6 +273,11 @@ namespace umbraco } } }); + } + else + { + return null; + } } From 060e584503039ac020f79602ae9565d7ee0531bb Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Dec 2013 15:57:59 +1100 Subject: [PATCH 8/8] backported IContentService updates --- src/Umbraco.Core/Models/Rdbms/NodeDto.cs | 4 +- src/Umbraco.Core/Publishing/PublishStatus.cs | 6 +- .../Publishing/PublishStatusType.cs | 2 +- src/Umbraco.Core/Services/ContentService.cs | 69 +++++- src/Umbraco.Core/Services/IContentService.cs | 42 +++- .../Services/ContentServiceTests.cs | 225 +++++++++--------- 6 files changed, 220 insertions(+), 128 deletions(-) diff --git a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs index ccbe9a1639..a89879d8b5 100644 --- a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs @@ -9,8 +9,10 @@ namespace Umbraco.Core.Models.Rdbms [ExplicitColumns] internal class NodeDto { + public const int NodeIdSeed = 1050; + [Column("id")] - [PrimaryKeyColumn(Name = "PK_structure", IdentitySeed = 1045)] + [PrimaryKeyColumn(Name = "PK_structure", IdentitySeed = NodeIdSeed)] public int NodeId { get; set; } [Column("trashed")] diff --git a/src/Umbraco.Core/Publishing/PublishStatus.cs b/src/Umbraco.Core/Publishing/PublishStatus.cs index aee9a1fafe..519046f5f9 100644 --- a/src/Umbraco.Core/Publishing/PublishStatus.cs +++ b/src/Umbraco.Core/Publishing/PublishStatus.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Publishing /// /// The result of publishing a content item /// - internal class PublishStatus + public class PublishStatus { public PublishStatus() { @@ -26,9 +26,9 @@ namespace Umbraco.Core.Publishing ///
public PublishStatus(IContent content) : this(content, PublishStatusType.Success) - { + { } - + public IContent ContentItem { get; private set; } public PublishStatusType StatusType { get; internal set; } diff --git a/src/Umbraco.Core/Publishing/PublishStatusType.cs b/src/Umbraco.Core/Publishing/PublishStatusType.cs index c3cb76e245..0d9ffcfa02 100644 --- a/src/Umbraco.Core/Publishing/PublishStatusType.cs +++ b/src/Umbraco.Core/Publishing/PublishStatusType.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Publishing /// /// Anything less than 10 = Success! /// - internal enum PublishStatusType + public enum PublishStatusType { /// /// The publishing was successful. diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 9d80369e93..7c54af38b3 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -220,6 +220,19 @@ namespace Umbraco.Core.Services } } + /// + /// Gets an object by Id + /// + /// Ids of the Content to retrieve + /// + public IEnumerable GetByIds(IEnumerable ids) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + return repository.GetAll(ids.ToArray()); + } + } + /// /// Gets an object by its 'UniqueId' /// @@ -597,10 +610,22 @@ namespace Umbraco.Core.Services /// The to publish /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False + [Obsolete("Use PublishWithStatus instead, that method will provide more detailed information on the outcome")] public bool Publish(IContent content, int userId = 0) { var result = SaveAndPublishDo(content, userId); - return result.Success; + return result.Success; + } + + /// + /// Publishes a single object + /// + /// The to publish + /// Optional Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False + public Attempt PublishWithStatus(IContent content, int userId = 0) + { + return SaveAndPublishDo(content, userId); } /// @@ -609,16 +634,29 @@ namespace Umbraco.Core.Services /// The to publish along with its children /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False + [Obsolete("Use PublishWithChildrenWithStatus instead, that method will provide more detailed information on the outcome and also allows the includeUnpublished flag")] public bool PublishWithChildren(IContent content, int userId = 0) { var result = PublishWithChildrenDo(content, userId, true); - + //This used to just return false only when the parent content failed, otherwise would always return true so we'll // do the same thing for the moment - if (!result.Any(x => x.Result.ContentItem.Id == content.Id)) - return false; + if (!result.Any(x => x.Result.ContentItem.Id == content.Id)) + return false; - return result.Single(x => x.Result.ContentItem.Id == content.Id).Success; + return result.Single(x => x.Result.ContentItem.Id == content.Id).Success; + } + + /// + /// Publishes a object and all its children + /// + /// The to publish along with its children + /// Optional Id of the User issueing the publishing + /// set to true if you want to also publish children that are currently unpublished + /// True if publishing succeeded, otherwise False + public IEnumerable> PublishWithChildrenWithStatus(IContent content, int userId = 0, bool includeUnpublished = false) + { + return PublishWithChildrenDo(content, userId, includeUnpublished); } /// @@ -639,10 +677,23 @@ namespace Umbraco.Core.Services /// Optional Id of the User issueing the publishing /// Optional boolean indicating whether or not to raise save events. /// True if publishing succeeded, otherwise False + [Obsolete("Use SaveAndPublishWithStatus instead, that method will provide more detailed information on the outcome")] public bool SaveAndPublish(IContent content, int userId = 0, bool raiseEvents = true) { var result = SaveAndPublishDo(content, userId, raiseEvents); - return result.Success; + return result.Success; + } + + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Optional Id of the User issueing the publishing + /// Optional boolean indicating whether or not to raise save events. + /// True if publishing succeeded, otherwise False + public Attempt SaveAndPublishWithStatus(IContent content, int userId = 0, bool raiseEvents = true) + { + return SaveAndPublishDo(content, userId, raiseEvents); } /// @@ -1160,18 +1211,20 @@ namespace Umbraco.Core.Services /// The to send to publication /// Optional Id of the User issueing the send to publication /// True if sending publication was succesfull otherwise false - internal bool SendToPublication(IContent content, int userId = 0) + public bool SendToPublication(IContent content, int userId = 0) { if (SendingToPublish.IsRaisedEventCancelled(new SendToPublishEventArgs(content), this)) return false; - //TODO: Do some stuff here.. RunActionHandlers + //TODO: Do some stuff here.. ... what is it supposed to do ? + // pretty sure all that legacy stuff did was raise an event? and we no longer have IActionHandlers so no worries there. SentToPublish.RaiseEvent(new SendToPublishEventArgs(content, false), this); Audit.Add(AuditTypes.SendToPublish, "Send to Publish performed by user", content.WriterId, content.Id); + //TODO: will this ever be false?? return true; } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 6525a9d825..0a19deda6e 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Publishing; namespace Umbraco.Core.Services { @@ -9,6 +11,11 @@ namespace Umbraco.Core.Services /// public interface IContentService : IService { + + bool SendToPublication(IContent content, int userId = 0); + + IEnumerable GetByIds(IEnumerable ids); + /// /// Creates an object using the alias of the /// that this Content should based on. @@ -121,7 +128,7 @@ namespace Umbraco.Core.Services /// Collection of to save /// Optional Id of the User saving the Content /// Optional boolean indicating whether or not to raise events. - void Save(IEnumerable contents, int userId = 0, bool raiseEvents = true); + void Save(IEnumerable contents, int userId = 0, bool raiseEvents = true); /// /// Deletes all content of specified type. All children of deleted content is moved to Recycle Bin. @@ -242,16 +249,35 @@ namespace Umbraco.Core.Services /// The to publish /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False + [Obsolete("Use PublishWithStatus instead, that method will provide more detailed information on the outcome")] bool Publish(IContent content, int userId = 0); + /// + /// Publishes a single object + /// + /// The to publish + /// Optional Id of the User issueing the publishing + /// The published status attempt + Attempt PublishWithStatus(IContent content, int userId = 0); + /// /// Publishes a object and all its children /// /// The to publish along with its children /// Optional Id of the User issueing the publishing /// True if publishing succeeded, otherwise False + [Obsolete("Use PublishWithChildrenWithStatus instead, that method will provide more detailed information on the outcome and also allows the includeUnpublished flag")] bool PublishWithChildren(IContent content, int userId = 0); + /// + /// Publishes a object and all its children + /// + /// The to publish along with its children + /// Optional Id of the User issueing the publishing + /// + /// The list of statuses for all published items + IEnumerable> PublishWithChildrenWithStatus(IContent content, int userId = 0, bool includeUnpublished = false); + /// /// UnPublishes a single object /// @@ -267,8 +293,18 @@ namespace Umbraco.Core.Services /// Optional Id of the User issueing the publishing /// Optional boolean indicating whether or not to raise save events. /// True if publishing succeeded, otherwise False + [Obsolete("Use SaveAndPublishWithStatus instead, that method will provide more detailed information on the outcome")] bool SaveAndPublish(IContent content, int userId = 0, bool raiseEvents = true); + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Optional Id of the User issueing the publishing + /// Optional boolean indicating whether or not to raise save events. + /// True if publishing succeeded, otherwise False + Attempt SaveAndPublishWithStatus(IContent content, int userId = 0, bool raiseEvents = true); + /// /// Permanently deletes an object. /// @@ -311,6 +347,8 @@ namespace Umbraco.Core.Services /// to retrieve ancestors for /// An Enumerable list of objects IEnumerable GetAncestors(IContent content); + + /// /// Sorts a collection of objects by updating the SortOrder according /// to the ordering of items in the passed in . /// @@ -337,6 +375,8 @@ namespace Umbraco.Core.Services /// to retrieve the parent from /// Parent object IContent GetParent(IContent content); + + /// /// Creates and saves an object using the alias of the /// that this Content should based on. /// diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 047fc202ed..1f693509a3 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -5,6 +5,7 @@ using System.Threading; using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; @@ -24,21 +25,21 @@ namespace Umbraco.Tests.Services [SetUp] public override void Initialize() { - base.Initialize(); + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } //TODO Add test to verify there is only ONE newest document/content in cmsDocument table after updating. //TODO Add test to delete specific version (with and without deleting prior versions) and versions by date. - - [Test] - public void Can_Remove_Property_Type() - { + + [Test] + public void Can_Remove_Property_Type() + { // Arrange var contentService = ServiceContext.ContentService; @@ -48,9 +49,9 @@ namespace Umbraco.Tests.Services // Assert Assert.That(content, Is.Not.Null); Assert.That(content.HasIdentity, Is.False); - } + } - [Test] + [Test] public void Can_Create_Content() { // Arrange @@ -63,7 +64,7 @@ namespace Umbraco.Tests.Services Assert.That(content, Is.Not.Null); Assert.That(content.HasIdentity, Is.False); } - + [Test] public void Can_Create_Content_Without_Explicitly_Set_User() { @@ -83,12 +84,12 @@ namespace Umbraco.Tests.Services public void Can_Save_New_Content_With_Explicit_User() { var user = new User(ServiceContext.UserService.GetUserTypeByAlias("admin")) - { - Name = "Test", - Email = "test@test.com", - Username = "test", - Password = "test" - }; + { + Name = "Test", + Email = "test@test.com", + Username = "test", + Password = "test" + }; ServiceContext.UserService.SaveUser(user); var content = new Content("Test", -1, ServiceContext.ContentTypeService.GetContentType("umbTextpage")); @@ -117,11 +118,11 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; // Act - var content = contentService.GetById(1046); + var content = contentService.GetById(NodeDto.NodeIdSeed + 1); // Assert Assert.That(content, Is.Not.Null); - Assert.That(content.Id, Is.EqualTo(1046)); + Assert.That(content.Id, Is.EqualTo(NodeDto.NodeIdSeed + 1)); } [Test] @@ -135,7 +136,7 @@ namespace Umbraco.Tests.Services // Assert Assert.That(content, Is.Not.Null); - Assert.That(content.Id, Is.EqualTo(1046)); + Assert.That(content.Id, Is.EqualTo(NodeDto.NodeIdSeed + 1)); } [Test] @@ -160,7 +161,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; // Act - var contents = contentService.GetChildren(1046); + var contents = contentService.GetChildren(NodeDto.NodeIdSeed + 1); // Assert Assert.That(contents, Is.Not.Null); @@ -177,7 +178,7 @@ namespace Umbraco.Tests.Services contentService.Save(hierarchy, 0); // Act - var contents = contentService.GetDescendants(1046); + var contents = contentService.GetDescendants(NodeDto.NodeIdSeed + 1); // Assert Assert.That(contents, Is.Not.Null); @@ -190,15 +191,15 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var parent = ServiceContext.ContentService.GetById(1046); + var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); ServiceContext.ContentService.Publish(parent);//Publishing root, so Text Page 2 can be updated. - var subpage2 = contentService.GetById(1048); + var subpage2 = contentService.GetById(NodeDto.NodeIdSeed + 3); subpage2.Name = "Text Page 2 Updated"; subpage2.SetValue("author", "Jane Doe"); contentService.SaveAndPublish(subpage2, 0);//NOTE New versions are only added between publish-state-changed, so publishing to ensure addition version. // Act - var versions = contentService.GetVersions(1048); + var versions = contentService.GetVersions(NodeDto.NodeIdSeed + 3); // Assert Assert.That(versions.Any(), Is.True); @@ -225,9 +226,9 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var root = contentService.GetById(1046); + var root = contentService.GetById(NodeDto.NodeIdSeed + 1); contentService.SaveAndPublish(root); - var content = contentService.GetById(1048); + var content = contentService.GetById(NodeDto.NodeIdSeed + 3); content.ExpireDate = DateTime.Now.AddSeconds(1); contentService.SaveAndPublish(content); @@ -278,7 +279,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1046); + var content = contentService.GetById(NodeDto.NodeIdSeed + 1); bool published = contentService.Publish(content, 0); // Act @@ -304,24 +305,24 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var published = contentService.RePublishAll(0); - var content = contentService.GetById(1046); + var content = contentService.GetById(NodeDto.NodeIdSeed + 1); // Act bool unpublished = contentService.UnPublish(content, 0); - var children = contentService.GetChildren(1046); + var children = contentService.GetChildren(NodeDto.NodeIdSeed + 1); // Assert Assert.That(published, Is.True);//Verify that everything was published - - //Verify that content with Id 1046 was unpublished + + //Verify that content with Id (NodeDto.NodeIdSeed + 1) was unpublished Assert.That(unpublished, Is.True); Assert.That(content.Published, Is.False); //Verify that all children was unpublished Assert.That(children.Any(x => x.Published), Is.False); - Assert.That(children.First(x => x.Id == 1047).Published, Is.False);//Released 5 mins ago, but should be unpublished - Assert.That(children.First(x => x.Id == 1047).ReleaseDate.HasValue, Is.False);//Verify that the release date has been removed - Assert.That(children.First(x => x.Id == 1048).Published, Is.False);//Expired 5 mins ago, so isn't be published + Assert.That(children.First(x => x.Id == NodeDto.NodeIdSeed + 2).Published, Is.False);//Released 5 mins ago, but should be unpublished + Assert.That(children.First(x => x.Id == NodeDto.NodeIdSeed + 2).ReleaseDate.HasValue, Is.False);//Verify that the release date has been removed + Assert.That(children.First(x => x.Id == NodeDto.NodeIdSeed + 3).Published, Is.False);//Expired 5 mins ago, so isn't be published } [Test] @@ -337,7 +338,7 @@ namespace Umbraco.Tests.Services var allContent = rootContent.Concat(rootContent.SelectMany(x => x.Descendants())); //for testing we need to clear out the contentXml table so we can see if it worked var provider = new PetaPocoUnitOfWorkProvider(); - var uow = provider.GetUnitOfWork(); + var uow = provider.GetUnitOfWork(); using (RepositoryResolver.Current.ResolveByType(uow)) { uow.Database.TruncateTable("cmsContentXml"); @@ -345,7 +346,7 @@ namespace Umbraco.Tests.Services //for this test we are also going to save a revision for a content item that is not published, this is to ensure //that it's published version still makes it into the cmsContentXml table! contentService.Save(allContent.Last()); - + // Act var published = contentService.RePublishAll(0); @@ -382,7 +383,7 @@ namespace Umbraco.Tests.Services // Act - contentService.RePublishAll(new int[]{allContent.Last().ContentTypeId}); + contentService.RePublishAll(new int[] { allContent.Last().ContentTypeId }); // Assert uow = provider.GetUnitOfWork(); @@ -397,7 +398,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1046); + var content = contentService.GetById(NodeDto.NodeIdSeed + 1); // Act bool published = contentService.Publish(content, 0); @@ -416,12 +417,12 @@ namespace Umbraco.Tests.Services var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true); contentTypeService.Save(contentType); - Content content = MockedContent.CreateSimpleContent(contentType, "Invalid Content", 1046); + Content content = MockedContent.CreateSimpleContent(contentType, "Invalid Content", NodeDto.NodeIdSeed + 1); content.SetValue("author", string.Empty); contentService.Save(content, 0); // Act - var parent = contentService.GetById(1046); + var parent = contentService.GetById(NodeDto.NodeIdSeed + 1); bool parentPublished = contentService.Publish(parent, 0); bool published = contentService.Publish(content, 0); @@ -438,16 +439,16 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1046); + var content = contentService.GetById(NodeDto.NodeIdSeed + 1); // Act bool published = contentService.PublishWithChildren(content, 0); - var children = contentService.GetChildren(1046); + var children = contentService.GetChildren(NodeDto.NodeIdSeed + 1); // Assert Assert.That(published, Is.True);//Nothing was cancelled, so should be true Assert.That(content.Published, Is.True);//No restrictions, so should be published - Assert.That(children.First(x => x.Id == 1047).Published, Is.True);//Released 5 mins ago, so should be published + Assert.That(children.First(x => x.Id == NodeDto.NodeIdSeed + 2).Published, Is.True);//Released 5 mins ago, so should be published } [Test] @@ -455,11 +456,11 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1048); //This Content expired 5min ago + var content = contentService.GetById(NodeDto.NodeIdSeed + 3); //This Content expired 5min ago content.ExpireDate = DateTime.Now.AddMinutes(-5); contentService.Save(content); - var parent = contentService.GetById(1046); + var parent = contentService.GetById(NodeDto.NodeIdSeed + 1); bool parentPublished = contentService.Publish(parent, 0);//Publish root Home node to enable publishing of '1048' // Act @@ -476,11 +477,11 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1047); + var content = contentService.GetById(NodeDto.NodeIdSeed + 2); content.ReleaseDate = DateTime.Now.AddHours(2); contentService.Save(content, 0); - var parent = contentService.GetById(1046); + var parent = contentService.GetById(NodeDto.NodeIdSeed + 1); bool parentPublished = contentService.Publish(parent, 0);//Publish root Home node to enable publishing of '1048' // Act @@ -497,7 +498,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.CreateContent("Subpage with Unpublisehed Parent", 1046, "umbTextpage", 0); + var content = contentService.CreateContent("Subpage with Unpublisehed Parent", NodeDto.NodeIdSeed + 1, "umbTextpage", 0); contentService.Save(content, 0); // Act @@ -513,7 +514,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1049); + var content = contentService.GetById(NodeDto.NodeIdSeed + 4); // Act bool published = contentService.Publish(content, 0); @@ -529,7 +530,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.CreateContent("Home US", - 1, "umbTextpage", 0); + var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0); content.SetValue("author", "Barack Obama"); // Act @@ -546,9 +547,9 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var root = contentService.GetById(1046); + var root = contentService.GetById(NodeDto.NodeIdSeed + 1); var rootPublished = contentService.Publish(root); - var content = contentService.GetById(1048); + var content = contentService.GetById(NodeDto.NodeIdSeed + 3); content.Properties["title"].Value = content.Properties["title"].Value + " Published"; bool published = contentService.SaveAndPublish(content); @@ -560,7 +561,7 @@ namespace Umbraco.Tests.Services var savedVersion = content.Version; // Act - var publishedDescendants = ((ContentService) contentService).GetPublishedDescendants(root); + var publishedDescendants = ((ContentService)contentService).GetPublishedDescendants(root); // Assert Assert.That(rootPublished, Is.True); @@ -579,7 +580,7 @@ namespace Umbraco.Tests.Services Assert.That(savedContentVersion.Properties["title"].Value, Contains.Substring("Saved")); //Ensure that the latest version of the content is the saved and not-yet-published one - var currentContent = contentService.GetById(1048); + var currentContent = contentService.GetById(NodeDto.NodeIdSeed + 3); Assert.That(currentContent.Published, Is.False); Assert.That(currentContent.Properties["title"].Value, Contains.Substring("Saved")); Assert.That(currentContent.Version, Is.EqualTo(savedVersion)); @@ -590,7 +591,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.CreateContent("Home US", - 1, "umbTextpage", 0); + var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0); content.SetValue("author", "Barack Obama"); // Act @@ -608,9 +609,9 @@ namespace Umbraco.Tests.Services var contentTypeService = ServiceContext.ContentTypeService; var contentType = contentTypeService.GetContentType("umbTextpage"); - Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Subpage 1", 1047); - Content subpage2 = MockedContent.CreateSimpleContent(contentType, "Text Subpage 2", 1047); - var list = new List {subpage, subpage2}; + Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Subpage 1", NodeDto.NodeIdSeed + 2); + Content subpage2 = MockedContent.CreateSimpleContent(contentType, "Text Subpage 2", NodeDto.NodeIdSeed + 2); + var list = new List { subpage, subpage2 }; // Act contentService.Save(list, 0); @@ -630,9 +631,9 @@ namespace Umbraco.Tests.Services contentService.Save(hierarchy, 0); Assert.That(hierarchy.Any(), Is.True); - Assert.That(hierarchy.Any(x => x.HasIdentity == false), Is.False); - //all parent id's should be ok, they are lazy and if they equal zero an exception will be thrown - Assert.DoesNotThrow(() => hierarchy.Any(x => x.ParentId != 0)); + Assert.That(hierarchy.Any(x => x.HasIdentity == false), Is.False); + //all parent id's should be ok, they are lazy and if they equal zero an exception will be thrown + Assert.DoesNotThrow(() => hierarchy.Any(x => x.ParentId != 0)); } @@ -659,11 +660,11 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1049); + var content = contentService.GetById(NodeDto.NodeIdSeed + 4); // Act contentService.Delete(content, 0); - var deleted = contentService.GetById(1049); + var deleted = contentService.GetById(NodeDto.NodeIdSeed + 4); // Assert Assert.That(deleted, Is.Null); @@ -674,7 +675,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1048); + var content = contentService.GetById(NodeDto.NodeIdSeed + 3); // Act contentService.MoveToRecycleBin(content, 0); @@ -690,10 +691,10 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); - Content subsubpage = MockedContent.CreateSimpleContent(contentType, "Text Page 3", 1047); + Content subsubpage = MockedContent.CreateSimpleContent(contentType, "Text Page 3", NodeDto.NodeIdSeed + 2); contentService.Save(subsubpage, 0); - var content = contentService.GetById(1046); + var content = contentService.GetById(NodeDto.NodeIdSeed + 1); // Act contentService.MoveToRecycleBin(content, 0); @@ -731,13 +732,13 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1049); + var content = contentService.GetById(NodeDto.NodeIdSeed + 4); // Act - moving out of recycle bin - contentService.Move(content, 1046, 0); + contentService.Move(content, NodeDto.NodeIdSeed + 1, 0); // Assert - Assert.That(content.ParentId, Is.EqualTo(1046)); + Assert.That(content.ParentId, Is.EqualTo(NodeDto.NodeIdSeed + 1)); Assert.That(content.Trashed, Is.False); Assert.That(content.Published, Is.False); } @@ -747,11 +748,11 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var temp = contentService.GetById(1048); + var temp = contentService.GetById(NodeDto.NodeIdSeed + 3); // Act var copy = contentService.Copy(temp, temp.ParentId, false, 0); - var content = contentService.GetById(1048); + var content = contentService.GetById(NodeDto.NodeIdSeed + 3); // Assert Assert.That(copy, Is.Not.Null); @@ -764,7 +765,7 @@ namespace Umbraco.Tests.Services } //Assert.AreNotEqual(content.Name, copy.Name); } - + [Test, NUnit.Framework.Ignore] public void Can_Send_To_Publication() { } @@ -774,9 +775,9 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var parent = ServiceContext.ContentService.GetById(1046); + var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); ServiceContext.ContentService.Publish(parent);//Publishing root, so Text Page 2 can be updated. - var subpage2 = contentService.GetById(1048); + var subpage2 = contentService.GetById(NodeDto.NodeIdSeed + 3); var version = subpage2.Version; var nameBeforeRollback = subpage2.Name; subpage2.Name = "Text Page 2 Updated"; @@ -784,7 +785,7 @@ namespace Umbraco.Tests.Services contentService.SaveAndPublish(subpage2, 0);//Saving and publishing, so a new version is created // Act - var rollback = contentService.Rollback(1048, version, 0); + var rollback = contentService.Rollback(NodeDto.NodeIdSeed + 3, version, 0); // Assert Assert.That(rollback, Is.Not.Null); @@ -795,14 +796,14 @@ namespace Umbraco.Tests.Services [Test] public void Can_Save_Lazy_Content() - { - var unitOfWork = PetaPocoUnitOfWorkProvider.CreateUnitOfWork(); + { + var unitOfWork = PetaPocoUnitOfWorkProvider.CreateUnitOfWork(); var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); - var root = ServiceContext.ContentService.GetById(1046); + var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); var c = new Lazy(() => MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page", root.Id)); var c2 = new Lazy(() => MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage", c.Value.Id)); - var list = new List> {c, c2}; + var list = new List> { c, c2 }; var repository = RepositoryResolver.Current.ResolveByType(unitOfWork); foreach (var content in list) @@ -826,14 +827,14 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1046); + var content = contentService.GetById(NodeDto.NodeIdSeed + 1); bool published = contentService.PublishWithChildren(content, 0); - var homepage = contentService.GetById(1046); + var homepage = contentService.GetById(NodeDto.NodeIdSeed + 1); homepage.Name = "Homepage"; ServiceContext.ContentService.Save(homepage); // Act - bool hasPublishedVersion = ServiceContext.ContentService.HasPublishedVersion(1046); + bool hasPublishedVersion = ServiceContext.ContentService.HasPublishedVersion(NodeDto.NodeIdSeed + 1); // Assert Assert.That(published, Is.True); @@ -841,20 +842,20 @@ namespace Umbraco.Tests.Services Assert.That(hasPublishedVersion, Is.True); } - [Test] - public void Can_Verify_Property_Types_On_Content() - { + [Test] + public void Can_Verify_Property_Types_On_Content() + { // Arrange - var contentTypeService = ServiceContext.ContentTypeService; + var contentTypeService = ServiceContext.ContentTypeService; var contentType = MockedContentTypes.CreateAllTypesContentType("allDataTypes", "All DataTypes"); contentTypeService.Save(contentType); - var contentService = ServiceContext.ContentService; - var content = MockedContent.CreateAllTypesContent(contentType, "Random Content", -1); + var contentService = ServiceContext.ContentService; + var content = MockedContent.CreateAllTypesContent(contentType, "Random Content", -1); contentService.Save(content); - var id = content.Id; + var id = content.Id; // Act - var sut = contentService.GetById(id); + var sut = contentService.GetById(id); // Arrange Assert.That(sut.GetValue("isTrue"), Is.True); @@ -868,7 +869,7 @@ namespace Umbraco.Tests.Services //MCH: I'm guessing this is an issue because of the format the date is actually stored as, right? Cause we don't do any formatting when saving or loading Assert.That(sut.GetValue("dateTime").ToString("G"), Is.EqualTo(content.GetValue("dateTime").ToString("G"))); Assert.That(sut.GetValue("colorPicker"), Is.EqualTo("black")); - Assert.That(sut.GetValue("folderBrowser"), Is.Empty); + Assert.That(sut.GetValue("folderBrowser"), Is.Empty); Assert.That(sut.GetValue("ddlMultiple"), Is.EqualTo("1234,1235")); Assert.That(sut.GetValue("rbList"), Is.EqualTo("random")); Assert.That(sut.GetValue("date").ToString("G"), Is.EqualTo(content.GetValue("date").ToString("G"))); @@ -877,40 +878,36 @@ namespace Umbraco.Tests.Services Assert.That(sut.GetValue("contentPicker"), Is.EqualTo(1090)); Assert.That(sut.GetValue("mediaPicker"), Is.EqualTo(1091)); Assert.That(sut.GetValue("memberPicker"), Is.EqualTo(1092)); - Assert.That(sut.GetValue("simpleEditor"), Is.EqualTo("This is simply edited")); - Assert.That(sut.GetValue("ultimatePicker"), Is.EqualTo("1234,1235")); Assert.That(sut.GetValue("relatedLinks"), Is.EqualTo("")); Assert.That(sut.GetValue("tags"), Is.EqualTo("this,is,tags")); - Assert.That(sut.GetValue("macroContainer"), Is.Empty); - Assert.That(sut.GetValue("imgCropper"), Is.Empty); - } + } - [Test] - public void Can_Delete_Previous_Versions_Not_Latest() - { + [Test] + public void Can_Delete_Previous_Versions_Not_Latest() + { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1049); - var version = content.Version; + var content = contentService.GetById(NodeDto.NodeIdSeed + 4); + var version = content.Version; - // Act - contentService.DeleteVersion(1049, version, true, 0); - var sut = contentService.GetById(1049); + // Act + contentService.DeleteVersion(NodeDto.NodeIdSeed + 4, version, true, 0); + var sut = contentService.GetById(NodeDto.NodeIdSeed + 4); // Assert Assert.That(sut.Version, Is.EqualTo(version)); - } + } private IEnumerable CreateContentHierarchy() { var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); - var root = ServiceContext.ContentService.GetById(1046); + var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); - var list = new List(); + var list = new List(); for (int i = 0; i < 10; i++) { - var content = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page " + i, root); + var content = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page " + i, root); list.Add(content); list.AddRange(CreateChildrenOf(contentType, content, 4)); @@ -921,12 +918,12 @@ namespace Umbraco.Tests.Services return list; } - private IEnumerable CreateChildrenOf(IContentType contentType, IContent content, int depth) + private IEnumerable CreateChildrenOf(IContentType contentType, IContent content, int depth) { var list = new List(); for (int i = 0; i < depth; i++) { - var c = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage " + i, content); + var c = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage " + i, content); list.Add(c); Console.WriteLine("Created: 'Hierarchy Simple Text Subpage {0}' - Depth: {1}", i, depth);