@@ -162,7 +162,7 @@ public override Task<IdentityResult> CreateAsync(
162
162
}
163
163
164
164
/// <inheritdoc />
165
- public override Task < IdentityResult > UpdateAsync (
165
+ public override async Task < IdentityResult > UpdateAsync (
166
166
MemberIdentityUser user ,
167
167
CancellationToken cancellationToken = default )
168
168
{
@@ -190,9 +190,21 @@ public override Task<IdentityResult> UpdateAsync(
190
190
var isLoginsPropertyDirty = user . IsPropertyDirty ( nameof ( MemberIdentityUser . Logins ) ) ;
191
191
var isTokensPropertyDirty = user . IsPropertyDirty ( nameof ( MemberIdentityUser . LoginTokens ) ) ;
192
192
193
- if ( UpdateMemberProperties ( found , user , out var updateRoles ) )
193
+ IReadOnlyList < string > propertiesUpdated = UpdateMemberProperties ( found , user , out var updateRoles ) ;
194
+
195
+ if ( propertiesUpdated . Count > 0 )
194
196
{
195
- _memberService . Save ( found ) ;
197
+ // As part of logging in members we update the last login date, and, if concurrent logins are disabled, the security stamp.
198
+ // If and only if we are updating these properties, we can avoid the overhead of a full save of the member with the associated
199
+ // locking, property updates, tag handling etc., and make a more efficient update.
200
+ if ( UpdatingOnlyLoginProperties ( propertiesUpdated ) )
201
+ {
202
+ await _memberService . UpdateLoginPropertiesAsync ( found ) ;
203
+ }
204
+ else
205
+ {
206
+ _memberService . Save ( found ) ;
207
+ }
196
208
197
209
if ( updateRoles )
198
210
{
@@ -223,15 +235,21 @@ public override Task<IdentityResult> UpdateAsync(
223
235
}
224
236
225
237
scope . Complete ( ) ;
226
- return Task . FromResult ( IdentityResult . Success ) ;
238
+ return IdentityResult . Success ;
227
239
}
228
240
catch ( Exception ex )
229
241
{
230
- return Task . FromResult (
231
- IdentityResult . Failed ( new IdentityError { Code = GenericIdentityErrorCode , Description = ex . Message } ) ) ;
242
+ return IdentityResult . Failed ( new IdentityError { Code = GenericIdentityErrorCode , Description = ex . Message } ) ;
232
243
}
233
244
}
234
245
246
+ private static bool UpdatingOnlyLoginProperties ( IReadOnlyList < string > propertiesUpdated )
247
+ {
248
+ string [ ] loginPropertyUpdates = [ nameof ( MemberIdentityUser . LastLoginDateUtc ) , nameof ( MemberIdentityUser . SecurityStamp ) ] ;
249
+ return ( propertiesUpdated . Count == 2 && propertiesUpdated . ContainsAll ( loginPropertyUpdates ) ) ||
250
+ ( propertiesUpdated . Count == 1 && propertiesUpdated . ContainsAny ( loginPropertyUpdates ) ) ;
251
+ }
252
+
235
253
/// <inheritdoc />
236
254
public override Task < IdentityResult > DeleteAsync (
237
255
MemberIdentityUser user ,
@@ -704,9 +722,9 @@ public override Task SetTokenAsync(MemberIdentityUser user, string loginProvider
704
722
return user ;
705
723
}
706
724
707
- private bool UpdateMemberProperties ( IMember member , MemberIdentityUser identityUser , out bool updateRoles )
725
+ private IReadOnlyList < string > UpdateMemberProperties ( IMember member , MemberIdentityUser identityUser , out bool updateRoles )
708
726
{
709
- var anythingChanged = false ;
727
+ var updatedProperties = new List < string > ( ) ;
710
728
updateRoles = false ;
711
729
712
730
// don't assign anything if nothing has changed as this will trigger the track changes of the model
@@ -715,7 +733,7 @@ private bool UpdateMemberProperties(IMember member, MemberIdentityUser identityU
715
733
|| ( identityUser . LastLoginDateUtc . HasValue &&
716
734
member . LastLoginDate ? . ToUniversalTime ( ) != identityUser . LastLoginDateUtc . Value ) )
717
735
{
718
- anythingChanged = true ;
736
+ updatedProperties . Add ( nameof ( MemberIdentityUser . LastLoginDateUtc ) ) ;
719
737
720
738
// if the LastLoginDate is being set to MinValue, don't convert it ToLocalTime
721
739
DateTime dt = identityUser . LastLoginDateUtc == DateTime . MinValue
@@ -729,14 +747,14 @@ private bool UpdateMemberProperties(IMember member, MemberIdentityUser identityU
729
747
|| ( identityUser . LastPasswordChangeDateUtc . HasValue && member . LastPasswordChangeDate ? . ToUniversalTime ( ) !=
730
748
identityUser . LastPasswordChangeDateUtc . Value ) )
731
749
{
732
- anythingChanged = true ;
750
+ updatedProperties . Add ( nameof ( MemberIdentityUser . LastPasswordChangeDateUtc ) ) ;
733
751
member . LastPasswordChangeDate = identityUser . LastPasswordChangeDateUtc ? . ToLocalTime ( ) ?? DateTime . Now ;
734
752
}
735
753
736
754
if ( identityUser . IsPropertyDirty ( nameof ( MemberIdentityUser . Comments ) )
737
755
&& member . Comments != identityUser . Comments && identityUser . Comments . IsNullOrWhiteSpace ( ) == false )
738
756
{
739
- anythingChanged = true ;
757
+ updatedProperties . Add ( nameof ( MemberIdentityUser . Comments ) ) ;
740
758
member . Comments = identityUser . Comments ;
741
759
}
742
760
@@ -746,34 +764,34 @@ private bool UpdateMemberProperties(IMember member, MemberIdentityUser identityU
746
764
|| ( ( member . EmailConfirmedDate . HasValue == false || member . EmailConfirmedDate . Value == default ) &&
747
765
identityUser . EmailConfirmed ) )
748
766
{
749
- anythingChanged = true ;
767
+ updatedProperties . Add ( nameof ( MemberIdentityUser . EmailConfirmed ) ) ;
750
768
member . EmailConfirmedDate = identityUser . EmailConfirmed ? DateTime . Now : null ;
751
769
}
752
770
753
771
if ( identityUser . IsPropertyDirty ( nameof ( MemberIdentityUser . Name ) )
754
772
&& member . Name != identityUser . Name && identityUser . Name . IsNullOrWhiteSpace ( ) == false )
755
773
{
756
- anythingChanged = true ;
774
+ updatedProperties . Add ( nameof ( MemberIdentityUser . Name ) ) ;
757
775
member . Name = identityUser . Name ?? string . Empty ;
758
776
}
759
777
760
778
if ( identityUser . IsPropertyDirty ( nameof ( MemberIdentityUser . Email ) )
761
779
&& member . Email != identityUser . Email && identityUser . Email . IsNullOrWhiteSpace ( ) == false )
762
780
{
763
- anythingChanged = true ;
781
+ updatedProperties . Add ( nameof ( MemberIdentityUser . Email ) ) ;
764
782
member . Email = identityUser . Email ! ;
765
783
}
766
784
767
785
if ( identityUser . IsPropertyDirty ( nameof ( MemberIdentityUser . AccessFailedCount ) )
768
786
&& member . FailedPasswordAttempts != identityUser . AccessFailedCount )
769
787
{
770
- anythingChanged = true ;
788
+ updatedProperties . Add ( nameof ( MemberIdentityUser . AccessFailedCount ) ) ;
771
789
member . FailedPasswordAttempts = identityUser . AccessFailedCount ;
772
790
}
773
791
774
792
if ( member . IsLockedOut != identityUser . IsLockedOut )
775
793
{
776
- anythingChanged = true ;
794
+ updatedProperties . Add ( nameof ( MemberIdentityUser . IsLockedOut ) ) ;
777
795
member . IsLockedOut = identityUser . IsLockedOut ;
778
796
779
797
if ( member . IsLockedOut )
@@ -785,48 +803,48 @@ private bool UpdateMemberProperties(IMember member, MemberIdentityUser identityU
785
803
786
804
if ( member . IsApproved != identityUser . IsApproved )
787
805
{
788
- anythingChanged = true ;
806
+ updatedProperties . Add ( nameof ( MemberIdentityUser . IsApproved ) ) ;
789
807
member . IsApproved = identityUser . IsApproved ;
790
808
}
791
809
792
810
if ( identityUser . IsPropertyDirty ( nameof ( MemberIdentityUser . UserName ) )
793
811
&& member . Username != identityUser . UserName && identityUser . UserName . IsNullOrWhiteSpace ( ) == false )
794
812
{
795
- anythingChanged = true ;
813
+ updatedProperties . Add ( nameof ( MemberIdentityUser . UserName ) ) ;
796
814
member . Username = identityUser . UserName ! ;
797
815
}
798
816
799
817
if ( identityUser . IsPropertyDirty ( nameof ( MemberIdentityUser . PasswordHash ) )
800
818
&& member . RawPasswordValue != identityUser . PasswordHash &&
801
819
identityUser . PasswordHash . IsNullOrWhiteSpace ( ) == false )
802
820
{
803
- anythingChanged = true ;
821
+ updatedProperties . Add ( nameof ( MemberIdentityUser . PasswordHash ) ) ;
804
822
member . RawPasswordValue = identityUser . PasswordHash ;
805
823
member . PasswordConfiguration = identityUser . PasswordConfig ;
806
824
}
807
825
808
826
if ( member . PasswordConfiguration != identityUser . PasswordConfig )
809
827
{
810
- anythingChanged = true ;
828
+ updatedProperties . Add ( nameof ( MemberIdentityUser . PasswordConfig ) ) ;
811
829
member . PasswordConfiguration = identityUser . PasswordConfig ;
812
830
}
813
831
814
832
if ( member . SecurityStamp != identityUser . SecurityStamp )
815
833
{
816
- anythingChanged = true ;
834
+ updatedProperties . Add ( nameof ( MemberIdentityUser . SecurityStamp ) ) ;
817
835
member . SecurityStamp = identityUser . SecurityStamp ;
818
836
}
819
837
820
838
if ( identityUser . IsPropertyDirty ( nameof ( MemberIdentityUser . Roles ) ) )
821
839
{
822
- anythingChanged = true ;
840
+ updatedProperties . Add ( nameof ( MemberIdentityUser . Roles ) ) ;
823
841
updateRoles = true ;
824
842
}
825
843
826
844
// reset all changes
827
845
identityUser . ResetDirtyProperties ( false ) ;
828
846
829
- return anythingChanged ;
847
+ return updatedProperties . AsReadOnly ( ) ;
830
848
}
831
849
832
850
/// <inheritdoc />
0 commit comments