• 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("
    ", property.Value.ToArray()));
                        continue;
                    }
    
                    //在控件上显示验证失败的信息
                    errorProvider1.SetError(map.Key, string.Join("
    ", property.Value.ToArray()));
                }
    
                if (sb.Length > 0)
                {
                    Helper.ShowError("填写的数据不完整,还包括以下这些信息:
    " + sb.ToString());
                }
            }

        六、运行实例

        运行程序,查看前面我们定义的ProductEdit窗体。

        我们从列表中双击一条数据进行编辑,Book的相关数据自动填充到了控件中。

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


        七、验证类的定义

        最后,将Book的验证类贴上来,以便更直观。

        //如果要启用实体验证,请使用以下特性,并在 BookMetadata 中定义验证特性。
        [MetadataType(typeof(BookMetadata))] 
        public partial class Book
        {
        }
    
        public class BookMetadata
        {
            
            /// <summary>
            /// 属性 Id 的验证特性。
            /// </summary>
            
            [Required]
            [StringLength(32)]
            public object Id { get; set; }
    
            /// <summary>
            /// 属性 ISBN 的验证特性。
            /// </summary>
            
            [StringLength(10)]
            public object ISBN { get; set; }
    
            /// <summary>
            /// 属性 BarCode 的验证特性。
            /// </summary>
            
            [Required]
            [StringLength(13)]
            public object BarCode { get; set; }
    
            /// <summary>
            /// 属性 PyCode 的验证特性。
            /// </summary>
            
            [StringLength(20)]
            public object PyCode { get; set; }
    
            /// <summary>
            /// 属性 Name 的验证特性。
            /// </summary>
            
            [Required]
            [StringLength(20)]
            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(4)]
            public object Unit { get; set; }
    
            /// <summary>
            /// 属性 SizeNo 的验证特性。
            /// </summary>
            
            [StringLength(10)]
            public object SizeNo { get; set; }
    
            /// <summary>
            /// 属性 MediaNo 的验证特性。
            /// </summary>
            
            [StringLength(10)]
            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(10)]
            public object TypeCode { get; set; }
    
            /// <summary>
            /// 属性 PublisherId 的验证特性。
            /// </summary>
            
            [StringLength(32)]
            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(200)]
            public object Remark { get; set; }
    
            /// <summary>
            /// 属性 MarkColor 的验证特性。
            /// </summary>
            
            [StringLength(6)]
            public object MarkColor { get; set; }
    
            /// <summary>
            /// 属性 DelFlag 的验证特性。
            /// </summary>
            
            public object DelFlag { get; set; }
    
        }
  • 相关阅读:
    Oracle配置监听
    Oracle创建表空间和分配用户权限
    Dijkstra
    【刷题】【dp】【记忆化搜索】单词游戏
    【刷题】【记忆化搜索】【dp】Longtail Hedgehog
    【刷题】【dp】 Make The Fence Great Again
    【技巧】【卡常】
    【二分】【基础】(跳石头)(合并果子)(蚯蚓)
    【笔记】两种交换元素的方案对比
    【刷题】【单调栈】请客
  • 原文地址:https://www.cnblogs.com/faib/p/3466711.html
Copyright © 2020-2023  润新知