在测试一个例子时发现的问题,这个示例实现的功能是刷新页面也能保持表格锁定列的状态,先看下页面的完成效果:

测试中发现,几乎相同的代码:

  • 在 FineUIMvc(Net Framework)下没有问题:http://mvc.fineui.com/#/GridLockColumn/SaveToDB

  • 但是在 FineUICore(Net Core)下就失效了,刷新页面后锁定列状态丢失:http://core.fineui.com/#/GridLockColumn/SaveToDB

这个例子使用了 Session 来保存表格的锁定状态,先来看下页面视图的定义:

@(F.Grid().IsFluid(true).CssClass("blockpanel").Title("表格").ShowHeader(true).ShowBorder(true).ID("Grid1").DataIDField("Id").DataTextField("Name").AllowColumnLocking(true)    .Columns(        F.RowNumberField(),        F.RenderField().HeaderText("姓名").DataField("Name").Width(100).EnableLock(true).Locked(true),        F.RenderField().HeaderText("性别").DataField("Gender").FieldType(FieldType.Int).RendererFunction("renderGender").Width(80).EnableLock(true),        F.RenderField().HeaderText("入学年份").DataField("EntranceYear").FieldType(FieldType.Int).Width(100).EnableLock(true),        F.RenderCheckField().HeaderText("是否在校").DataField("AtSchool").RenderAsStaticField(true).Width(100).EnableLock(true),        F.RenderField().HeaderText("所学专业").DataField("Major").RendererFunction("renderMajor").Width(300).EnableLock(true),        F.RenderField().HeaderText("分组").DataField("Group").RendererFunction("renderGroup").Width(80).EnableLock(true),        F.RenderField().HeaderText("注册日期").DataField("LogTime").FieldType(FieldType.Date).Renderer(Renderer.Date).RendererArgument("yyyy-MM-dd").Width(100).EnableLock(true)    ).Listener("columnlock", "onGridColumnLock").Listener("columnunlock", "onGridColumnUnlock")    .DataSource(DataSourceUtil.GetDataTable()))

在客户端事件 columnlock 和 columnunlock 中,会将锁定列的状态改变回发到后台:

function onGridColumnLock(event, columnId) {// 触发后台事件    F.doPostBack('@Url.Action("Grid1_ColumnLockUnlock")', {        type: 'lock',        columnId: columnId    });}

function onGridColumnUnlock(event, columnId) {// 触发后台事件    F.doPostBack('@Url.Action("Grid1_ColumnLockUnlock")', {        type: 'unlock',        columnId: columnId    });}

后台会将列状态信息保存到 Session 中(实际项目中是要保存到数据库中的):

[HttpPost][ValidateAntiForgeryToken]public ActionResult Grid1_ColumnLockUnlock(string type, string columnId){// 模拟操作数据库中的数据    List<string> lockedColumns = GetLockedColumns();if (type == "lock")    {if (!lockedColumns.Contains(columnId))        {            lockedColumns.Add(columnId);        }    }else if (type == "unlock")    {if (lockedColumns.Contains(columnId))        {            lockedColumns.Remove(columnId);        }    }

return UIHelper.Result();}

private static readonly string KEY_FOR_DATASOURCE_SESSION = "GridLockColumn.SaveToDB";

// 模拟在服务器端保存数据// 特别注意:在真实的开发环境中,不要在Session放置大量数据,否则会严重影响服务器性能private List<string> GetLockedColumns(){if (Session[KEY_FOR_DATASOURCE_SESSION] == null)    {        Session[KEY_FOR_DATASOURCE_SESSION] = new List<string>() { };    }return (List<string>)Session[KEY_FOR_DATASOURCE_SESSION];}

当然,上面对 Session 的操作是在 FineUIMvc(ASP.NET MVC) 中的代码,也就是运行在 .Net  Framework 下的代码。

FineUICore(ASP.NET Core)中的代码稍微不同,如下所示:

[HttpPost][ValidateAntiForgeryToken]public IActionResult Grid1_ColumnLockUnlock(string type, string columnId){// 模拟操作数据库中的数据    List<string> lockedColumns = GetLockedColumns();if (type == "lock")    {if (!lockedColumns.Contains(columnId))        {            lockedColumns.Add(columnId);        }    }else if (type == "unlock")    {if (lockedColumns.Contains(columnId))        {            lockedColumns.Remove(columnId);        }    }

return UIHelper.Result();}

private static readonly string KEY_FOR_DATASOURCE_SESSION = "GridLockColumn.SaveToDB";

// 模拟在服务器端保存数据// 特别注意:在真实的开发环境中,不要在Session放置大量数据,否则会严重影响服务器性能private List<string> GetLockedColumns(){if (HttpContext.Session.GetObject<List<string>>(KEY_FOR_DATASOURCE_SESSION) == null)    {        HttpContext.Session.SetObject(KEY_FOR_DATASOURCE_SESSION, new List<string>() { });    }return HttpContext.Session.GetObject<List<string>>(KEY_FOR_DATASOURCE_SESSION);}

上面是保存状态的逻辑,而刷新页面后,会从Session中读取保存的列锁定状态:

// GET: GridLockColumn/SaveToDBpublic ActionResult Index(){    LoadData();

return View();}

private void LoadData(){    ViewBag.LockedColumns = GetLockedColumns();}

然后,在页面视图中,将保存的列锁定状态设置到表格上,如下所示:

@{    Grid grid1 = F.GetControl<Grid>("Grid1");

    List<string> lockedColumns = ViewBag.LockedColumns as List<string>;if (lockedColumns.Count > 0)    {foreach (GridColumn column in grid1.Columns)        {            RenderBaseField field = column as RenderBaseField;if (field == null)            {continue;            }

if (lockedColumns.Contains(field.ColumnID) || lockedColumns.Contains(field.DataField))            {                field.Locked = true;            }

        }    }}

至此,整个流程全部完成。问题是,几乎一模一样的代码,为什么在 .Net Framework 下一切正常,而 .Net Core 下却出问题了?

经过代码调试,我们发现,在 .Net Core 下将状态保存到 Session 中后,再去 Session 中检查却不存在!

后来才发现,我们过于相信引用类型了,请看如下代码:

// 模拟操作数据库中的数据List<string> lockedColumns = GetLockedColumns();if (type == "lock"){if (!lockedColumns.Contains(columnId))    {        lockedColumns.Add(columnId);    }}else if (type == "unlock"){if (lockedColumns.Contains(columnId))    {        lockedColumns.Remove(columnId);    }}

有过面向对象编程经验的同学都知道,lockedColumns实际上是Session中的一个对象引用,因此下面对此对象的 Add 和 Remove 操作会直接改变 Session 中的对象。

为什么 .Net Core 下,这个逻辑就失效了?

我第一个想到的是深拷贝,莫非下面的代码返回了一个 Session 对象的深拷贝?

HttpContext.Session.GetObject<List<string>>(KEY_FOR_DATASOURCE_SESSION)

转到 GetObject 方法的定义,我却发现自己的忘性有多大,却原来 GetObject 是自己很久之前定义的一个扩展方法,.Net Core本身并没有定义这个方法,我们来看一眼:

using Microsoft.AspNetCore.Http;using Newtonsoft.Json;using System;using System.Collections.Generic;using System.Text;

namespace FineUICore{/// <summary>/// Session扩展/// </summary>public static class SessionExtension    {/// <summary>/// 设置Session对象/// </summary>/// <typeparam name="T"></typeparam>/// <param name="session"></param>/// <param name="key"></param>/// <param name="obj"></param>public static void SetObject<T>(this ISession session, string key, T obj)        {            session.SetString(key, JsonConvert.SerializeObject(obj));        }

/// <summary>/// 获取Session对象/// </summary>/// <typeparam name="T"></typeparam>/// <param name="session"></param>/// <param name="key"></param>/// <returns></returns>public static T GetObject<T>(this ISession session, string key)        {            T result = default(T);var value = session.GetString(key);if(!String.IsNullOrEmpty(value))            {                result = JsonConvert.DeserializeObject<T>(value);            }return result;        }

    }}

为什么 Session 中保存个对象还要通过JSON字符串中转?

原来 .Net Core 中原生只提供了在 Session 中保存字符串和 byte 数组的支持,想要保存复杂类型,只能自己写扩展方法了。

而这个扩展方法 GetObject 返回的Session对象的确像是一个深度拷贝的对象,因此对于它的 Add 和 Remove 并不会影响 Session 中实际存储的 JSON字符串。

至此,问题已经很明朗了,我们再来复习下 ASP.NET Core 中使用 Session 的步骤:

1. 首先在 Startup.cs 中添加 Session 服务

public void ConfigureServices(IServiceCollection services){    services.AddDistributedMemoryCache();    services.AddSession();

// FineUI 和 MVC 服务    services.AddFineUI(Configuration);    services.AddMvc(options =>    {// 自定义模型绑定(Newtonsoft.Json)        options.ModelBinderProviders.Insert(0, new JsonModelBinderProvider());    });

}

public void Configure(IApplicationBuilder app, IHostingEnvironment env){    app.UseStaticFiles();       app.UseSession();

// FineUI 和 MVC 中间件(确保 UseFineUI 位于 UseMvc 的前面)    app.UseFineUI();                app.UseMvc();}

2. 控制器中使用 HttpContext.Session.SetString 来保存字符串

HttpContext.Session.SetString("StartedTime", "Started time:" + DateTime.Now.ToString());

var startedTime = HttpContext.Session.GetString("StartedTime");

如果我们看下 SetString 的定义,会知道甚至这个方法也是通过 Microsoft.AspNetCore.Http 里面定义的扩展方法提供的:

知道了根本原因,再去修正 FineUICore(ASP.NET Core)下的这个问题就简单多了。

在控制器方法中,修改完 lockedColumns 对象后,需要显式的保存到 Session 中,如下所示:

[HttpPost][ValidateAntiForgeryToken]public IActionResult Grid1_ColumnLockUnlock(string type, string columnId){// 模拟操作数据库中的数据    List<string> lockedColumns = GetLockedColumns();if (type == "lock")    {if (!lockedColumns.Contains(columnId))        {            lockedColumns.Add(columnId);        }    }else if (type == "unlock")    {if (lockedColumns.Contains(columnId))        {            lockedColumns.Remove(columnId);        }    }

    HttpContext.Session.SetObject(KEY_FOR_DATASOURCE_SESSION, lockedColumns);

return UIHelper.Result();}

原文地址:https://www.cnblogs.com/sanshi/p/10550977.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

你注意到 .Net Framework 和 .Net Core 中使用 Session 的区别了吗?相关推荐

  1. 你注意到 .Net Framework 和 .Net Core 中使用 Session 的区别了吗?

    起因 在测试一个例子时发现的问题,这个示例实现的功能是刷新页面也能保持表格锁定列的状态,先看下页面的完成效果: 测试中发现,几乎相同的代码: 在 FineUIMvc(Net Framework)下没有 ...

  2. ASP.NET Core 中简单Session登录校验

    ASP.NET Core 中简单Session登录校验:从Session的配置添加.到请求过滤.再到页面操作.推荐相关阅读:ASP.NET 会话状态概述  ASP.NET Cookie 概述  ASP ...

  3. C# .Net Framework、.Net Core和.Net Standard的区别

    .Net Core .NET Core 是一个开源.跨平台的开发平台,可以用来构建现代.可伸缩和高性能的跨平台软件应用程序的,包括Asp.Net Core.WPF.WInform等..Net Core ...

  4. EF的Include 在.NET Framework 和.net core 中进行多个表查询的时候不同的写法

    当存两个以上的表存在一对多的qing情况下,如何根据第一个主表去查询下面子表的多条数据? 具体问题如下 A,B,C 三个表 其中 A表为主表,而B表则是A表的子表,两表的关系属于一对多,这个时候我们需 ...

  5. 工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题

    在 .NET Framework 与 .NET Core 中 HttpClient 有个臭名昭著的问题,HttpClient 实现了 IDispose 接口,但当你 Dispose 它时,它不会立即关 ...

  6. 如何在ASP.Net Core中使用辅助服务

    ASP.Net Core 3.0 Preview 3添加了对创建工作程序服务的支持,这些工作程序是诸如Windows服务和Linux守护程序之类的后台服务. 还有一个用于在Visual Studio中 ...

  7. 在 .NET Core 中如何让 Entity Framework Core 在日志中记录由 LINQ 生成的SQL语句

    在开发中,我们想在调试中查看EF Core执行的sql语句,可以使用SQL Studio Manager Tools工具,另一种方式是使用EF Core提供的日志.在ASP.NET Core使用Ent ...

  8. 一套代码同时支持.NET Framework和.NET Core

    在.NET Core的迁移过程中,我们将原有的.NET Framework代码迁移到.NET Core.如果线上只有一个小型的应用还好,迁移升级完成后,只需要维护.NET Core这个版本的代码. 但 ...

  9. .NET Framework VS .NET Core

    本文对应的原文来至 c-sharpcorner 的一篇文章,文末有链接.如有错误,还请指正. 前言 你会为你的下一个应用程序选择哪一种开发平台 - .NET Framework 或者 .NET Cor ...

最新文章

  1. 科学家:冬天,就应该睡饱了再起床上班!
  2. C++ Primer 第10章 习题10.23
  3. optee中的panic函数实现
  4. 问题描述 给定一个由n行数字组成的数字三角形如下图所示。试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。 对于给定的由n行数字组成的数字三角形,计算从三角形的顶至底
  5. Taro+react开发(83):taro路由配置
  6. 处理字符集中的算式问题
  7. 6月30日云栖精选夜读:程序员技术与文艺的PK_来首届阿里巴巴研发效能嘉年华...
  8. selenium报错解决 ElementNotInteractableException,element not interactable
  9. 三星电视机的极光TV显示服务器异常,三星液晶电视机故障有哪些 三星液晶电视机故障解决方法【图文】...
  10. 剑指Offer面试题:16.合并两个排序的链表
  11. Ruby First
  12. 远程计算机没反映6678,6678 PCIe 与FPGA LINK UP 后 不能获得FPGA的DEVICE_ID和VENDDR_ID
  13. 一次监视雷达行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  14. ESP32烧录Marlin固件
  15. ubuntu 14.04 更新 flash
  16. intellij idea 破解 2017
  17. 利用代码实现山脊线、山谷线的提取(arcpy版)
  18. 头条php,基于PHP的免费新闻头条接口查询
  19. 如何跳出令人窒息的职场死循环
  20. 5个开发必备的 Python 工具,你用过几个?

热门文章

  1. android+notepad教程,Android Sample学习——NotePad
  2. getComputedStyle方法的那些事
  3. 介绍一个代码管理系统-Git
  4. 【LINUX学习】链接文件
  5. MapReduce经典案例——统计单词数
  6. 标准梯度—lhMorpGradient
  7. 关于Virtual-Hosting的理解
  8. 战神II导演 首席程序员访谈(转自www.npc6.com )
  9. 如何判断一个程序是 32bit 还是 64bit ?
  10. C# wpf编程CM框架快速入门项目实例