问题详情
64 | 健哥
Known项目新增批量更新方法(不知道有没有搞复杂了,请各位老师指点)

批量更新的方法参考批量导入的方法

  • 首先是新增按钮,在actions.txt里面新增按钮UpdateM| 批量更新 | Update| primary
  • 这个资源是在Utils-GetResource中加载,一开始就加载了,启动后是不会运行这部分的。
  • 添加批量更新按钮主要添加了这些部分,有没有都用到暂时不知道,直接复制批量导入的方法

我做的是工程列表的批量更新

1、由于工程列表是通过模块管理里面创建的,所以先在AutoTablePage里面添加:

public void UpdateM() => ShowUpdateMForm();

2、ShowImportForm方法在BaseTablePage里面,ShowUpdateMForm也放在里面

protected async void ShowUpdateMForm(string param = null) // 定义一个受保护的异步void方法,可以带有一个默认为null的字符串参数。
{
    var type = typeof(TItem); // 获取泛型类型TItem的Type对象。
    var id = $"{type.Name}UpdateM"; // 根据TItem的名称构造一个ID字符串。
    if (!string.IsNullOrWhiteSpace(param)) // 如果param不为空且不是空白字符串。
    id += $"{param}"; // 将param附加到ID字符串后面,前面加上下划线。
    if (Table.IsDictionary) // 如果Table对象的IsDictionary属性为true。
    id += $""; // 将Context.Current对象的Id属性附加到ID字符串后面。
    var fileService = await CreateServiceAsync(); // 异步创建并获取IFileService接口的实例。
    var info = await fileService.GetUpdateMAsync(id); // 调用fileService的GetUpdateMAsync方法,传入ID,获取导入信息。
    info.Name = PageName; // 将导入信息的Name属性设置为PageName。
    info.BizName = UpdateMTitle; // 将导入信息的BizName属性设置为UpdateMTitle。
    var model = new DialogModel { Title = UpdateMTitle }; // 创建DialogModel对象,并设置其Title属性为UpdateMTitle。
    model.Content = builder => // 设置DialogModel的Content属性,使用一个lambda表达式来构建内容。
    {
    builder.Component() // 使用builder来添加一个Importer组件。
    .Set(c => c.Model, info) // 设置Importer组件的Model属性为info。
    .Set(c => c.OnSuccess, async () => // 设置Importer组件的OnSuccess事件。
    {
    await model.CloseAsync(); // 当Importer组件成功时,关闭对话框。
    await RefreshAsync(); // 刷新界面。
    })
    .Build(); // 构建Importer组件。
    };
    UI.ShowDialog(model); // 显示对话框,传入model对象。
}

3、在IFileService中添加

1)Task GetUpdateMAsync(string bizId);
2)Task UpdateMFilesAsync(UploadInfo info);

同时在FileService中实现该方法逻辑: 1)

public async Task GetUpdateMAsync(string bizId) // 定义一个公共的异步方法,返回类型为UpdateMFormInfo,接收一个字符串参数bizId。
{
    var user = Database.User; // 从数据库获取当前用户的信息。
    var task = await Database.Query() // 使用异步方式查询SysTask表。
    .Where(d => d.CompNo == user.CompNo && d.CreateBy == user.UserName && d.BizId == bizId) // 使用Where方法过滤查询结果,只保留满足条件的记录。
    .OrderByDescending(d => d.CreateTime) // 使用OrderByDescending方法按创建时间降序排列查询结果。
    .FirstAsync(); // 使用FirstAsync获取排序后的第一个结果。
    return UpdateMHelper.GetUpdateM(Context, bizId, task); // 调用UpdateMHelper的帮助方法,传入Context、bizId和查询到的task,返回UpdateMFormInfo对象。
}

2)

public async Task UpdateMFilesAsync(UploadInfo info)
{
    // 定义局部变量,用于存储导入表单的数据、任务、文件列表、当前用户等。
    var form = info.Model; // 获取上传信息中的模型数据。
    SysTask task = null; // 初始化任务对象,可能用于异步处理。
    var sysFiles = new List(); // 创建一个用于存储系统文件对象的列表。
    var user = CurrentUser; // 获取当前用户信息。
    var files = info.Files.GetAttachFiles(user, "Upload", form); // 获取附件文件并根据用户和表单信息进行处理。
    // 定义result变量,用于存储数据库事务的结果。
    var result = await Database.TransactionAsync(Language.Upload, async db =>
    {
        // 在数据库事务中添加文件,并返回文件列表。
        sysFiles = await AddFilesAsync(db, files, form.BizId, form.BizType);
        // 如果业务类型符合特定条件,则创建任务对象。
        if (form.BizType == UpdateMHelper.BizType)
        {
            task = UpdateMHelper.CreateTask(form);
            task.Target = sysFiles[0].Id; // 设置任务的目标为第一个文件的ID。
            // 如果表单指定了异步处理,则将任务保存到数据库。
            if (form.IsAsync)
                await db.SaveAsync(task);
        }
    });
    // 将文件列表添加到结果数据中。
    result.Data = sysFiles;
    // 如果结果有效并且业务类型符合特定条件。
    if (result.IsValid && form.BizType == UpdateMHelper.BizType)
    {
        // 如果表单指定了异步处理,则在结果消息中添加相应的提示。
        if (form.IsAsync)
            result.Message += Language["Import.FileImporting"];
        else if (task != null)
        // 如果任务对象存在,则执行导入操作,并更新结果。
            result = await UpdateMHelper.ExecuteAsync(Database, task);
    }
    // 返回包含操作结果的对象。
    return result;
}

4、在Known.Components中新增UpdateMer全部代码如下:

namespace Known.Components;

// 定义了一个名为Importer的类,它继承自BaseComponent,用于处理文件导入功能。
class UpdateMer : BaseComponent
{
    // 私有字段,用于存储导入操作是否完成的状态。
    private bool isFinished;
    // 私有字段,用于存储文件信息。
    private string fileInfo;
    // 私有字段,用于存储错误信息。
    private string error;
    // 私有字段,用于存储消息信息。
    private string message;
    // 私有字段,用于存储文件服务接口。
    private IFileService Service;
    // 私有字段,用于存储文件数据信息。
    private FileDataInfo file;

    // 属性,用于获取错误消息,通过Language字典获取。
    private string ErrorMessage => Language["Import.Error"];

    // 属性,用于获取或设置导入表单信息。
    [Parameter] public UpdateMFormInfo Model { get; set; }
    // 属性,用于设置导入成功后的回调动作。
    [Parameter] public Action OnSuccess { get; set; }

    // 重写基类的OnInitAsync方法,用于异步初始化导入器。
    protected override async Task OnInitAsync()
    {
        await base.OnInitAsync();
        // 初始化isFinished、error和message字段。
        isFinished = Model.IsFinished;
        error = Model.Error;
        message = Model.Message;
        // 创建文件服务实例。
        Service = await CreateServiceAsync<IFileService>();
    }
    // 重写基类的BuildRender方法,用于构建渲染树。
    protected override void BuildRender(RenderTreeBuilder builder)
    {
        // 构建渲染树的逻辑,包括显示导入提示、选择文件、导入按钮、异步导入选项、下载模板链接、错误信息展示和导入消息。
        builder.Div("kui-import", () =>
        {
            builder.Div("danger", Language["Import.Tips"]);
            builder.Div("item", () =>
            {
                BuildInputFile(builder);
                if (isFinished)
                    builder.Button(Language.Import, this.Callback<MouseEventArgs>(OnImportAsync), "primary");
                builder.Div("async", () =>
                {
                    UI.BuildCheckBox(builder, new InputModel<bool>
                    {
                        Placeholder = Language["Import.IsAsync"],
                        Value = Model.IsAsync,
                        ValueChanged = this.Callback<bool>(v => Model.IsAsync = v)
                    });
                });
            });
            builder.Div(() =>
            {
                builder.Link(Language["Import.Download"], this.Callback(OnDownloadTemplateAsync));
                if (!string.IsNullOrWhiteSpace(error))
                    builder.Link(ErrorMessage, this.Callback(OnErrorMessage));
                builder.Span("size", fileInfo);
            });
            var style = string.IsNullOrWhiteSpace(error) ? "primary" : "danger";
            builder.Div($"kui-import-message {style}", message);
        });
    }
    // 私有方法,用于显示错误消息。
    private void OnErrorMessage()
    {
        // 显示对话框,展示错误内容。
        UI.ShowDialog(new DialogModel
        {
            Title = ErrorMessage,
            Content = builder => builder.Markup(error)
        });
    }

    // 私有方法,用于构建文件输入组件。
    private void BuildInputFile(RenderTreeBuilder builder)
    {
        // 构建文件输入组件的逻辑,包括设置接受的文件类型、禁用状态和文件变更事件。
        builder.OpenComponent<InputFile>(0);
        builder.AddAttribute(1, "accept", "text/plain,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        builder.AddAttribute(2, "disabled", !isFinished);
        builder.AddAttribute(3, "OnChange", this.Callback<InputFileChangeEventArgs>(OnInputFilesChanged));
        builder.CloseComponent();
    }

    // 私有异步方法,用于处理文件输入变更事件。
    private async void OnInputFilesChanged(InputFileChangeEventArgs e)
    {
        // 处理文件变更事件,更新文件信息并读取文件内容。
        if (e.File == null || e.File.Size == 0)
            return;

        fileInfo = $"{Language["Import.Size"]}{e.File.Size / 1024}KB";
        this.file = await e.File.ReadFileAsync();
    }

    // 私有异步方法,用于处理导入操作。
    private async void OnImportAsync(MouseEventArgs e)
    {
        try
        {
            // 处理导入操作,包括检查文件、更新消息、调用文件服务进行导入,并处理导入结果。
            if (file == null)
            {
                UI.Error(Language["Import.SelectFile"]);
                return;
            }

            message = Language["Import.Importing"];
            isFinished = false;

            var info = new UploadInfo<UpdateMFormInfo>(Model);
            info.Files["Upload"] = [file];
            var result = await Service.UpdateMFilesAsync(info);
            if (!result.IsValid)
            {
                message = result.Message;
                isFinished = true;
                return;
            }
            if (Model.IsAsync)
            {
                message = result.Message;
                isFinished = false;
            }
            else
            {
                OnSuccess?.Invoke();
            }
        }
        catch (Exception ex)
        {
            message = ex.Message;
        }
    }

    // 私有异步方法,用于处理下载模板操作。
    private async Task OnDownloadTemplateAsync()
    {
        // 处理下载模板操作,获取模板数据并触发下载。
        var bytes = await Service.GetImportRuleAsync(Model.BizId);
        // 检查变量 bytes 是否不为 null 且长度大于 0。
        if (bytes != null && bytes.Length > 0)
        {
            // 将 bytes 转换为 MemoryStream 对象,MemoryStream 是一个包装了一个字节数组的流。
            var stream = new MemoryStream(bytes);

            // 调用 JS.DownloadFileAsync 方法,这是一个异步方法,用于下载文件。
            // 该方法需要两个参数:文件名和包含文件内容的流。
            // 第一个参数是文件名,这里使用 Language["Import.Template"] 作为前缀,后面跟着下划线 _ 和 Model.Name 作为文件名的主体,文件扩展名为 .xlsx。
            // 第二个参数是上面创建的 MemoryStream 对象,它包含了要下载的文件内容。
            await JS.DownloadFileAsync($"{Language["Import.Template"]}_{Model.Name}.xlsx", stream);
        }
    }
}

5、Known\Config.cs中添加:

internal static Dictionary<string, Type> UpdateMTypes { get; } = [];(暂时不知道有没有用到)

6、Known\FormInfos.cs中添加UpdateMFormInfo

public class UpdateMFormInfo : FileFormInfo
{
    public string Name { get; set; }
    public bool IsAsync { get; set; }
    public bool IsFinished { get; set; } = true;
    public string Message { get; set; }
    public string Error { get; set; }

    public static List<string> GetUpdateMColumns(string modelType)
    {
        var columns = new List<string>();
        var baseProperties = TypeHelper.Properties(typeof(EntityBase));
        var type = Type.GetType(modelType);
        var properties = TypeHelper.Properties(type);
        foreach (var item in properties)
        {
            if (item.GetGetMethod().IsVirtual || baseProperties.Any(p => p.Name == item.Name))
                continue;

            var name = item.DisplayName();
            if (!string.IsNullOrWhiteSpace(name))
                columns.Add(name);
        }
        return columns;
    }
}

7、Known\Helpers\UpdateMHelper.cs中新增UpdateMHelper.cs

namespace Known.Helpers;

public sealed class UpdateMHelper
{
    internal const string BizType = "ImportFiles";

    private UpdateMHelper() { }

    internal static UpdateMFormInfo GetUpdateM(Context context, string bizId, SysTask task) // 定义一个内部静态方法,返回UpdateMFormInfo对象,接收Context、bizId和SysTask task作为参数。
    {
        var info = new UpdateMFormInfo { BizId = bizId, BizType = BizType, IsFinished = true }; // 创建一个新的UpdateMFormInfo对象,并设置BizId、BizType和IsFinished属性。
        if (task != null) // 如果传入的SysTask task对象不为null。
        {
            switch (task.Status) // 根据task的Status属性值进行switch语句判断。
            {
                case TaskStatus.Pending: // 如果状态是Pending(待处理)。
                    info.Message = context.Language["Import.TaskPending"]; // 设置info的Message属性为根据上下文Language获取的相应消息。
                    info.IsFinished = false; // 设置IsFinished属性为false,表示任务未完成。
                    break;
                case TaskStatus.Running: // 如果状态是Running(运行中)。
                    info.Message = context.Language["Import.TaskRunning"]; // 设置info的Message属性为相应的消息。
                    info.IsFinished = false; // 设置IsFinished属性为false。
                    break;
                case TaskStatus.Failed: // 如果状态是Failed(失败)。
                    info.Message = context.Language["Import.TaskFailed"]; // 设置info的Message属性为相应的消息。
                    info.Error = task.Note; // 设置info的Error属性为task的Note属性,可能是错误信息。
                    break;
                case TaskStatus.Success: // 如果状态是Success(成功)。
                    info.Message = "更新成功"; // 清空info的Message属性,表示没有消息。
                    break;
            }
        }

        return info; // 返回填充好的UpdateMFormInfo对象。
    }

    internal static Task<byte[]> GetDictionaryRuleAsync(Context context, SysModule module)
    {
        var fields = module.Form.Fields;
        var excel = ExcelFactory.Create();
        var sheet = excel.CreateSheet("Sheet1");
        sheet.SetCellValue("A1", context.Language["Import.TemplateTips"], new StyleInfo { IsBorder = true });
        sheet.MergeCells(0, 0, 1, fields.Count);
        for (int i = 0; i < fields.Count; i++)
        {
            var field = fields[i];
            var note = !string.IsNullOrWhiteSpace(field.Length) ? $"{field.Length}" : "";
            sheet.SetColumnWidth(i, 13);
            sheet.SetCellValue(1, i, note, new StyleInfo { IsBorder = true, IsTextWrapped = true });
            var fontColor = field.Required ? System.Drawing.Color.Red : System.Drawing.Color.White;
            sheet.SetCellValue(2, i, field.Name, new StyleInfo { IsBorder = true, FontColor = fontColor, BackgroundColor = Utils.FromHtml("#6D87C1") });
        }
        sheet.SetRowHeight(1, 30);
        var stream = excel.SaveToStream();
        return Task.FromResult(stream.ToArray());
    }

    // 定义一个异步方法,返回一个byte数组,表示生成的Excel文件的内容。
    internal static Task<byte[]> GetUpdateMRuleAsync(Context context, string bizId)
    {
        // 创建一个导入基础对象,使用传入的上下文和业务ID。
        var import = UpdateMBase.Create(new UpdateMContext { Context = context, BizId = bizId });
        // 如果创建失败(import为null),则返回一个空的byte数组。
        if (import == null)
            return Task.FromResult(Array.Empty<byte>());

        // 初始化导入对象的列信息。
        import.InitColumns();
        // 如果列信息为空或计数为0,同样返回一个空的byte数组。
        if (import.Columns == null || import.Columns.Count == 0)
            return Task.FromResult(Array.Empty<byte>());

        // 创建一个Excel工作簿对象。
        var excel = ExcelFactory.Create();
        // 在工作簿中创建一个名为"Sheet1"的工作表。
        var sheet = excel.CreateSheet("Sheet1");
        // 在A1单元格设置模板提示信息,并应用边框样式。
        sheet.SetCellValue("A1", context.Language["Import.TemplateTips"], new StyleInfo { IsBorder = true });
        // 将A1单元格与接下来的单元格合并,数量等于导入列的数量。
        sheet.MergeCells(0, 0, 1, import.Columns.Count);
        // 遍历所有导入列。
        for (int i = 0; i < import.Columns.Count; i++)
        {
            var column = import.Columns[i];
            // 设置当前列的宽度为13。
            sheet.SetColumnWidth(i, 13);
            // 获取列的导入规则注释。
            var note = column.GetImportRuleNote(context);
            // 在第一行设置列的注释信息,并应用边框和文本换行样式。
            sheet.SetCellValue(1, i, note, new StyleInfo { IsBorder = true, IsTextWrapped = true });
            // 根据列是否必填设置字体颜色。
            var fontColor = column.Required ? System.Drawing.Color.Red : System.Drawing.Color.White;
            // 获取列的名称。
            var columnName = context.Language.GetString(column);
            // 在第二行设置列的名称,并应用边框、字体颜色和背景颜色。
            sheet.SetCellValue(2, i, columnName, new StyleInfo { IsBorder = true, FontColor = fontColor, BackgroundColor = Utils.FromHtml("#6D87C1") });
        }
        try
        {
            // 设置Excel工作表中第一行的高度为30个单位。
            sheet.SetRowHeight(1, 30);
            // 将Excel工作簿保存到一个流(Stream)中。
            var stream = excel.SaveToStream();
            // 将流(Stream)中的数据转换为字节数组(byte[])。
            var byteArray = stream.ToArray();
            // 返回字节数组。
            return Task.FromResult(byteArray);
        }
        catch (Exception ex)
        {
            // 出现异常时,记录错误日志。
            Logger.Error(ex.Message);
            // 返回一个空的字节数组。
            return Task.FromResult(Array.Empty<byte>());
        }


    }

    internal static SysTask CreateTask(UpdateMFormInfo form)
    {
        return new SysTask
        {
            BizId = form.BizId,
            Type = form.BizType,
            Name = form.BizName,
            Target = "",
            Status = TaskStatus.Pending
        };
    }

    internal static async Task<Result> ExecuteAsync(Database db, SysTask task)
    {
        var context = new UpdateMContext
        {
            Database = db,
            Context = db.Context,
            BizId = task.BizId
        };
        var updatem = UpdateMBase.Create(context);
        if (updatem == null)
            return Result.Error("The import method is not registered and cannot be executed!");

        var file = await db.QueryByIdAsync<SysFile>(task.Target);
        return await updatem.ExecuteAsync(file);
    }

    public static Task ExecuteAsync()
    {
        return Task.Run(async () =>
        {
            await TaskHelper.RunAsync(BizType, ExecuteAsync);
        });
    }

    public static Result ReadFile<TItem>(Context context, SysFile file, Action<UpdateMRow<TItem>> action)
    {
        var path = Config.GetUploadPath(file.Path);
        if (!File.Exists(path))
            return Result.Error(context.Language["Import.FileNotExists"]);

        if (!path.EndsWith(".txt"))
            return ReadExcelFile<TItem>(context, path, action);

        var columns = string.IsNullOrWhiteSpace(file.Note)
                    ? []
                    : UpdateMFormInfo.GetUpdateMColumns(file.Note);
        return ReadTextFile<TItem>(context, path, columns, action);
    }

    private static Result ReadExcelFile<TItem>(Context context, string path, Action<UpdateMRow<TItem>> action)
    {
        var excel = ExcelFactory.Create(path);
        if (excel == null)
            return Result.Error(context.Language["Import.ExcelFailed"]);

        var errors = new Dictionary<int, string>();
        var lines = excel.SheetToDictionaries(0, 2);
        if (lines == null || lines.Count == 0)
            return Result.Error(context.Language["Import.DataRequired"]);

        for (int i = 0; i < lines.Count; i++)
        {
            var item = new UpdateMRow<TItem>(context);
            foreach (var line in lines[i])
            {
                item[line.Key] = line.Value;
            }
            action?.Invoke(item);
            if (!string.IsNullOrWhiteSpace(item.ErrorMessage))
                errors.Add(i, item.ErrorMessage);
        }
        return ReadResult(context, errors);
    }

    private static Result ReadTextFile<TItem>(Context context, string path, List<string> columns, Action<UpdateMRow<TItem>> action)
    {
        var errors = new Dictionary<int, string>();
        var lines = File.ReadAllLines(path);
        if (lines == null || lines.Length == 0)
            return Result.Error(context.Language["Import.DataRequired"]);

        for (int i = 0; i < lines.Length; i++)
        {
            var line = lines[i];
            if (string.IsNullOrWhiteSpace(line))
                continue;

            var items = line.Split('\t');
            if (items[0] == columns[0])
                continue;

            var item = new UpdateMRow<TItem>(context);
            for (int j = 0; j < columns.Count; j++)
            {
                item[columns[j]] = items.Length > j ? items[j] : "";
            }
            action?.Invoke(item);
            if (!string.IsNullOrWhiteSpace(item.ErrorMessage))
                errors.Add(i, item.ErrorMessage);
        }
        return ReadResult(context, errors);
    }

    private static Result ReadResult(Context context, Dictionary<int, string> errors)
    {
        if (errors.Count == 0)
            return Result.Success(context.Language["Import.ValidSuccess"]);

        var error = string.Join(Environment.NewLine, errors.Select(e =>
        {
            var rowNo = context.Language["Import.RowNo"].Replace("{key}", $"{e.Key}");
            return $"{rowNo}{e.Value}";
        }));
        return Result.Error($"{context.Language["Import.ValidFailed"]}{Environment.NewLine}{error}");
    }
}

8、Known\Language.cs中添加internal string GetUpdateMTitle(string name) => this["Title.UpdateM"].Replace("{name}", name);

9、Known\UpdateM.cs添加UpdateM.cs,代码如下:

namespace Known;

public class UpdateMContext
{
    internal Context Context { get; set; }
    internal Database Database { get; set; }
    internal string BizId { get; set; }
    public string BizParam => GetBizIdValue(1);
    internal bool IsDictionary => !string.IsNullOrWhiteSpace(BizId) && BizId.StartsWith("Dictionary");

    private string GetBizIdValue(int index)
    {
        if (string.IsNullOrWhiteSpace(BizId))
            return string.Empty;

        var bizIds = BizId.Split('_');
        if (bizIds.Length > index)
            return bizIds[index];

        return string.Empty;
    }
}

public abstract class UpdateMBase(UpdateMContext context)
{
    internal UpdateMContext UpdateMContext { get; } = context;
    public Context Context { get; } = context.Context;
    public Database Database { get; } = context.Database;
    public Language Language => Context?.Language;
    public List Columns { get; } = [];

    public virtual void InitColumns() { }
    public virtual Task<Result> ExecuteAsync(SysFile file) => Result.SuccessAsync("");

    internal static UpdateMBase Create(UpdateMContext context) // 定义一个内部静态方法,用于创建 ImportBase 类型的对象。
    {
        // 判断 context 对象的 IsDictionary 属性是否为 true。
        if (context.IsDictionary)
            return new DictionaryUpdateM(context); // 如果是,创建一个 DictionaryImport 类型的对象并返回。

        // 尝试从 Config.ImportTypes 字典中获取与 context.BizId 对应的 Type 对象。
        if (!Config.UpdateMTypes.TryGetValue(context.BizId, out Type type))
            return null; // 如果没有找到对应的 Type 对象,则返回 null。

        // 使用 Activator.CreateInstance 方法创建 type 类型的对象,并传入 context 作为构造函数参数。
        // 将创建的对象转换为 ImportBase 类型并返回。
        return Activator.CreateInstance(type, context) as UpdateMBase;
    }
}

public abstract class UpdateMBase(UpdateMContext context) : UpdateMBase(context)
{
    protected void AddColumn(Expression<Func<TItem, TValue>> selector)
    {
    var property = TypeHelper.Property(selector);
    var column = new ColumnInfo(property);
    Columns.Add(column);
    }
}

public class UpdateMRow : Dictionary<string, string>
{
    private readonly Context context;

    internal UpdateMRow(Context context)
    {
        this.context = context;
    }

    public string ErrorMessage { get; set; }

    public string GetValue<TValue>(Expression<Func<TItem, TValue>> selector)
    {
        var key = GetKey(selector);
        return GetValue(key);
    }

    public string GetValue<TValue>(Result vr, Expression<Func<TItem, TValue>> selector, bool required)
    {
        var key = GetKey(selector);
        return GetValue(vr, key, required);
    }

    public TValue GetValueT<TValue>(Expression<Func<TItem, TValue>> selector)
    {
        var key = GetKey(selector);
        return GetValue<TValue>(key);
    }

    public TValue GetValueT<TValue>(Result vr, Expression<Func<TItem, TValue>> selector, bool required)
    {
        var key = GetKey(selector);
        return GetValue<TValue>(vr, key, required);
    }

    public string GetValue(string key)
    {
        if (!ContainsKey(key))
            return string.Empty;

        return this[key];
    }

    public string GetValue(Result vr, string key, bool required)
    {
        var value = GetValue(key);
        if (required && string.IsNullOrWhiteSpace(value))
            vr.AddError(context.Language.Required(key));

        return value;
    }

    public T GetValue<T>(string key)
    {
        var value = GetValue(key);
        if (string.IsNullOrWhiteSpace(value))
            return default;

        return Utils.ConvertTo<T>(value);
    }

    public T GetValue<T>(Result vr, string key, bool required)
    {
        var value = GetValue<T>(key);
        if (required && value == null)
            vr.AddError(context.Language.GetString("Valid.FormatInvalid", key));

        return value;
    }

    private string GetKey<TValue>(Expression<Func<TItem, TValue>> selector)
    {
        var property = TypeHelper.Property(selector);
        var column = new ColumnInfo(property);
        return context.Language.GetString(column);
    }
}

class DictionaryUpdateM(UpdateMContext context) : UpdateMBase(context)
{
    public override async Task ExecuteAsync(SysFile file)
    {
    var module = await Database.QueryByIdAsync(UpdateMContext.BizParam);
    if (module == null)
    return Result.Error(Language.Required("ModuleId"));

        var entity = DataHelper.GetEntity(module.EntityData);
        if (entity == null || string.IsNullOrWhiteSpace(entity.Id))
            return Result.Error(Language.Required("TableName"));

        var form = module.Form;
        if (form == null || form.Fields == null)
            return Result.Error(Language.Required("Form.Fields"));

        var models = new List<Dictionary<string, object>>();
        var result = UpdateMHelper.ReadFile<Dictionary<string, object>>(Context, file, item =>
        {
            var model = new Dictionary<string, object>();
            foreach (var field in form.Fields)
            {
                if (field.Type == FieldType.Date || field.Type == FieldType.DateTime)
                    model[field.Id] = item.GetValue<DateTime?>(field.Name);
                else
                    model[field.Id] = item.GetValue(field.Name);
            }
            models.Add(model);
        });

        if (!result.IsValid)
            return result;

        return await Database.TransactionAsync(Language.Import, async db =>
        {
            foreach (var item in models)
            {
                await db.UpdateMAsync(entity.Id, item);
            }
        });
    }
}

10、Sample\Sample\Resources\Locales\zh-CN.txt在资源中加载相应的文字资源

"Title.UpdateM": "批量更新{name}",

"UpdateM.TaskPending": "更新任务等待中...",
"UpdateM.TaskRunning": "更新任务执行中...",
"UpdateM.TaskFailed": "更新任失败"

11、重点:在Known\Data\Database.cs中新增方法:

internal async Task UpdateMAsync(string tableName, Dictionary<string, object> data)
{
    // 检查 data 是否为空或不包含任何键值对。
    if (data == null || data.Count == 0)
        return 0;

    // 从 data 字典中获取 Id 的值。
    var id = data.GetValue<string>($"{tableName}Id");
    //var id = $"{tableName}Id";

    // 获取用于计数的 SQL 命令信息,用于检查给定的 tableName 和 id 在数据库中是否存在。
    var info = Builder.GetCountCommand(tableName, $"{tableName}Id", id);

    // 执行 SQL 命令并获取结果,这里期望返回一个整数,表示记录的数量。
    var count = await ScalarAsync<int>(info);

    // 如果记录数量大于 0,说明存在对应的记录,进行更新操作。
    if (count > 0)
    {
        // 从 data 字典中获取 Version 的值,并增加 1。
        data[nameof(EntityBase.Version)] = data.GetValue<int>(nameof(EntityBase.Version)) + 1;

        // 调用 UpdateAsync 方法更新记录。
        return await UpdateAsync(tableName, $"{tableName}Id", data);
    }
    return 0;
}

12、重点:在Known\Data\SqlBuilder.cs中新增方法:

public CommandInfo GetCountCommand(string tableName, string key, string id)
{
    var sql = $"select count(*) from {FormatName(tableName)} where {key}=@id";
    return new CommandInfo(this, sql, new { id });
}
回复列表

暂无数据