Implement Delete and Bulk Delete

This commit is contained in:
Sipke Schoorstra 2021-04-09 12:32:31 +02:00
parent e9bfce5e4e
commit fffc41cd6a
6 changed files with 163 additions and 83 deletions

View file

@ -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>;

View file

@ -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 {

View file

@ -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>

View file

@ -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

View file

@ -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)

View file

@ -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;