大家好,我是码农刚子。这是「Radzen 实战」第 1 篇,复盘 SPcbERP 里那些差点出事的真实坑。

SPcbERP 是 PCB 行业 ERP,前端用 Radzen.Blazor 搭。项目跑了小半年,一直稳。

上个月做依赖升级,把 Radzen.Blazor 从 5.x 升到 6.0.0。编译一次过,dotnet run 正常,浏览器一打开——白屏。

控制台报一串看不懂的错。来回折腾两天才理清,一共四个坑,一个比一个隐蔽。

今天全写出来,你升级的时候照着对一遍,能省两天。


01 坑一:NU1107,依赖版本打架

这个坑最直接,编译就报,不会等到运行。

报错原文:

NU1107: Version conflict detected for Microsoft.AspNetCore.Components.Web.
  Reference the package directly from the project to resolve this issue.
  SPcbERP -> Radzen.Blazor 6.0.0 -> Components.Web (>= 8.0.10)
  SPcbERP -> Components.Web (>= 7.0.0)

翻译一下:Radzen.Blazor 6.0.0 要 Components.Web 8.0.10 以上,你项目里直接引了 7.0.0,两边对不上。

麻烦的是,.NET 8 项目里 Components.Web 本来是框架隐式提供的,正常根本不用手写版本。但一旦你因为某个功能手动引了一次,版本就被钉死了,升级 Radzen 时就会撞。

                       SPcbERP
                   /            \
        Radzen.Blazor 6.0.0    Components.Web
          (传递依赖)           (项目里手动引用)
                |                      |
          要求 >= 8.0.10          要求 >= 7.0.0
                \                    /
                 ⚡ NU1107 版本冲突

三个办法,按省事程度排:

一、删掉手动引用。 如果那个直接引用不是必需的,删掉,让 Radzen 的传递依赖决定版本。最干净。

二、显式对齐版本。 在 csproj 里把 Components.Web 显式指到 8.0.10 以上,和 Radzen 的要求一致。

三、锁 Radzen 版本。 暂时升不上去就先别升,等依赖链理顺再说。

顺序提醒:先解决 NU1107,再谈其他坑。编译不过,后面的错根本看不到。

02 坑二:DataGrid 分页,Count 得自己赋值

编译过了,页面能开了,但表格分页不对。

RadzenDataGrid 做服务端分页,用 LoadData 事件。6.0 里这个事件的参数类型是 DataGridLoadDataEventArgs,里面有 Skip 和 Top,但没有 Count。

总条数得你自己查出来,赋给 args.Count。不赋值,分页器不知道一共多少条,只能显示一页。

只查数据,忘了 Count:

async Task LoadData(DataGridLoadDataEventArgs args)
{
    args.Data = await db.Orders
        .OrderBy(o => o.Id)
        .Skip(args.Skip).Take(args.Top)
        .ToListAsync();
    // args.Count 没赋值 → 分页器算不出总页数
}

数据 + 总数一起给:

async Task LoadData(DataGridLoadDataEventArgs args)
{
    var query = db.Orders.AsQueryable();

    // 总数单独查一次
    args.Count = await query.CountAsync();

    args.Data = await query
        .OrderBy(o => o.Id)
        .Skip(args.Skip).Take(args.Top)
        .ToListAsync();
}

代价是多一次 COUNT 查询。但这是服务端分页绕不开的成本——分页器要知道总页数,就得知道总数。除非你关掉分页器,改用「上一页/下一页」的游标式导航。

顺带一提:LoadData 在筛选、排序变化时也会触发。你的 Count 查询必须和 Data 查询用同一套筛选条件,否则筛选后页数还是按全表算的,会出现「筛出来 3 条,却有 5 页」。

03 坑三:DialogService 的泛型和默认尺寸

第三个坑是弹窗。表现有两种:要么弹不出来,要么弹出来窄得像手机屏。

6.0 里 DialogService.OpenAsync 的泛型推断更严格了,DialogOptions 的 Width / Height 不显式指定的话,默认值和 5.x 不一样。

靠默认值撑满:

await DialogService.OpenAsync<EditOrder>(
    "编辑订单",
    new Dictionary<string, object> { { "Id", id } });
// 6.0 里默认宽度变了,弹窗变得很窄

尺寸和泛型都写死:

await DialogService.OpenAsync<EditOrder>(
    "编辑订单",
    new Dictionary<string, object> { { "Id", id } },
    new DialogOptions
    {
        Width = "800px",
        Height = "600px",
        Resizable = true
    });

还有一个相关的:Close() 的传参。如果你的弹窗要返回值,OpenAsync<T> 的 T 是返回类型,Close(null) 在 6.0 里会因为推断不出类型而编译失败。老老实实写 Close(default(T)) 或者干脆改成 Close(false) 这种能推断出类型的值。

经验:凡是「靠默认值生效」的地方,升级时都要重新显式声明一遍。默认值是库的内部决定,它改了不会通知你。

04 坑四:样式类名变了,自定义 CSS 全失效

这个坑最难查,因为它不报错。

页面看着「没坏」,但变丑了——表格边框没了、按钮圆角没了、对话框标题栏变白。

原因是 6.0 调整了组件生成的 DOM 结构和 CSS 类名。你项目里针对老类名写的覆盖样式,一条都不生效了。

我的建议是:别去覆盖 Radzen 的具体类名,用它的主题 CSS 变量。 类名是内部实现,换个版本就可能变;CSS 变量才是对外承诺的接口。

排查方法也很土但有效:升级后拿浏览器 DevTools 对着一个表格、一个按钮、一个弹窗,逐个对比 DOM 和计算样式。半小时能捞干净。

05 升级 Radzen 的避坑清单

时机 动作
升级前 翻一遍 release notes 里的 breaking changes,别只看版本号
升级前 先看依赖树,把 NU1107 这类冲突提前排掉
升级后 服务端分页的表格,逐个确认 Count 有回填
升级后 弹窗全部显式指定 Width/Height 和泛型参数
升级后 过一遍视觉回归,重点看表格、按钮、对话框
长期 锁死包版本,别用浮动版本号,升级当成一次小项目做

四个坑里,NU1107 是硬错误,编译就拦你;剩下三个都是软错误——不报错,只是行为变了。软错误才最耗时间,因为你得先意识到「它变了」。

一句话总结:升级组件库,别只看版本号。依赖冲突先排掉,靠默认值的地方全部显式写一遍,升完做一次视觉回归。

你在 Radzen 或者别的组件库上栽过最贵的坑是啥?评论区唠唠。

转发给正在做依赖升级的同事,少走我们趟过的弯路。

关注 CSharp精选营,每周二四 get 能直接抄的 C# 实战。

下一篇聊:SaveChanges 一万行?换批量操作快 10 倍。


关于作者

码农刚子,六年 ERP / 制造业系统开发,专注 C# / .NET / Blazor。

博客:https://www.coderlog.net/ 公众号:CSharp精选营