指点一个优化方向(c# winform)

tengxinhengye 2017-09-30 03:12:21
项目中有三个窗体,aForm,bForm,cForm(将来可能出现更多的类似Form)
这三个Form中各有一个完成保存单据功能的button(btnSave),分别是:保存入库单,保存出库单,保存检验单
三个保存按钮的业务逻辑都针对都是不同的表,且都用到了事务
项目用EF4.0+dev+winform+sqlserver2005写的

问题:重构代码,但找不到合适的方向(创建业务基类,窗体基类,接口,“套用”设计模式等好像不太合适本项目)

补充:项目未分层,业务基本在ui中写,link to entity使用还是让项目简化了不少
项目已上线,目前还算稳定,重构纯属自己意愿。
项目类型:企业生产+仓库管理

望指点!
...全文
496 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
正怒月神 2017-10-09
  • 打赏
  • 举报
回复
引用 4 楼 tengxinhengye 的回复:
[quote=引用 2 楼 hanjun0612 的回复:] 然后保存代码如下: public virtual T Add(T entity) { dbEF.Entry<T>(entity).State = EntityState.Added; dbEF.SaveChanges(); return entity; } 我不太使用Linq to entity。 但是我理解下来应该和ef的保存方式差不多。
实际应用中add方法几乎都会被重写,这样定义成虚方法意义大么? 我在想派生类的add方法千变万化时,抽象出基类有什么意义。 也许变成了硬工作量[/quote] 我觉得你的理解可能存在偏差,对于新增来说,就是一个提交数据库的操作。 唯一的区别在于传入的对象可能不同。 所以上面使用了泛型。 而你的意思,听起来更像是把业务逻辑和新增操作放在了一起。
足球中国 2017-10-05
  • 打赏
  • 举报
回复
表示没用过ef,Linq to entity.我们的代码差不多可以完整的转化为java的代码(当然其中有一些我们自己写的简单的工具)。 没看出你的问题在哪?卡,慢,还是啥??
xuzuning 2017-10-03
  • 打赏
  • 举报
回复
重构是推倒重来,而不是修修补补,重构若不能跳出原有的框框,还不如不做 你现在的模块化架构并没有什么问题,非要想做点什么的话,可以将其改造成插件式架构
  • 打赏
  • 举报
回复
在早先我们招聘 winform 和 asp.net 程序员过程中,很多年有过一个上机面试的题目,就是给出一个数据库表以及关联的几个数据库表,要求自动产生“增删改查”的操作界面。 这就跟你把所有基本上值得抽象的“单据”先分层抽象一样,你的所有纯技术的什么 UI 设计,其实都是围绕着对业务领域建模的面向对象继承和多态结构而展开,也围绕着它而崩溃。所以你需要先把对象类的关联结构图画出来,先看看业务模型如何重构。而编程代码再好看,其实都体现了一种从需求、业务分析出发而来的素质和习惯,并不是“为了技术而技术”地去搞技术的。
圣殿骑士18 2017-10-02
  • 打赏
  • 举报
回复
/// <summary>
        /// 复制新增明细
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void bCopyAdd_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            //无效行返回
            if (gridViewData.FocusedRowHandle < 0)
            {
                e.SetCancel();
                return;
            }
        }

        /// <summary>
        /// 编辑明细
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void bEdit_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            //无效行返回
            if (gridViewData.FocusedRowHandle < 0)
            {
                e.SetCancel();
                return;
            }
        }

        /// <summary>
        /// 双击行,用于打开编辑窗体
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void gridViewData_DoubleClick(object sender, EventArgs e)
        {
            //无效行返回
            if (gridViewData.FocusedRowHandle < 0)
            {
                e.SetCancel();
                return;
            }

            //等效于编辑操作
            bEdit.PerformClick();
        }

        /// <summary>
        /// 删除明细
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void bDelete_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            //无效行返回
            if (gridViewData.FocusedRowHandle < 0)
            {
                e.SetCancel();
                return;
            }
            //提示是否删除
            var result = XtraMsgBox.Show("是否删除明细?", MessageType.Question, MessageBoxDefaultButton.Button1);
            if (result == System.Windows.Forms.DialogResult.No)
            {
                e.SetCancel();
                return;
            }
        }

        /// <summary>
        /// 保存按钮功能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void lbSave_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            if (HeadService != null)
            {
                //数据校验和提示
                var listValidResults = HeadService.DoAllValidate();
                if (listValidResults.Count > 0)
                {
                    string message = listValidResults.GetMessage();
                    XtraMsgBox.Show(message);

                    e.SetCancel();
                    return;
                }
            }

            //保存
            ResultEventArgs result = new ResultEventArgs();
            OnSave(result);
            if (result.IsCanceled()) return;

            //执行保存后事件
            if (Saved != null) Saved(this.editMode, this.headModel, result);
            if (result.IsCanceled()) return;

            this.editMode = DataEditWinMode.Edit;
        }

        /// <summary>
        /// 保存并关闭按钮功能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void lbSaveClose_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            if (HeadService != null)
            {
                //数据校验和提示
                var listValidResults = HeadService.DoAllValidate();
                if (listValidResults.Count > 0)
                {
                    string message = listValidResults.GetMessage();
                    XtraMsgBox.Show(message);

                    e.SetCancel();
                    return;
                }
            }

            //保存
            ResultEventArgs result = new ResultEventArgs();
            OnSave(result);
            if (result.IsCanceled()) return;

            //执行保存后事件
            if (Saved != null) Saved(this.editMode, this.headModel, result);
            if (result.IsCanceled()) return;

            //关闭编辑
            this.DialogResult = System.Windows.Forms.DialogResult.OK;
            this.Close();
        }

        /// <summary>
        /// 关闭按钮功能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void lbClose_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// 自动绘制指示列
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void gridViewData_CustomDrawRowIndicator(object sender, DevExpress.XtraGrid.Views.Grid.RowIndicatorCustomDrawEventArgs e)
        {
            if (e.Info.IsRowIndicator && e.RowHandle >= 0)
            {
                e.Info.DisplayText = (e.RowHandle + 1).ToString();
            }
        }

        /// <summary>
        /// 设定自定义行高
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void gridViewData_CalcRowHeight(object sender, DevExpress.XtraGrid.Views.Grid.RowHeightEventArgs e)
        {
            //设置首行的行高不变,解决行上显示图片的问题
            if (e.RowHandle == GridControl.AutoFilterRowHandle) e.RowHeight = -1;
        }

        /// <summary>
        /// 通用右键菜单
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void gridViewData_PopupMenuShowing(object sender, DevExpress.XtraGrid.Views.Grid.PopupMenuShowingEventArgs e)
        {
            if (e.MenuType == DevExpress.XtraGrid.Views.Grid.GridMenuType.Row) //数据行右键菜单
            {
                e.Menu.Items.Add(new DXMenuItem("复制单元格", new EventHandler(CopyCellData), Properties.Resources.copy_16x16));
            }
        }

        /// <summary>
        /// 复制单元格
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CopyCellData(object sender, EventArgs e)
        {
            object foValue = this.gridViewData.GetFocusedValue();

            if (foValue != null) Clipboard.SetDataObject(foValue.ToString());
        }
圣殿骑士18 2017-10-02
  • 打赏
  • 举报
回复
祖先代码,部分,贴不全

public partial class FormBillEdit : XtraForm
    {
        /// <summary>
        /// 窗体加载状态
        /// </summary>
        protected bool isLoading;

        /// <summary>
        /// 窗体修改数据打开的模式
        /// </summary>
        protected DataEditWinMode editMode;

        /// <summary>
        /// 单据表头Model
        /// </summary>
        protected object headModel;

        /// <summary>
        /// 单据编辑行在记录列表中的位置
        /// 初始值 -1 表示处于非编辑状态
        /// </summary>
        protected int editIndex = -1;

        /// <summary>
        /// 复制新增用Model
        /// </summary>
        protected object copySourceModel;

        /// <summary>
        /// 共享主窗体的元数据
        /// </summary>
        protected CachingBase metaData;

        /// <summary>
        /// 保存父列表窗体的数据列表
        /// </summary>
        protected List<object> parentModelList;

        /// <summary>
        /// 当窗体作为Dialog打开时,作为传入的定义参数
        /// </summary>
        public List<object> DialogArgumentList = new List<object>();

        /// <summary>
        /// 当窗体作为Dialog打开时,其返回的对象可以存在此列表中
        /// </summary>
        public List<object> DialogResultList = new List<object>();

        /// <summary>
        /// 打印预览窗体
        /// </summary>
        protected ReportPrintTool printTool { get; set; }

        /// <summary>
        /// 保存成功后调用的事件,一般用于刷新父窗体的列表内容
        /// </summary>
        public event Action<DataEditWinMode, object, ResultEventArgs> Saved;

        /// <summary>
        /// 用于管理单据主数据区的列设置的服务
        /// </summary>
        public EditFormService HeadService { get; set; }

        /// <summary>
        /// 用于管理GridControl数据区的列设置的服务
        /// </summary>
        protected GridControlService GridService { get; set; }

        /// <summary>
        /// 构造方法:默认 保留此构造方法,是为了能够正常显示UI设计器,但new对象时不需要此方法,所以修饰为private
        /// </summary>
        private FormBillEdit()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 构造方法:新增
        /// </summary>
        /// <param name="modelList"></param>
        /// <param name="metaData"></param>
        public FormBillEdit(List<object> modelList, CachingBase metaData)
            : base()
        {
            InitializeComponent();

            this.editMode = DataEditWinMode.Add;
            this.headModel = null;
            this.metaData = metaData;
            this.parentModelList = modelList;

            this.Text = "新增单据";
        }

        /// <summary>
        /// 构造方法:编辑/复制新增
        /// </summary>
        /// <param name="model"></param>
        /// <param name="modelList"></param>
        /// <param name="metaData"></param>
        /// <param name="isCopyAdd"></param>
        public FormBillEdit(object model, List<object> modelList, CachingBase metaData, bool isCopyAdd = false)
            : base()
        {
            InitializeComponent();

            this.editMode = isCopyAdd ? DataEditWinMode.CopyAdd : DataEditWinMode.Edit;
            this.headModel = model;
            this.metaData = metaData;
            this.parentModelList = modelList;
            copySourceModel = ((BaseModel)headModel).Clone();

            this.Text = isCopyAdd ? "复制单据" : "编辑单据";
        }

        /// <summary>
        /// 窗体载入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void Form_Load(object sender, EventArgs e)
        {
            isLoading = true;

            //设置指示行宽度,配合自绘中加入行号
            gridViewData.IndicatorWidth = 40;

            InitialData(true);
        }

        /// <summary>
        /// 窗体载入时,初始化数据
        /// </summary>
        /// <param name="isOpenInitial"></param>
        protected virtual void InitialData(bool isOpenInitial)
        {
            //设定编辑行
            if (editMode == DataEditWinMode.Edit)
            {
                this.editIndex = parentModelList.FindIndex(c => object.ReferenceEquals(c, headModel));
            }
        }
圣殿骑士18 2017-10-02
  • 打赏
  • 举报
回复
1、UI层提取出父类还是可以做的,不过对于Dev来说,难度更高一点,但还是有意义的,因为对于单据UI来说,至少框架类似,还是能节省不少UI层的代码(虽然是在Designer.cs中)

给你看看我的祖先:

  • 打赏
  • 举报
回复
引用 10 楼 tengxinhengye 的回复:
[code=csharp] private void btnSubmit_Click(object sender, EventArgs e) { IntelligentPaperDataEntities ctx = new IntelligentPaperDataEntities(Program.connectString); try { if (ctx.Connection.State != ConnectionState.Open) ctx.Connection.Open(); //1.生成单号 //2.创建对象 ctx.ProductOffLineOrders.AddObject(order); //3.修改相关对象 int count2 = ctx.SaveChanges(); MessageBox.Show("操作成功!\n影响行数:" + count2); } catch (Exception ex) { MessageBox.Show(ex.InnerException.Message); } finally { ctx.Connection.Close(); ctx.Dispose(); }
异常最好是在Form层面或者进程层面统一捕获,而不要每一个方法都写 try...catch。特别是 Debug 版本基本上不应该写 try....catch,只有 Release 版本采用应该有 try...catch。 另外,这种 finally 中要实现的机制,应该使用 using(.....){......} 结构来净化,不要写这么一大堆代码。 基本上都是一些基本的编程语言的伎俩。其实真正的设计在于业务和算法结构,而你的问题显然都集中到底层一点点语句,而没有从必要的层次去设计代码。
  • 打赏
  • 举报
回复
没有细节,这里根本看不出什么“优化、重构”的必要。
tengxinhengye 2017-09-30
  • 打赏
  • 举报
回复

private void btnSubmit_Click(object sender, EventArgs e)
        {
            IntelligentPaperDataEntities ctx = new IntelligentPaperDataEntities(Program.connectString);
            try
            {
                if (ctx.Connection.State != ConnectionState.Open)
                    ctx.Connection.Open();

                //1.生成单号

                //2.创建对象  ctx.ProductOffLineOrders.AddObject(order);

                //3.修改相关对象

                int count2 = ctx.SaveChanges();
                MessageBox.Show("操作成功!\n影响行数:" + count2);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.InnerException.Message);
            }
            finally
            {
                ctx.Connection.Close();
                ctx.Dispose();
            }
        }
代码基本都是这么实现的,在一个ObjectContext生命期内完成对象的增删该查,然后提交(含事务) 这样的代码如何重构呢(如果有三个这样的按钮位于不同界面,但里面的逻辑基本不一样)
tengxinhengye 2017-09-30
  • 打赏
  • 举报
回复
引用 7 楼 ilikeff8 的回复:
如果是通用产品级的,等下个重构版本在下个客户那稳定了,如果有必要,例如加功能,再找个机会给老客户更新升级
这是一个纠结的选择。属于定制产品。 重构主要还是想练习并提高,用平时时间练练。应该不会放到线上去实际用
tengxinhengye 2017-09-30
  • 打赏
  • 举报
回复
回复:MORANS 比方说对于public void 新增()方法,不同的类方法体大相径庭,定义接口好处是统一约定,但并没有觉得会缩减代码量 所以并不是理想的重构 在用ado.net编写程序时,抽象出baseDal基类,并实现IBaseBal接口,确实能规范简化代码 用EF后,至少我不再专门设Dal层,因为代码已经简化了很多 我喜欢将逻辑直接放在ui的事件里(这个或许有人反对),但因为代码量不大,又基本放在try catch finnaly块中,所以看着也匀称舒服 不过这中方式也让逻辑和ui死死耦合了。抽象出业务层,或者抽象一个基类窗体变得困难。
ilikeff8 2017-09-30
  • 打赏
  • 举报
回复
如果是通用产品级的,等下个重构版本在下个客户那稳定了,如果有必要,例如加功能,再找个机会给老客户更新升级
ilikeff8 2017-09-30
  • 打赏
  • 举报
回复
已经上线的系统就不要大改了,別没事找事,改成不稳定和bug,下个项目(项目级)或下个客户(产品级)再重新设计
雨何方 2017-09-30
  • 打赏
  • 举报
回复
我的操作方法是: 1.创建一个接口: interface 数据库操作 { string TableName{get,set;}//数据表名。 string Conditions{get;set;}//条件 string ColumnsName{get;set;}//不需要操作的列名。 class 执行数据库操作:数据库操作 { public void 新增() { } public void 编辑() { } } } 然后在每个窗体中的按钮事件中: namespace MySoftware { public partial class 入库: Form { public Form1() { InitializeComponent(); } 执行数据库操作 执行=new 执行数据库操作(); public void button1_Click(object sender ,EventArgs e) { 执行.TabName=数据库表名; 如果有不需要添加的列名,请定义ColumnsName; 执行.新增() } } } 不管有多少个窗体,只要涉及到此些侶,你只要给出数据表名,如果是编辑的,再给出条件。 因为,所有的列名是读取数据库表的列名,所以,如果有不需要的列名,再定义ColumnsName就可以了。
tengxinhengye 2017-09-30
  • 打赏
  • 举报
回复
引用 2 楼 hanjun0612 的回复:
然后保存代码如下: public virtual T Add(T entity) { dbEF.Entry<T>(entity).State = EntityState.Added; dbEF.SaveChanges(); return entity; } 我不太使用Linq to entity。 但是我理解下来应该和ef的保存方式差不多。
实际应用中add方法几乎都会被重写,这样定义成虚方法意义大么? 我在想派生类的add方法千变万化时,抽象出基类有什么意义。 也许变成了硬工作量
tengxinhengye 2017-09-30
  • 打赏
  • 举报
回复
引用 1 楼 u012948520 的回复:
界面类我是不想提取父类的,界面排版太纠结了 硬是想优化,没事做就提取一些工具类出来吧,字符串处理,日期格式处理之类的。 有EF代码不多,逻辑和展示分开也是强耦合,感觉是脱裤子放屁
项目中有个helper类做了一些提取 用了ef后,代码确实不太多,所以基本都放在事件中了,分起来困难重重
白衣如花 2017-09-30
  • 打赏
  • 举报
回复
界面类我是不想提取父类的,界面排版太纠结了 硬是想优化,没事做就提取一些工具类出来吧,字符串处理,日期格式处理之类的。 有EF代码不多,逻辑和展示分开也是强耦合,感觉是脱裤子放屁
正怒月神 2017-09-30
  • 打赏
  • 举报
回复
然后保存代码如下: public virtual T Add(T entity) { dbEF.Entry<T>(entity).State = EntityState.Added; dbEF.SaveChanges(); return entity; } 我不太使用Linq to entity。 但是我理解下来应该和ef的保存方式差不多。

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧