Winform快速开发组件的实现(二)
昨天我们一直在做准备工作,最终表单数据需要从数据库里提取,并保存到数据库,今天接着介绍如何做提取、保存和验证。
四、提取并显示信息
在EditForm我们定义一个InfoId属性,用于接收在列表页面打开编辑窗体时传递主键,然后编辑窗体通过主键查询实体,最终填充到映射好的控件上。
/// <summary>
/// 获取或设置信息ID,根据此ID查询实体并填充在窗体上。
/// </summary>
public string InfoId { get; set; }
在窗体的Load事件中,判断InfoId是否为空,如果不空则查询出实体对象,将数据填充到各控件。
private void EditForm_Load(object sender, EventArgs e)
{
if (!DesignMode)
{
LoadInfo();
}
} /// <summary>
/// 读取实体信息,显示在窗体上。
/// </summary>
protected virtual void LoadInfo()
{
if (EntityType == null || EntityPropertyExtend == null)
{
Helper.ShowError("没有绑定 EntityType 或 EntityPropertyExtend。");
return;
} if (!string.IsNullOrEmpty(InfoId))
{
using (var persister = new EntityPersister(EntityType))
{
var entity = persister.First(InfoId) as IEntity;
if (entity == null)
{
return;
} FillEntityValues(entity);
}
}
} /// <summary>
/// 将实体的属性值填充到窗体上。
/// </summary>
/// <param name="entity"></param>
protected virtual void FillEntityValues(IEntity entity)
{
foreach (var kvp in EntityPropertyExtend.GetProperties())
{
var value = entity.GetValue(kvp.Value);
if (value == null || value.IsEmpty)
{
continue;
} var converter = ControlEntityMapHelper.GetMapper(kvp.Key.GetType()); if (converter != null)
{
converter.SetValue(kvp.Key, value.GetStorageValue());
}
}
}
五、数据验证并保存
Fireasy.Data.Entity基于System.ComponentModel.DataAnnotations实现了内部的验证机制。在持久化对象Create和Update之前,会调用ValidationUnity对实体对象进行验证。
在窗体上,我们还是使用了ErrorProvider组件来显示验证提示信息。
private void btnSave_Click(object sender, EventArgs e)
{
SaveData();
} /// <summary>
/// 保存表单数据到数据库。
/// </summary>
/// <param name="createNew">保存后是否新建信息。</param>
/// <returns></returns>
protected virtual IEntity SaveData(bool createNew = false)
{
if (EntityType == null || EntityPropertyExtend == null)
{
Helper.ShowError("没有绑定 EntityType 或 EntityPropertyExtend。");
return null;
} //清理验证错误的提示
errorProvider1.Clear(); try
{
using (var persister = new EntityPersister(EntityType))
{
var entity = (string.IsNullOrEmpty(InfoId) ? persister.NewEntity() : persister.First(InfoId)) as IEntity;
if (entity == null)
{
return null;
} ReadEntityValues(entity); var infoId = string.Empty;
if (entity.EntityState == EntityState.Attached)
{
infoId = SetPrimaryProperty(entity);
} persister.Save(entity); if (entity.EntityState == EntityState.Attached)
{
InfoId = infoId;
} Helper.ShowInformation("数据保存成功。");
return entity;
}
}
catch (EntityInvalidateException exp)
{
ShowPropertyInvalidateMessages(exp);
}
catch (Exception exp)
{
var log = LoggerFactory.CreateLogger();
if (log != null)
{
log.Error("[" + EntityType + "]保存数据失败", exp);
} Helper.ShowError("数据保存失败。", exp);
} return null;
}
ReadEntityValues方法用于从窗体控件中读取数据,然后写入到实体对象中。
/// <summary>
/// 读取窗口上的控件值,写给实体属性。
/// </summary>
/// <param name="entity"></param>
protected virtual void ReadEntityValues(IEntity entity)
{
foreach (var kvp in EntityPropertyExtend.GetProperties())
{
var converter = ControlEntityMapHelper.GetMapper(kvp.Key.GetType()); if (converter != null)
{
var value = converter.GetValue(kvp.Key);
var property = PropertyUnity.GetProperty(EntityType, kvp.Value);
entity.SetValue(kvp.Value, PropertyValue.New(value, property.Type));
}
}
}
PropertyValue是Fireasy.Data.Entity中的类,可以参考Fireasy.Data.Entity组件。
SetPrimaryProperty用于手动设置主键的值。
/// <summary>
/// 设置主键的值,并返回主键属性。
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
private string SetPrimaryProperty(IEntity entity)
{
var keyValue = Guid.NewGuid().ToString("N");
var accessor = entity as IEntityPropertyAccessor;
var pk = PropertyUnity.GetPrimaryProperties(EntityType).FirstOrDefault();
accessor.SetValue(pk, keyValue);
return keyValue;
}
如果实体验证失败,会抛出一个EntityInvalidateException类型的异常信息,该对象包括验证失败的各个属性及对应的错误信息,以及在实体类型上定义的全局验证特性,即不是单一的属性值验证,而是业务逻辑验证。
/// <summary>
/// 显示验证失败的信息。
/// </summary>
/// <param name="exp"></param>
private void ShowPropertyInvalidateMessages(EntityInvalidateException exp)
{
var sb = new StringBuilder();
foreach (var property in exp.PropertyErrors)
{
//查找有没有验证失败属性相关联的控件
var map = EntityPropertyExtend.GetProperties().Where(s => s.Value == property.Key.Name).FirstOrDefault();
if (map.Key == null)
{
sb.AppendLine(string.Join("\n", property.Value.ToArray()));
continue;
} //在控件上显示验证失败的信息
errorProvider1.SetError(map.Key, string.Join("\n", property.Value.ToArray()));
} if (sb.Length > )
{
Helper.ShowError("填写的数据不完整,还包括以下这些信息:\n" + sb.ToString());
}
}
六、运行实例
运行程序,查看前面我们定义的ProductEdit窗体。
我们从列表中双击一条数据进行编辑,Book的相关数据自动填充到了控件中。

我们把个别的文本清空,或是输入较长的字符串,这时将保存失败,并在窗体上显示红色的图标。

七、验证类的定义
最后,将Book的验证类贴上来,以便更直观。
//如果要启用实体验证,请使用以下特性,并在 BookMetadata 中定义验证特性。
[MetadataType(typeof(BookMetadata))]
public partial class Book
{
} public class BookMetadata
{ /// <summary>
/// 属性 Id 的验证特性。
/// </summary> [Required]
[StringLength()]
public object Id { get; set; } /// <summary>
/// 属性 ISBN 的验证特性。
/// </summary> [StringLength()]
public object ISBN { get; set; } /// <summary>
/// 属性 BarCode 的验证特性。
/// </summary> [Required]
[StringLength()]
public object BarCode { get; set; } /// <summary>
/// 属性 PyCode 的验证特性。
/// </summary> [StringLength()]
public object PyCode { get; set; } /// <summary>
/// 属性 Name 的验证特性。
/// </summary> [Required]
[StringLength()]
public object Name { get; set; } /// <summary>
/// 属性 Price 的验证特性。
/// </summary> [Required]
public object Price { get; set; } /// <summary>
/// 属性 Agio 的验证特性。
/// </summary> [Required]
public object Agio { get; set; } /// <summary>
/// 属性 Unit 的验证特性。
/// </summary> [Required]
[StringLength()]
public object Unit { get; set; } /// <summary>
/// 属性 SizeNo 的验证特性。
/// </summary> [StringLength()]
public object SizeNo { get; set; } /// <summary>
/// 属性 MediaNo 的验证特性。
/// </summary> [StringLength()]
public object MediaNo { get; set; } /// <summary>
/// 属性 VolumesOfBar 的验证特性。
/// </summary> public object VolumesOfBar { get; set; } /// <summary>
/// 属性 BarsOfBag 的验证特性。
/// </summary> public object BarsOfBag { get; set; } /// <summary>
/// 属性 Emphasis 的验证特性。
/// </summary> public object Emphasis { get; set; } /// <summary>
/// 属性 TypeCode 的验证特性。
/// </summary> [StringLength()]
public object TypeCode { get; set; } /// <summary>
/// 属性 PublisherId 的验证特性。
/// </summary> [StringLength()]
public object PublisherId { get; set; } /// <summary>
/// 属性 PublishDate 的验证特性。
/// </summary> public object PublishDate { get; set; } /// <summary>
/// 属性 UpperLimit 的验证特性。
/// </summary> [Required]
public object UpperLimit { get; set; } /// <summary>
/// 属性 LowerLimit 的验证特性。
/// </summary> [Required]
public object LowerLimit { get; set; } /// <summary>
/// 属性 State 的验证特性。
/// </summary> public object State { get; set; } /// <summary>
/// 属性 Remark 的验证特性。
/// </summary> [StringLength()]
public object Remark { get; set; } /// <summary>
/// 属性 MarkColor 的验证特性。
/// </summary> [StringLength()]
public object MarkColor { get; set; } /// <summary>
/// 属性 DelFlag 的验证特性。
/// </summary> public object DelFlag { get; set; } }
Winform快速开发组件的实现(二)的更多相关文章
- Winform快速开发组件的实现(一)
好久好久没有露面了,呵呵,对于写文章都有点生疏了. 在拿到任何一个项目,不管是b/s的还是c/s,我不会立即开始写代码,我一般会为使这些项目能够快速开发制定一系列的支持组件,虽然可能前期会付出一些代价 ...
- winform快速开发平台 -> 工作流组件(仿GooFlow)
对于web方向的工作流,一直在用gooflow对于目前我的winform开发平台却没有较好的工作流组件. 针对目前的项目经验告诉我们.一个工作流控件是很必要的. 当然在winform方面的工作流第三 ...
- winform快速开发平台 -> 基础组件之分页控件
一个项目控件主要由及部分的常用组件,当然本次介绍的是通用分页控件. 处理思想:我们在处理分页过程中主要是针对数据库操作. 一般情况主要是传递一些开始位置,当前页数,和数据总页数以及相关关联的业务逻辑. ...
- winform快速开发平台->让有限的资源创造无限的价值!
最近一直在维护一套自己的快速开发平台. 主要应对针对C/S架构下的项目.然而对winform这快,还真没有看到过相对好的快速开发平台, 何为快速,在博客园逛了了好久, 预览了很多通用权限管理系统. 确 ...
- winform快速开发平台 -> 通用权限管理之动态菜单
这几个月一直忙APP的项目,没来得及更新项目,想想该抽出时间整理一下开发思路,跟大家分享,同时也希望得到宝贵的建议. 先说一下我们的权限管理的的设计思路,首先一个企业信息化管理系统一定会用到权限管理, ...
- winform快速开发平台 -> 快速绑定ComboBox数据控件
通常我们在处理编辑窗体时.往往会遇到数据绑定.例如combobox控件绑定数据字典可能是我们经常用到的.然而在我的winform快速开发平台中我是如何处理这个频繁的操作呢? 首先,我们要绑定combo ...
- BOLT.NET 学习笔记(一) 开篇 用.net winform 快速开发 炫酷的界面
BOLT.NET 学习笔记(一) 开篇 用.net winform 快速开发 炫酷的界面 bolt 基本介绍 Bolt界面引擎是迅雷公司从2009年开始开发的第四代界面库.迅雷7是首个采用该引擎成功开 ...
- C#.NET Winform 快速开发平台
C/S系统开发框架-企业版 V4.0 (Enterprise Edition) 简介: http://www.csframework.com/cs-framework-4.0.htm 适用软件:适合开 ...
- SNF开发平台WinForm之十二-发送手机短信功能调用-金笛-SNF快速开发平台3.3-Spring.Net.Framework
1.调用前组装参数 2.调用发送信息服务脚本 .调用前组装参数: BaseSendTaskEntity entity = new BaseSendTaskEntity(); entity.Mess ...
随机推荐
- paip.函数式编程方法概述以及总结
paip.函数式编程方法概述以及总结 1 函数式编程:函数式风格..很多命令式语言里支持函数式编程风格 1.1 起源 (图灵机,Lisp机器, 神经网络计算机) 1.2 函 ...
- iOS开发——高级技术&PassBook服务
PassBook服务 Passbook是苹果推出的一个管理登机牌.会员卡.电影票.优惠券等信息的 工具.Passbook就像一个卡包,用于存放你的购物卡.积分卡.电影票.礼品卡等,而这些票据就是一个“ ...
- 从range和xrange的性能对比到yield关键字(上)
使用xrange 当我们获取某个数量的循环时,我们惯用的手法是for循环和range函数,例如: for i in range(10): print i 这里range(10)生成了一个长度为10 ...
- web前端基础——jQuery编程进阶
1 jQuery本质 jQuery不是一门独立的语言,它是JavaScript的一个类库或框架.jQuery的核心思想就是:选取元素,对其操作.很多时候写jQuery代码的关键就是怎样设计合适的选择器 ...
- Spring基于注解及SpringMVC
1.使用注解 (1)组件扫描 指定一个包路径,Spring会自动扫描该包 及其子包所有组件类,当发现组件类定义前有 特定的注解标记时,就将该组件纳入到Spring 容器.等价于原有XML配置中的< ...
- git stash恢复
今天下午在使用Git命令进行代码管理时,因为自己一时疏忽直接把自己一天的劳动成果给弄丢了,这还了得,吓死宝宝了.真的,相信有代码丢失的朋友肯定能体会我当时的心情,不能体会我心情的那就祝你们也丢次代码, ...
- 新版PHP 7效能實測:Drupal 7能快70%,碎形計算大勝Ruby和Python
PHP 7才剛在12月3日正式釋出,網頁開發框架Zend公司立刻發表了一份PHP新舊版效能大車拼報告,除了PHP 7和PHP 5.6之外,也把HHVM 3.7版納入一起比較. Zend公司選擇了幾套知 ...
- 取消 virtualStore 注册表[启用和禁止 UAC虚拟化]
近日发现,在win2008R2 x64下运行的服务器程序,其注册表读取路径为: [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\SZDomain\itvc1] 但是经 ...
- ES5 数组方法reduce
reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始合并,最终为一个值. 参数 callback 执行数组中每个值的函数,包含四个参数 previou ...
- Crontab中shell每分钟执行一次HDFS文件上传不执行的解决方案
一.Crontab -e 加入输出Log */1 * * * * /qiwen_list/upload_to_hdfs.sh > /qiwen_list/mapred.log 2>& ...