Implement Delete and Bulk Delete
This commit is contained in:
parent
e9bfce5e4e
commit
fffc41cd6a
|
|
@ -3,7 +3,7 @@ import {LocationSegments, RouterHistory} from "@stencil/router";
|
|||
import * as collection from 'lodash/collection';
|
||||
import * as array from 'lodash/array';
|
||||
import {createElsaClient} from "../../../../services/elsa-client";
|
||||
import {OrderBy, PagedList, VersionOptions, WorkflowBlueprintSummary, WorkflowInstanceSummary, WorkflowStatus} from "../../../../models";
|
||||
import {OrderBy, PagedList, VersionOptions, WorkflowBlueprintSummary, WorkflowDefinitionSummary, WorkflowInstanceSummary, WorkflowStatus} from "../../../../models";
|
||||
import {DropdownButtonItem, DropdownButtonOrigin} from "../../../controls/elsa-dropdown-button/models";
|
||||
import {Map, parseQuery} from '../../../../utils/utils';
|
||||
|
||||
|
|
@ -32,10 +32,10 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
await this.loadWorkflowBlueprints();
|
||||
await this.loadWorkflowInstances();
|
||||
}
|
||||
|
||||
applyQueryString(queryString?: string){
|
||||
|
||||
applyQueryString(queryString?: string) {
|
||||
const query = parseQuery(queryString);
|
||||
|
||||
|
||||
this.selectedWorkflowId = query.workflow;
|
||||
this.selectedWorkflowStatus = query.status;
|
||||
this.selectedOrderBy = query.orderBy ?? OrderBy.Started;
|
||||
|
|
@ -52,7 +52,7 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
const elsaClient = this.createClient();
|
||||
const page = 1;
|
||||
const pageSize = 25;
|
||||
|
||||
|
||||
this.workflowInstances = await elsaClient.workflowInstancesApi.list(page, pageSize, this.selectedWorkflowId, this.selectedWorkflowStatus, this.selectedOrderBy);
|
||||
}
|
||||
|
||||
|
|
@ -100,25 +100,25 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
}
|
||||
|
||||
async routeChanged(e: LocationSegments) {
|
||||
if(!e.pathname.toLowerCase().indexOf('workflow-instances'))
|
||||
if (!e.pathname.toLowerCase().indexOf('workflow-instances'))
|
||||
return;
|
||||
|
||||
this.applyQueryString(e.search);
|
||||
await this.loadWorkflowInstances();
|
||||
}
|
||||
|
||||
onSelectAllCheckChange(e: Event){
|
||||
onSelectAllCheckChange(e: Event) {
|
||||
const checkBox = e.target as HTMLInputElement;
|
||||
const isChecked = checkBox.checked;
|
||||
|
||||
this.selectAllChecked = isChecked;
|
||||
this.selectedWorkflowInstanceIds = [];
|
||||
|
||||
if(isChecked)
|
||||
|
||||
if (isChecked)
|
||||
this.selectedWorkflowInstanceIds = this.workflowInstances.items.map(x => x.id);
|
||||
}
|
||||
|
||||
onWorkflowInstanceCheckChange(e: Event, workflowInstance: WorkflowInstanceSummary){
|
||||
onWorkflowInstanceCheckChange(e: Event, workflowInstance: WorkflowInstanceSummary) {
|
||||
const checkBox = e.target as HTMLInputElement;
|
||||
const isChecked = checkBox.checked;
|
||||
|
||||
|
|
@ -130,10 +130,60 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
this.selectAllChecked = this.workflowInstances.items.findIndex(x => this.selectedWorkflowInstanceIds.findIndex(id => id == x.id) < 0) < 0;
|
||||
}
|
||||
|
||||
async onDeleteClick(e: Event, workflowInstance: WorkflowInstanceSummary) {
|
||||
const result = await this.confirmDialog.show('Delete Workflow Instance', 'Are you sure you wish to permanently delete this workflow instance?');
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
const elsaClient = this.createClient();
|
||||
await elsaClient.workflowInstancesApi.delete(workflowInstance.id);
|
||||
await this.loadWorkflowInstances();
|
||||
}
|
||||
|
||||
async onBulkDelete(){
|
||||
const result = await this.confirmDialog.show('Delete Selected Workflow Instances', 'Are you sure you wish to permanently delete all selected workflow instances?');
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
const elsaClient = this.createClient();
|
||||
await elsaClient.workflowInstancesApi.bulkDelete({workflowInstanceIds: this.selectedWorkflowInstanceIds});
|
||||
await this.loadWorkflowInstances();
|
||||
}
|
||||
|
||||
async onBulkActionSelected(e: CustomEvent<DropdownButtonItem>) {
|
||||
const action = e.detail;
|
||||
|
||||
switch(action.name)
|
||||
{
|
||||
case 'Delete':
|
||||
await this.onBulkDelete();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const workflowInstances = this.workflowInstances.items;
|
||||
const workflowBlueprints = this.workflowBlueprints;
|
||||
|
||||
const viewIcon = (
|
||||
<svg class="h-5 w-5 text-gray-500" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const deleteIcon = (
|
||||
<svg class="h-5 w-5 text-gray-500" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z"/>
|
||||
<line x1="4" y1="7" x2="20" y2="7"/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||
<path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"/>
|
||||
<path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div class="p-8 flex content-end justify-right bg-white space-x-4">
|
||||
|
|
@ -147,7 +197,7 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
{this.renderStatusFilter()}
|
||||
{this.renderOrderByFilter()}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-8 sm:block">
|
||||
<div class="align-middle inline-block min-w-full border-b border-gray-200">
|
||||
<table class="min-w-full">
|
||||
|
|
@ -191,7 +241,6 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
const workflowBlueprint = workflowBlueprints.find(x => x.id == workflowInstance.definitionId && x.version == workflowInstance.version) ?? {displayName: '(Workflow definition not found)'};
|
||||
const displayName = workflowBlueprint.displayName;
|
||||
const statusColor = this.getStatusColor(workflowInstance.workflowStatus);
|
||||
debugger;
|
||||
const viewUrl = `/workflow-instances/${workflowInstance.id}/viewer`;
|
||||
const instanceName = !workflowInstance.name ? '' : workflowInstance.name;
|
||||
const isSelected = this.selectedWorkflowInstanceIds.findIndex(x => x === workflowInstance.id) >= 0;
|
||||
|
|
@ -199,7 +248,8 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
|
||||
return <tr>
|
||||
<td class="px-6 py-3 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
|
||||
<input type="checkbox" value={workflowInstance.id} checked={isSelected} onChange={e => this.onWorkflowInstanceCheckChange(e, workflowInstance)} class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded"/>
|
||||
<input type="checkbox" value={workflowInstance.id} checked={isSelected} onChange={e => this.onWorkflowInstanceCheckChange(e, workflowInstance)}
|
||||
class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded"/>
|
||||
</td>
|
||||
<td class="px-6 py-3 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
|
||||
<a href={viewUrl} class="truncate hover:text-gray-600">{workflowInstance.id}</a>
|
||||
|
|
@ -234,7 +284,10 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
{!!workflowInstance.faultedAt ? workflowInstance.faultedAt.toString() : '-'}
|
||||
</td>
|
||||
<td class="pr-6">
|
||||
<elsa-context-menu/>
|
||||
<elsa-context-menu history={this.history} menuItems={[
|
||||
{text: 'View', anchorUrl: viewUrl, icon: viewIcon},
|
||||
{text: 'Delete', clickHandler: e => this.onDeleteClick(e, workflowInstance), icon: deleteIcon}
|
||||
]}/>
|
||||
</td>
|
||||
</tr>
|
||||
})}
|
||||
|
|
@ -255,10 +308,10 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
|
||||
const actions: Array<DropdownButtonItem> = [{
|
||||
text: 'Delete',
|
||||
name: 'Delete'
|
||||
name: 'Delete',
|
||||
}];
|
||||
|
||||
return <elsa-dropdown-button text="Bulk Actions" items={actions} icon={bulkActionIcon} origin={DropdownButtonOrigin.TopLeft}/>
|
||||
return <elsa-dropdown-button text="Bulk Actions" items={actions} icon={bulkActionIcon} origin={DropdownButtonOrigin.TopLeft} onItemSelected={e => this.onBulkActionSelected(e)}/>
|
||||
}
|
||||
|
||||
renderWorkflowFilter() {
|
||||
|
|
@ -271,7 +324,7 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
let items: Array<DropdownButtonItem> = latestWorkflowBlueprints.map(x => ({text: x.displayName!, value: x.id, url: this.buildFilterUrl(x.id, selectedWorkflowStatus, SelectedOrderBy), isSelected: x.id == selectedWorkflowId}));
|
||||
|
||||
items = [{text: 'All', value: null, url: this.buildFilterUrl(null, selectedWorkflowStatus, SelectedOrderBy), isSelected: !selectedWorkflowId}, ...items];
|
||||
|
||||
|
||||
const icon = <svg class="mr-3 h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke="none" d="M0 0h24v24H0z"/>
|
||||
<rect x="4" y="4" width="6" height="6" rx="1"/>
|
||||
|
|
@ -282,32 +335,33 @@ export class ElsaWorkflowInstancesListScreen {
|
|||
|
||||
return <elsa-dropdown-button text={selectedWorkflowText} items={items} icon={icon} origin={DropdownButtonOrigin.TopRight} onItemSelected={e => this.selectedWorkflowId = e.detail.value as string}/>
|
||||
}
|
||||
|
||||
renderStatusFilter(){
|
||||
|
||||
renderStatusFilter() {
|
||||
const selectedWorkflowStatus = this.selectedWorkflowStatus;
|
||||
const selectedWorkflowStatusText = !!selectedWorkflowStatus ? selectedWorkflowStatus : 'Status';
|
||||
const statuses: Array<WorkflowStatus> = [null, WorkflowStatus.Running, WorkflowStatus.Suspended, WorkflowStatus.Finished, WorkflowStatus.Faulted, WorkflowStatus.Cancelled, WorkflowStatus.Idle];
|
||||
|
||||
|
||||
const items: Array<DropdownButtonItem> = statuses.map(x => {
|
||||
const text = x ?? 'All';
|
||||
return ({text: text, url: this.buildFilterUrl(this.selectedWorkflowId, x, this.selectedOrderBy), isSelected: x == selectedWorkflowStatus});
|
||||
});
|
||||
|
||||
|
||||
const icon = <svg class="mr-3 h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<circle cx="12" cy="12" r="10"/> <polygon points="10 8 16 12 10 16 10 8"/>
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polygon points="10 8 16 12 10 16 10 8"/>
|
||||
</svg>;
|
||||
|
||||
|
||||
return <elsa-dropdown-button text={selectedWorkflowStatusText} items={items} icon={icon} origin={DropdownButtonOrigin.TopRight}/>
|
||||
}
|
||||
|
||||
renderOrderByFilter(){
|
||||
renderOrderByFilter() {
|
||||
const selectedOrderBy = this.selectedOrderBy;
|
||||
const selectedOrderByText = !!selectedOrderBy ? `Sort by: ${selectedOrderBy}` : 'Sort';
|
||||
const orderByValues: Array<OrderBy> = [OrderBy.Finished, OrderBy.LastExecuted, OrderBy.Started];
|
||||
const items: Array<DropdownButtonItem> = orderByValues.map(x => {
|
||||
return ({text: x, url: this.buildFilterUrl(this.selectedWorkflowId, this.selectedWorkflowStatus, x), isSelected: x == selectedOrderBy});
|
||||
});
|
||||
|
||||
|
||||
const icon = <svg class="mr-3 h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4h13M3 8h9m-9 4h9m5-4v12m0 0l-4-4m4 4l4-4"/>
|
||||
</svg>;
|
||||
|
|
|
|||
|
|
@ -82,27 +82,36 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
|
|||
const response = await httpClient.get<PagedList<WorkflowBlueprintSummary>>(`v1/workflow-registry?version=${versionOptionsString}`);
|
||||
return response.data;
|
||||
}
|
||||
},
|
||||
},
|
||||
workflowInstancesApi: {
|
||||
list: async (page?: number, pageSize?: number, workflowDefinitionId?: string, workflowStatus?: WorkflowStatus, orderBy?: OrderBy, searchTerm?: string): Promise<PagedList<WorkflowInstanceSummary>> => {
|
||||
const queryString = {};
|
||||
|
||||
if(!!workflowDefinitionId)
|
||||
|
||||
if (!!workflowDefinitionId)
|
||||
queryString['workflow'] = workflowDefinitionId;
|
||||
|
||||
if(workflowStatus != null)
|
||||
|
||||
if (workflowStatus != null)
|
||||
queryString['status'] = workflowStatus;
|
||||
|
||||
if(!!orderBy)
|
||||
|
||||
if (!!orderBy)
|
||||
queryString['orderBy'] = orderBy;
|
||||
|
||||
if(!!searchTerm)
|
||||
|
||||
if (!!searchTerm)
|
||||
queryString['searchTerm'] = searchTerm;
|
||||
|
||||
|
||||
const queryStringItems = collection.map(queryString, (v, k) => `${k}=${v}`);
|
||||
const queryStringText = queryStringItems.length > 0 ? `?${queryStringItems.join('&')}` : '';
|
||||
const response = await httpClient.get<PagedList<WorkflowInstanceSummary>>(`v1/workflow-instances${queryStringText}`);
|
||||
return response.data;
|
||||
},
|
||||
delete: async id => {
|
||||
await httpClient.delete(`v1/workflow-instances/${id}`);
|
||||
},
|
||||
bulkDelete: async request => {
|
||||
const response = await httpClient.delete(`v1/workflow-instances/bulk`, {
|
||||
data: request
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
},
|
||||
scriptingApi: {
|
||||
|
|
@ -128,27 +137,39 @@ export interface ActivitiesApi {
|
|||
}
|
||||
|
||||
export interface WorkflowDefinitionsApi {
|
||||
getByDefinitionAndVersion(definitionId: string, versionOptions: VersionOptions): Promise<WorkflowDefinition>
|
||||
getByDefinitionAndVersion(definitionId: string, versionOptions: VersionOptions): Promise<WorkflowDefinition>;
|
||||
|
||||
list(page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise<PagedList<WorkflowDefinitionSummary>>
|
||||
list(page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise<PagedList<WorkflowDefinitionSummary>>;
|
||||
|
||||
save(request: SaveWorkflowDefinitionRequest): Promise<WorkflowDefinition>
|
||||
save(request: SaveWorkflowDefinitionRequest): Promise<WorkflowDefinition>;
|
||||
|
||||
delete(definitionId: string): Promise<void>
|
||||
delete(definitionId: string): Promise<void>;
|
||||
|
||||
retract(workflowDefinitionId: string): Promise<WorkflowDefinition>
|
||||
retract(workflowDefinitionId: string): Promise<WorkflowDefinition>;
|
||||
|
||||
export(workflowDefinitionId: string, versionOptions: VersionOptions): Promise<ExportWorkflowResponse>
|
||||
export(workflowDefinitionId: string, versionOptions: VersionOptions): Promise<ExportWorkflowResponse>;
|
||||
|
||||
import(workflowDefinitionId: string, file: File): Promise<WorkflowDefinition>
|
||||
import(workflowDefinitionId: string, file: File): Promise<WorkflowDefinition>;
|
||||
}
|
||||
|
||||
export interface WorkflowRegistryApi {
|
||||
list(page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise<PagedList<WorkflowBlueprintSummary>>
|
||||
list(page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise<PagedList<WorkflowBlueprintSummary>>;
|
||||
}
|
||||
|
||||
export interface WorkflowInstancesApi {
|
||||
list(page?: number, pageSize?: number, workflowDefinitionId?: string, workflowStatus?: WorkflowStatus, orderBy?: OrderBy, searchTerm?: string): Promise<PagedList<WorkflowInstanceSummary>>
|
||||
list(page?: number, pageSize?: number, workflowDefinitionId?: string, workflowStatus?: WorkflowStatus, orderBy?: OrderBy, searchTerm?: string): Promise<PagedList<WorkflowInstanceSummary>>;
|
||||
|
||||
delete(id: string): Promise<void>;
|
||||
|
||||
bulkDelete(request: BulkDeleteWorkflowsRequest): Promise<BulkDeleteWorkflowsResponse>;
|
||||
}
|
||||
|
||||
export interface BulkDeleteWorkflowsRequest {
|
||||
workflowInstanceIds: Array<string>;
|
||||
}
|
||||
|
||||
export interface BulkDeleteWorkflowsResponse {
|
||||
deletedWorkflowCount: number;
|
||||
}
|
||||
|
||||
export interface ScriptingApi {
|
||||
|
|
@ -156,18 +177,18 @@ export interface ScriptingApi {
|
|||
}
|
||||
|
||||
export interface SaveWorkflowDefinitionRequest {
|
||||
workflowDefinitionId?: string
|
||||
name?: string
|
||||
displayName?: string
|
||||
description?: string
|
||||
variables?: Variables
|
||||
contextOptions?: WorkflowContextOptions
|
||||
isSingleton?: boolean
|
||||
persistenceBehavior?: WorkflowPersistenceBehavior
|
||||
deleteCompletedInstances?: boolean
|
||||
publish?: boolean
|
||||
activities: Array<ActivityDefinition>
|
||||
connections: Array<ConnectionDefinition>
|
||||
workflowDefinitionId?: string;
|
||||
name?: string;
|
||||
displayName?: string;
|
||||
description?: string;
|
||||
variables?: Variables;
|
||||
contextOptions?: WorkflowContextOptions;
|
||||
isSingleton?: boolean;
|
||||
persistenceBehavior?: WorkflowPersistenceBehavior;
|
||||
deleteCompletedInstances?: boolean;
|
||||
publish?: boolean;
|
||||
activities: Array<ActivityDefinition>;
|
||||
connections: Array<ConnectionDefinition>;
|
||||
}
|
||||
|
||||
export interface ExportWorkflowResponse {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\..\..\common.props" />
|
||||
<Import Project="..\..\..\..\configureawait.props" />
|
||||
|
||||
<Import Project="..\..\..\..\common.props"/>
|
||||
<Import Project="..\..\..\..\configureawait.props"/>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Description>
|
||||
|
|
@ -13,12 +13,12 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.5" />
|
||||
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="5.1.29" />
|
||||
<PackageReference Include="EFCore.BulkExtensions" Version="5.0.6"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.5"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\core\Elsa.Core\Elsa.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\core\Elsa.Core\Elsa.Core.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Linq;
|
||||
using Elsa.Models;
|
||||
using Elsa.Persistence.EntityFramework.Core.Configuration;
|
||||
using Elsa.Persistence.EntityFramework.Core.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ namespace Elsa.Persistence.EntityFramework.Core
|
|||
{
|
||||
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ElsaContext).Assembly);
|
||||
|
||||
if (Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite")
|
||||
if (Database.IsSqlite())
|
||||
{
|
||||
// SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations
|
||||
// here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using EFCore.BulkExtensions;
|
||||
using Elsa.Models;
|
||||
using Elsa.Persistence.EntityFramework.Core.Extensions;
|
||||
using Elsa.Persistence.Specifications;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
|
@ -34,7 +37,7 @@ namespace Elsa.Persistence.EntityFramework.Core.Stores
|
|||
{
|
||||
var dbSet = dbContext.Set<T>();
|
||||
var existingEntity = await dbSet.FindAsync(new object[] { entity.Id }, cancellationToken);
|
||||
|
||||
|
||||
if (existingEntity == null)
|
||||
{
|
||||
await dbSet.AddAsync(entity, cancellationToken);
|
||||
|
|
@ -44,7 +47,7 @@ namespace Elsa.Persistence.EntityFramework.Core.Stores
|
|||
{
|
||||
// Can't use the approach on the next line because we explicitly ignore certain properties (in order for them to be stored in the Data shadow property).
|
||||
// dbContext.Entry(existingEntity).CurrentValues.SetValues(entity);
|
||||
|
||||
|
||||
// Therefore using AutoMapper to copy properties instead.
|
||||
existingEntity = _mapper.Map(entity, existingEntity);
|
||||
}
|
||||
|
|
@ -94,12 +97,12 @@ namespace Elsa.Persistence.EntityFramework.Core.Stores
|
|||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteAsync(T entity, CancellationToken cancellationToken = default) => await DoWorkOnSet(async dbSet => await dbSet.DeleteByKeyAsync(cancellationToken, entity.Id), cancellationToken);
|
||||
public virtual async Task DeleteAsync(T entity, CancellationToken cancellationToken = default) => await DoWorkOnSet(async dbSet => await dbSet.AsQueryable().Where(x => x.Id == entity.Id).BatchDeleteAsync(cancellationToken), cancellationToken);
|
||||
|
||||
public virtual async Task<int> DeleteManyAsync(ISpecification<T> specification, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var filter = MapSpecification(specification);
|
||||
return await DoWorkOnSet(async dbSet => await dbSet.Where(filter).DeleteFromQueryAsync(cancellationToken), cancellationToken);
|
||||
return await DoWorkOnSet(async dbSet => await dbSet.Where(filter).BatchDeleteAsync(cancellationToken), cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<T>> FindManyAsync(ISpecification<T> specification, IOrderBy<T>? orderBy = default, IPaging? paging = default, CancellationToken cancellationToken = default)
|
||||
|
|
@ -169,7 +172,7 @@ namespace Elsa.Persistence.EntityFramework.Core.Stores
|
|||
work(dbContext);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
protected virtual void OnSaving(ElsaContext dbContext, T entity) => OnSaving(entity);
|
||||
|
||||
protected virtual void OnSaving(T entity)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Linq.Expressions;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using EFCore.BulkExtensions;
|
||||
using Elsa.Models;
|
||||
using Elsa.Persistence.Specifications;
|
||||
using Elsa.Serialization;
|
||||
|
|
@ -24,25 +25,25 @@ namespace Elsa.Persistence.EntityFramework.Core.Stores
|
|||
public override async Task DeleteAsync(WorkflowInstance entity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflowInstanceId = entity.Id;
|
||||
|
||||
|
||||
await DoWork(async dbContext =>
|
||||
{
|
||||
await dbContext.Set<WorkflowExecutionLogRecord>().AsQueryable().Where(x => x.WorkflowInstanceId == workflowInstanceId).DeleteFromQueryAsync(cancellationToken);
|
||||
await dbContext.Set<Bookmark>().AsQueryable().Where(x => x.WorkflowInstanceId == workflowInstanceId).DeleteFromQueryAsync(cancellationToken);
|
||||
await dbContext.Set<WorkflowInstance>().DeleteByKeyAsync(cancellationToken, workflowInstanceId);
|
||||
await dbContext.Set<WorkflowExecutionLogRecord>().AsQueryable().Where(x => x.WorkflowInstanceId == workflowInstanceId).BatchDeleteAsync(cancellationToken);
|
||||
await dbContext.Set<Bookmark>().AsQueryable().Where(x => x.WorkflowInstanceId == workflowInstanceId).BatchDeleteAsync(cancellationToken);
|
||||
await dbContext.Set<WorkflowInstance>().AsQueryable().Where(x => x.Id == workflowInstanceId).BatchDeleteAsync(cancellationToken);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public override async Task<int> DeleteManyAsync(ISpecification<WorkflowInstance> specification, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflowInstances = (await FindManyAsync(specification, cancellationToken: cancellationToken)).ToList();
|
||||
var workflowInstanceIds = workflowInstances.Select(x => x.Id).ToList();
|
||||
var workflowInstanceIds = workflowInstances.Select(x => x.Id).ToArray();
|
||||
|
||||
await DoWork(async dbContext =>
|
||||
{
|
||||
await dbContext.Set<WorkflowExecutionLogRecord>().WhereBulkContains(workflowInstanceIds, x => x.WorkflowInstanceId).DeleteFromQueryAsync(cancellationToken);
|
||||
await dbContext.Set<Bookmark>().WhereBulkContains(workflowInstanceIds, x => x.WorkflowInstanceId).DeleteFromQueryAsync(cancellationToken);
|
||||
await dbContext.Set<WorkflowInstance>().WhereBulkContains(workflowInstanceIds, x => x.Id).DeleteFromQueryAsync(cancellationToken);
|
||||
await dbContext.Set<WorkflowExecutionLogRecord>().AsQueryable().Where(x => workflowInstanceIds.Contains(x.WorkflowInstanceId)).BatchDeleteAsync(cancellationToken);
|
||||
await dbContext.Set<Bookmark>().AsQueryable().Where(x => workflowInstanceIds.Contains(x.WorkflowInstanceId)).BatchDeleteAsync(cancellationToken);
|
||||
await dbContext.Set<WorkflowInstance>().AsQueryable().Where(x => workflowInstanceIds.Contains(x.Id)).BatchDeleteAsync(cancellationToken);
|
||||
}, cancellationToken);
|
||||
|
||||
return workflowInstances.Count;
|
||||
|
|
@ -69,7 +70,7 @@ namespace Elsa.Persistence.EntityFramework.Core.Stores
|
|||
};
|
||||
|
||||
var json = _contentSerializer.Serialize(data);
|
||||
|
||||
|
||||
dbContext.Entry(entity).Property("Data").CurrentValue = json;
|
||||
}
|
||||
|
||||
|
|
@ -87,12 +88,12 @@ namespace Elsa.Persistence.EntityFramework.Core.Stores
|
|||
entity.Fault,
|
||||
entity.CurrentActivity
|
||||
};
|
||||
|
||||
var json = (string)dbContext.Entry(entity).Property("Data").CurrentValue;
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(json))
|
||||
data = JsonConvert.DeserializeAnonymousType(json, data, DefaultContentSerializer.CreateDefaultJsonSerializationSettings());
|
||||
|
||||
|
||||
var json = (string) dbContext.Entry(entity).Property("Data").CurrentValue;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(json))
|
||||
data = JsonConvert.DeserializeAnonymousType(json, data, DefaultContentSerializer.CreateDefaultJsonSerializationSettings())!;
|
||||
|
||||
entity.Output = data.Output;
|
||||
entity.Variables = data.Variables;
|
||||
entity.ActivityData = data.ActivityData;
|
||||
|
|
|
|||
Loading…
Reference in a new issue