@ -1,12 +1,13 @@
using MongoDB.Driver ;
using MongoDB.Driver.Linq ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Linq.Expressions ;
using System.Threading ;
using System.Threading.Tasks ;
using MongoDB.Driver ;
using MongoDB.Driver.Linq ;
using Volo.Abp.Auditing ;
using Volo.Abp.Data ;
using Volo.Abp.Domain.Entities ;
using Volo.Abp.Domain.Entities.Events ;
using Volo.Abp.EventBus ;
@ -97,11 +98,18 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
AsyncHelper . RunSync ( ( ) = > TriggerDomainEventsAsync ( entity ) ) ;
Collection . ReplaceOne (
CreateEntityFilter ( entity ) ,
var oldConcurrencyStamp = SetNewConcurrencyStamp ( entity ) ;
var result = Collection . ReplaceOne (
CreateEntityFilter ( entity , true , oldConcurrencyStamp ) ,
entity
) ;
if ( result . MatchedCount < = 0 )
{
ThrowOptimisticConcurrencyException ( ) ;
}
return entity ;
}
@ -124,32 +132,50 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
await TriggerDomainEventsAsync ( entity ) ;
await Collection . ReplaceOneAsync (
CreateEntityFilter ( entity ) ,
var oldConcurrencyStamp = SetNewConcurrencyStamp ( entity ) ;
var result = await Collection . ReplaceOneAsync (
CreateEntityFilter ( entity , true , oldConcurrencyStamp ) ,
entity ,
cancellationToken : GetCancellationToken ( cancellationToken )
) ;
if ( result . MatchedCount < = 0 )
{
ThrowOptimisticConcurrencyException ( ) ;
}
return entity ;
}
public override void Delete ( TEntity entity , bool autoSave = false )
{
AsyncHelper . RunSync ( ( ) = > ApplyAbpConceptsForDeletedEntityAsync ( entity ) ) ;
var oldConcurrencyStamp = SetNewConcurrencyStamp ( entity ) ;
if ( entity is ISoftDelete softDeleteEntity )
{
softDeleteEntity . IsDeleted = true ;
Collection . ReplaceOne (
CreateEntityFilter ( entity ) ,
var result = Collection . ReplaceOne (
CreateEntityFilter ( entity , true , oldConcurrencyStamp ) ,
entity
) ;
if ( result . MatchedCount < = 0 )
{
ThrowOptimisticConcurrencyException ( ) ;
}
}
else
{
Collection . DeleteOne (
CreateEntityFilter ( entity )
var result = Collection . DeleteOne (
CreateEntityFilter ( entity , true , oldConcurrencyStamp )
) ;
if ( result . DeletedCount < = 0 )
{
ThrowOptimisticConcurrencyException ( ) ;
}
}
}
@ -159,22 +185,33 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
CancellationToken cancellationToken = default )
{
await ApplyAbpConceptsForDeletedEntityAsync ( entity ) ;
var oldConcurrencyStamp = SetNewConcurrencyStamp ( entity ) ;
if ( entity is ISoftDelete softDeleteEntity )
{
softDeleteEntity . IsDeleted = true ;
await Collection . ReplaceOneAsync (
CreateEntityFilter ( entity ) ,
var result = await Collection . ReplaceOneAsync (
CreateEntityFilter ( entity , true , oldConcurrencyStamp ) ,
entity ,
cancellationToken : GetCancellationToken ( cancellationToken )
) ;
if ( result . MatchedCount < = 0 )
{
ThrowOptimisticConcurrencyException ( ) ;
}
}
else
{
await Collection . DeleteOneAsync (
CreateEntityFilter ( entity ) ,
var result = await Collection . DeleteOneAsync (
CreateEntityFilter ( entity , true , oldConcurrencyStamp ) ,
GetCancellationToken ( cancellationToken )
) ;
if ( result . DeletedCount < = 0 )
{
ThrowOptimisticConcurrencyException ( ) ;
}
}
}
@ -239,7 +276,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
) ;
}
protected virtual FilterDefinition < TEntity > CreateEntityFilter ( TEntity entity )
protected virtual FilterDefinition < TEntity > CreateEntityFilter ( TEntity entity , bool withConcurrencyStamp = false , string concurrencyStamp = null )
{
throw new NotImplementedException (
$"{nameof(CreateEntityFilter)} is not implemented for MongoDB by default. It should be overrided and implemented by the deriving class!"
@ -332,6 +369,28 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
generatesDomainEventsEntity . ClearDistributedEvents ( ) ;
}
}
/// <summary>
/// Sets a new <see cref="IHasConcurrencyStamp.ConcurrencyStamp"/> value
/// if given entity implements <see cref="IHasConcurrencyStamp"/> interface.
/// Returns the old <see cref="IHasConcurrencyStamp.ConcurrencyStamp"/> value.
/// </summary>
protected virtual string SetNewConcurrencyStamp ( TEntity entity )
{
if ( ! ( entity is IHasConcurrencyStamp concurrencyStampEntity ) )
{
return null ;
}
var oldConcurrencyStamp = concurrencyStampEntity . ConcurrencyStamp ;
concurrencyStampEntity . ConcurrencyStamp = Guid . NewGuid ( ) . ToString ( "N" ) ;
return oldConcurrencyStamp ;
}
protected virtual void ThrowOptimisticConcurrencyException ( )
{
throw new AbpDbConcurrencyException ( "Database operation expected to affect 1 row but actually affected 0 row. Data may have been modified or deleted since entities were loaded. This exception has been thrown on optimistic concurrency check." ) ;
}
}
public class MongoDbRepository < TMongoDbContext , TEntity , TKey >
@ -390,7 +449,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
public virtual void Delete ( TKey id , bool autoSave = false )
{
Collection . DeleteOne ( CreateEntityFilter ( id ) ) ;
Collection . DeleteOne ( CreateEntityFilter ( id ) ) ; //TODO: How to handle concurrency stamp!
}
public virtual Task DeleteAsync (
@ -404,9 +463,22 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
) ;
}
protected override FilterDefinition < TEntity > CreateEntityFilter ( TEntity entity )
protected override FilterDefinition < TEntity > CreateEntityFilter ( TEntity entity , bool withConcurrencyStamp = false , string concurrencyStamp = null )
{
return Builders < TEntity > . Filter . Eq ( e = > e . Id , entity . Id ) ;
if ( ! withConcurrencyStamp | | ! ( entity is IHasConcurrencyStamp entityWithConcurrencyStamp ) )
{
return Builders < TEntity > . Filter . Eq ( e = > e . Id , entity . Id ) ;
}
if ( concurrencyStamp = = null )
{
concurrencyStamp = entityWithConcurrencyStamp . ConcurrencyStamp ;
}
return Builders < TEntity > . Filter . And (
Builders < TEntity > . Filter . Eq ( e = > e . Id , entity . Id ) ,
Builders < TEntity > . Filter . Eq ( e = > ( ( IHasConcurrencyStamp ) e ) . ConcurrencyStamp , concurrencyStamp )
) ;
}
protected virtual FilterDefinition < TEntity > CreateEntityFilter ( TKey id , bool applyFilters = false )