I am writing the "Who is online" function for my application and found that Membership.GetUser() does not update the last-activity date/time stamp for the user as expected. This make my application considers a user offline after he/she logs into the system after the number of minutes specified in the "userIsOnlineTimeWindow" property (even he/she is still working in the application). I have searched on web all day and found no clues on this problem.
Has anyone encountered the same kind of problem? Any help would be greatly appreciated.
Thanks a lot.
Membership.GetUser() calls Membership.GetUser(userName, true), which calls Provider.GetUser(userName, true); If this isn't updating your last activity time, then there's a problem with your membership provider.
However, if you call Membership.GetUser(userName) or Membership.GetUser(providerUserKey), the userIsOnline flag defaults to false, so your last activity time won't be updated. In this case, you should replace your calls with Membership.GetUser(userName, true) or Membership.GetUser(providerUserKey, true) in order to update the activity time.
RichardD, thanks for your reply. Actually, I am using the built-in SqlMembershipProvider except that I reconfigure it in the web.config file to relax the password requirement. Below is the configuration that I have made in web.config:
<membership defaultProvider="AspNetSqlMembershipProviderPasswordRelaxed" userIsOnlineTimeWindow="15">
<add name="AspNetSqlMembershipProviderPasswordRelaxed" type="System.Web.Security.SqlMembershipProvider" connectionStringName="AppConnectionString" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" applicationName="/" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" passwordAttemptWindow="10" passwordStrengthRegularExpression="" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="0"/>
I would be grateful if you could provide any further assistance. Thanks a lot.
Finally, I found the cause of the problem. It is because Membership.GetUser() won't update the last activity timestamp of the user in the data store. It will only update the last activity timestamp in the MembershipUser object that is returned. So, in order to update the last activity timestamp of the user in the data store, we have to call Membership.UpdateUser(user) by ourselves. I found this solution on http://www.sitepoint.com/forums/showthread.php?t=380584.
If you look at the two GetUser methods on the SqlMembershipProvider class, they call either "dbo.aspnet_Membership_GetUserByName" or "dbo.aspnet_Membership_GetUserByUserId", passing the userIsOnline flag to the @UpdateLastActivity parameter. However, there seems to be a bug in the "dbo.aspnet_Membership_GetUserByName" procedure, in that it attempts to update the user by ID without ever reading the ID.
To fix the problem, you can execute the following in your membership database:IF (EXISTS (SELECT name
FROM sysobjects WHERE (name = N'aspnet_Membership_GetUserByName')
AND (type = 'P')))
DROP PROCEDURE dbo.aspnet_Membership_GetUserByName
CREATE PROCEDURE dbo.aspnet_Membership_GetUserByName
@UpdateLastActivity bit = 0
DECLARE @UserId uniqueidentifier
SELECT TOP 1 @UserId = u.UserId
FROM dbo.aspnet_Applications a, dbo.aspnet_Users u, dbo.aspnet_Membership m
WHERE LOWER(@ApplicationName) = a.LoweredApplicationName AND u.ApplicationId = a.ApplicationId AND LOWER(@UserName) = u.LoweredUserName AND u.UserId = m.UserId If (@@ROWCOUNT = 0) -- Username not found RETURN -1
IF (@UpdateLastActivity = 1)
SET LastActivityDate = @CurrentTimeUtc
WHERE @UserId = UserId
SELECT m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
m.CreateDate, m.LastLoginDate, u.LastActivityDate, m.LastPasswordChangedDate,
u.UserId, m.IsLockedOut, m.LastLockoutDate
FROM dbo.aspnet_Users u, dbo.aspnet_Membership m
WHERE @UserId = u.UserId AND u.UserId = m.UserId
IF ( @@ROWCOUNT = 0 ) -- User ID not found RETURN -1
END GO GRANT EXECUTE ON dbo.aspnet_Membership_GetUserByName TO aspnet_Membership_BasicAccess
GRANT EXECUTE ON dbo.aspnet_Membership_GetUserByName TO aspnet_Membership_ReportingAccess
NB: I've kept this as close as possible to the style of the original. It could be improved by using inner joins instead of the old-style joins, but this could break in the unlikely event that you're using an older version of SQL Server.
I have reported this as a bug: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=306297