精华内容
下载资源
问答
  • 随笔背景:在很多时候,很多入门不久朋友都会问我:我是从其他语言转到C#开发有没有一些基础性资料给我们学习学习呢,你框架感觉一下太大了,希望有个循序渐进教程或者视频来学习就了。 其实也许我们...

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了。

    其实也许我们每天面对的太多东西了,觉得很多都稀松平常了,即使很细微的地方,可能我们都已经形成习惯了。反过来,如果我们切换到其他领域,如IOS、android,那么开始我们可能对里面很多设计的规则不甚了解,开始可能也是一头雾水。

    本篇继续上一篇《循序渐进开发WinForm项目(2)--项目代码的分析》,继续介绍如何循序渐进开发Winform项目,继续介绍Winform界面层的项目设计以及相关代码的组成部分,使得我们逐渐了解一个完整的开发方案过程。

    1、界面层的项目搭建

    我们从前面两篇随笔的介绍中,可以大概了解到界面层以下所发生的事情,本篇继续这一趋势,介绍界面层的开发内容。

    首先我们创建一个Windows Application项目,使用VS2010(其他版本也可以)创建的Windows应用程序项目,然后把它们的项目目标框架和输出类型修改一下,如下所示。

    然后删除自动生成的所有窗体和类库代码,界面最后成为如下的内容。

    这样做的本意,是把这个界面部分作为一个独立的模块,我们在启动程序框架里面就可以很方便把它包含进来使用,因为我们知道,我们每次开发的业务可能都只是属于一个大的项目中的某部分模块,按模块的划分来进行开发,有方便管理维护、方便整合使用等特点。

    最终在主体界面中整合的是一个个模块,每个模块里面又有不同的窗体界面可供使用,大概的界面模块组合图形如下所示。

    2、业务模块的Winform界面的生成

    从第一小节中可以搭建一个干净的界面项目模块,但是里面还没有任何的内容,这就需要我们进一步生成或者手工编写我们所需的界面内容了。

    为了加快效率,减少界面控件名称的琐碎设置,减少对象赋值的代码编写,我们一般建议采用工具来进行界面的生成,如下界面所示。

    使用代码生成工具生成的界面,命名和数据库字段名称对应,统一风格,减少出错的几率,提高开发效率。

    通过以上设置的界面,设置我们在列表界面里面的查询条件内容,以及需要显示的字段内容;和编辑、新增对象界面的数据内容,然后我们就可以生成相关的窗体界面了。

    单击“Windows界面代码生成”后,生成的窗体界面文件如下所示,分为了两个部分,一个是列表界面,一个是数据编辑界面,通过手工的做法,我们也建议把新增、编辑数据这样合并处理,这样可以减少大项目模块的代码文件。

    把相关的界面内容复制到刚才创建的空项目工程上,并添加相关的类库应用,如框架需要支持的界面基类,公用类库等必备的程序集,这也是我们常见的开发操作了,再次不在赘述。

    最后界面层的项目截图如下所示。

    3、窗体界面的调整处理

    一般来说,自动生成的界面,可能或多或少需要进行手工的一些处理,如可能一些类库没有添加应用,关联的项目没有添加进来等原因,或者是数据类型转换的问题,不过一般问题很小,稍微处理下就能解决的。

    下面我们来对自动生成的窗体界面进行一定的调整,以方便我们的数据显示和处理。

    自动创建的列表界面打开如下所示。

    粗看我们应该觉得还是挺标准的列表界面,但是为了更好看,更统一,我们还是需要进行一些微调,如由于年龄是数值型,这里查询条件有两个作为一个区间进行组合查询的;另外,每个查询条件的控件宽度默认是会自动缩放的,由于条件较少,我们还是把它设置为固定宽度较好,最终我们稍微美化调整后的窗体界面如下所示。

    搞定列表界面,下面我们再来看看数据的编辑界面部分,数据编辑界面生成的效果如下所示(由于版面的原因,我把它进行了一定的缩小)。

    我们看到,里面的字段说明,控件的名称等内容,都已经合理安排好了,基本上设计数据库的时候,指定的字段备注就能正确生成出来了。

    这里演示的表字段比较少,因此可能排版布局方面没有很好的演示效果,不过我们知道DevExpress本身的LayoutControl布局很强大,我们可以随意调整里面控件的位置。

    对于控件的类型,也可以进行不同类型的转换,如可以吧文本类型,转换为数值类型的输入控件等等。

    这样简单的界面可能很多读者会说,这个我自己添加几个控件就可以了,也很快的,但是如果对于需要显示的界面控件(数据库字段很多)的情况下,如果控件显示内容、控件名称、布局位置,全部都要手工操作调整,那么这个工作量是非常惊人的。

    我在开发自己的一个项目程序《客户关系管理系统》的时候,里面的业务很多表的字段都不少,我就只需要拖动,调整下位置或者增加一些特别的控件进行合理展示即可,不需要去调整太多内容,这样可以为我节省大量的界面开发时间,更重要的是,减少出错的几率。

    如对于《客户关系管理系统》客户联系人这个表,字段不少,生成的界面很多控件,默认都是单排的,我根据需要,把他们进行再次排列,并增加一个TabControl选项卡的控件,以便放置更多的内容,和进行必要的区分,详细调整后的界面效果如下所示,这个就是一个复杂界面调整的一个好的案例说明了。

    4、整合业务逻辑层到界面项目

     我刚才在设计编译界面层的时候,是在界面项目引用的地方,把业务逻辑层的项目工程包含到引用里面了,如下所示。

    这样的引用操作是没什么问题,但是我们的项目组件如果拆分的较多,每个界面还需要带着一个业务逻辑的程序集,那么程序集DLL的数量就会翻倍,不便于管理。

    我的意思就是把他们全部进行整合,避免文件过于零散,但是最好避免到处复制文件的局面,这样增加维护成本,不是我们希望看见的。

    基于上面的两种考虑,我们可以在界面项目里面添加文件“链接”的操作,也就是代码文件的影子,编译的时候,他们是编译整合到程序集里面的,但是我们维护的时候,它们所有的”链接“引用,都是指向同一个文件,不会造成多处复制的问题。

    最终的项目构成如下所示,并顺利可以通过编译,这样我们在使用这个Customer的业务模块的时候,只需要一个界面部分的程序集(WHC.TestProject.UIDx.DLL)即可。

    下一篇我们继续介绍界面模块如何整合到主体项目工程里面,进行使用等操作。

    本文转自博客园伍华聪的博客,原文链接:循序渐进开发WinForm项目(3)--Winform界面层的项目设计,如需转载请自行联系原博主。



    展开全文
  • 随笔背景:在很多时候,很多入门不久朋友都会问我:我是从其他语言转到C#开发有没有一些基础性资料给我们学习学习呢,你框架感觉一下太大了,希望有个循序渐进教程或者视频来学习就了。 其实也许我们...
    循序渐进开发WinForm项目(4)--Winform界面模块的集成使用
    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了。
    其实也许我们每天面对的太多东西了,觉得很多都稀松平常了,即使很细微的地方,可能我们都已经形成习惯了。反过来,如果我们切换到其他领域,如IOS、android,那么开始我们可能对里面很多设计的规则不甚了解,开始可能也是一头雾水。


    本篇继续上一篇《循序渐进开发WinForm项目(3)--Winform界面层的项目设计》,继续介绍如何循序渐进开发Winform项目,继续介绍Winform界面模块如何整合到主体项目工程里面,进行使用等操作,使得我们逐渐了解一个完整的开发方案过程。


    1、窗体界面的集成使用


    上篇介绍了如何利用工具进行Winform界面层窗体的快速生成,并进行适当的调整,已达到合理布局,显示美观等的效果,本篇继续这一主题介绍下去,上篇我们开发好的独立界面模块,如何在主体项目中集成使用呢?


    首先我们把生成的界面层DLL复制到项目工程中,然后在主项目工程中添加相关的应用,如下所示。






    然后,我们需要做的就是,在主体界面模块里面添加一个功能按钮的入口,如下所示是我在我的框架界面启动模块里面添加一个按钮的效果。






    然后在按钮的单击事件里面,添加下面的代码即可。


            private void tool_Customer_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
            {
                ChildWinManagement.LoadMdiForm(this, typeof(WHC.TestProject.UI.FrmCustomer));
            }
    其中ChildWinManagement是公用类库里面一个辅助类,用来在多文档的情况下进行窗体的展示,传入一个MDI的Parent的窗体对象引用,另外一个是构造显示的窗体类型,它会根据类型来判断是否已经实例化了,如果存在就打开,否则就创建一个新的窗体病显示出来主界面里面。


    启动界面,后看到的效果如下所示(我们在后台添加一些测试数据后)。






    双击数据出来的编辑界面如下所示。






    这样,我们在还没有添加任何代码和逻辑实现的情况下,基本的界面已经出来了,而且相关的数据存储和显示的功能已经存在,我们所需要做的就只是细化里面的内容即可。


    2、窗体界面的在插件化框架的集成使用


     第一节中介绍的是传统方式的界面模块的继承,开发框架本身也还提供了另外一种方式的界面模块集成方式,插件化的模块化集成。我们通过把相关的DLL复制到运行的目录下,并且在数据库里面配置好相关的Winform模块信息后,就可以在主界面中调用出来是用来。


    关于插件化的框架实现的介绍,大家可以看看我前面写的一篇博客文章《Winform开发框架之插件化应用框架实现》。


    首先我们配置菜单的时候,登陆权限管理系统,添加相关的菜单项目,如下所示。当然,如果你有自己的菜单管理模块,自己通过自己的手工设置好相关的信息即可。






     好,搞定菜单的动态配置后,我们重新登陆下系统的主界面,看看有无变化了。






    从主界面的Ribbon工具栏,我们可以看到,里面已经新增了一个客户管理(红色部分)的内容了,这个位置就是我们刚才新增菜单的位置。单击菜单按钮,那么就会展现出来客户管理的内容了。


    整个主界面框架,加上打开的客户管理界面,整体的效果是一个多文档的界面效果。






    3、集成登陆用户信息


    前面几篇的随笔,主要就是介绍给我们认识如何快速开发一个模块,并且集成到系统框架里面进行使用,我们甚至还没有开始编码,就已经给我们处理好很多细节上的东西,基本上就已经完成一个业务小模块的展示工作了。


    完成本文的前面两个小节,不知道你们有没有发现,我们好像还没有真正的整合登陆的用户信息呢?在独立的系统模块开发过程中,我们如何整合登陆的用户信息呢?


    我们重新回到开发的业务模块的界面项目里面看看原来的编辑界面代码。






    这里面对于保存新增的数据,我们调整一下,把它的创建的人员和时间在代码FrmEditCustomer.cs里面调整成合理的代码,记录人员和当前时间。




            /// <summary>
            /// 编辑或者保存状态下取值函数
            /// </summary>
            /// <param name="info"></param>
            private void SetInfo(CustomerInfo info)
            {
                info.Name = txtName.Text;
                info.Age = txtAge.Value.ToString().ToInt32();
            }


            /// <summary>
            /// 新增状态下的数据保存
            /// </summary>
            /// <returns></returns>
            public override bool SaveAddNew()
            {
                CustomerInfo info = tempInfo;//必须使用存在的局部变量,因为部分信息可能被附件使用
                SetInfo(info);


                info.CreateTime = DateTime.Now;
                info.Creator = LoginUserInfo.ID.ToString();//为了更好管理,我们这里存储用户的ID,而非名称


                try
                {
                    #region 新增数据


                    bool succeed = BLLFactory<Customer>.Instance.Insert(info);
                    if (succeed)
                    {
                        //可添加其他关联操作


                        return true;
                    }
                    #endregion
                }
                catch (Exception ex)
                {
                    LogTextHelper.Error(ex);
                    MessageDxUtil.ShowError(ex.Message);
                }
                return false;
            }


    其中红色部分就是我们新增的内容,我在代码里面存储当前登陆用户的ID:LoginUserInfo.ID.ToString()。


    这里的LoginUserInfo是窗体基类的一个属性,这个属性通过两种方式获得,一个是通过用户在调用窗体显示前进行指定,一种是通过基类自动把缓存里面的用户对象赋值。


    如下面的代码就是界面基类BaseForm的部分代码。




    namespace WHC.Framework.BaseUI
    {
        /// <summary>
        /// 常规界面基类
        /// </summary>
        public partial class BaseForm : DevExpress.XtraEditors.XtraForm, IFunction
        {
            public event EventHandler OnDataSaved;//子窗体数据保存的触发


            public BaseForm()
            {
                InitializeComponent();


                //为了保证一些界面控件的权限控制和身份确认,以及简化操作,在界面初始化的时候,从缓存里面内容(如果存在的话)
                //继承的子模块,也可以通过InitFunction()进行指定用户相关信息
                this.LoginUserInfo = Cache.Instance["LoginUserInfo"] as LoginUserInfo;
                this.FunctionDict = Cache.Instance["FunctionDict"] as Dictionary<string, string>;
            }


    这些用户和功能的信息来源于登陆主界面的时候,我们把它们进行了缓存,方便基类窗体进行获取。


                        Portal.gc.LoginUserInfo = Portal.gc.ConvertToLoginUser(info);
                        Cache.Instance.Add("LoginUserInfo", Portal.gc.LoginUserInfo);//缓存用户信息,方便后续处理
                        Cache.Instance.Add("FunctionDict", Portal.gc.FunctionDict);//缓存权限信息,方便后续使用
     


    第二种方式指定当前用户信息的步骤,是通过基类窗体的InitFunction函数进行指定。




            /// <summary>
            /// 初始化权限控制信息
            /// </summary>
            public void InitFunction(LoginUserInfo userInfo, Dictionary<string, string> functionDict)
            {
                if (userInfo != null)
                {
                    this.LoginUserInfo = userInfo;
                }
                if (functionDict != null && functionDict.Count > 0)
                {
                    this.FunctionDict = functionDict;
                }
            }


    手工指定当前用户信息的调用代码如下所示。




            private void btnAddNew_Click(object sender, EventArgs e)
            {
                FrmEditCustomer dlg = new FrmEditCustomer();
                dlg.InitFunction(base.LoginUserInfo, base.FunctionDict);//该步骤省略也可以,用户信息以通过基类缓存进行获取


                if (DialogResult.OK == dlg.ShowDialog())
                {
                    BindData();
                }
            }


    一般情况下,我们建议采用第一种,不用多余的代码进行设置指定,只需要在登录的时候,把它放到缓存里面即可,这样界面基类实例化的时候,就会自动获取用户信息了,这个操作类似于Web领域里面的Session操作,只要存储/获取的键值保存一致即可。


    好了,我们前面说到,保存的时候,是保存当前用户的ID信息,那么我们在列表展示的时候,默认就会展示用户的ID信息而已,得到的界面效果如下所示。






    我们为了更好展示内容,就需要对用户ID的数据进行转义。


    由于DevExpress有这样对每行记录进行转义的操作,我们在列表界面上添加一个转义函数。


                this.winGridViewPager1.gridView1.CustomColumnDisplayText += new DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventHandler(gridView1_CustomColumnDisplayText);
    数据转义函数里面涉及到对权限系统模块的引用(我们需要把ID转义为FullName(用户全名)),我们把权限模块的DLL引用包含进来即可(因为权限管理模块是所有界面模块都可以使用的)。


    然后在这个函数里面对当前的Creator进行转义。




            void gridView1_CustomColumnDisplayText(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventArgs e)
            {
                if (e.Column.ColumnType == typeof(DateTime))
                {
                    string columnName = e.Column.FieldName;
                    if (e.Value != null)
                    {
                        if (Convert.ToDateTime(e.Value) <= Convert.ToDateTime("1900-1-1"))
                        {
                            e.DisplayText = "";
                        }
                        else
                        {
                            e.DisplayText = Convert.ToDateTime(e.Value).ToString("yyyy-MM-dd HH:mm");//yyyy-MM-dd
                        }
                    }
                }
                else if (e.Column.FieldName == "Creator")
                {
                    if (e.Value != null)
                    {
                        e.DisplayText = BLLFactory<User>.Instance.GetFullNameByID(e.Value.ToString().ToInt32());
                    }
                }
            }


    然后复制文件,重新运行主程序即可看到如下界面所示。






    至此,我们本小节已经完成了,登陆用户信息的记录和转义的操作了,当然我们系统模块里面,可能还有很多地方需要用到用户信息的或者角色信息的,这个例子只是一个抛砖引玉的操作。


     循序渐进开发WInform项目--系列文章导引:


    《循序渐进开发WinForm项目(4)--Winform界面模块的集成使用》


    《循序渐进开发WinForm项目(3)--Winform界面层的项目设计》


    《循序渐进开发WinForm项目(2)--项目代码的分析》


     《循序渐进开发WinForm项目(1) --数据库设计和项目框架的生成》
    展开全文
  • 在C# Winform项目中,想通过“网络爬虫”定时抓取某个网站中指定某个菜单中内容(指定某个菜单内容可能是列表,也可能纯文字或文字加图片说明内容)。 注:指定某个菜单中内容是列表话,“网络爬虫”既...
  • 在C# Winform项目,在form1窗体一个button按钮。 目前已经可以在button点击事件中,通过以下代码实现将“CBOX央视影音”在线视频播放App客户端打开。 代码如下: Process proc = new Process(); proc.StartInfo....
  • 知道一天开始写控制台、WinForm的程序,^_^,当然也想享受这种方便啦,可是发现Windows应用项目中不支持特殊文件夹,根本没有这种东西! 在ASP.net程序中只要将mdf文件放到项目的App_Data文件夹即可,在连接...

    ASP.net开发web应用,asp.net 2.0特有几个特殊的文件夹App_Data,web应用的数据库大可以放在这个下面,一直工作的很好。......知道有一天开始写控制台、WinForm的程序,^_^,当然也想享受这种方便啦,可是发现Windows应用项目中不支持特殊的文件夹,根本没有这种东西!

    在ASP.net程序中只要将mdf文件放到项目的App_Data文件夹即可,在连接字符串中使用 Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\数据库名称.mdf;Integrated Security=True;User Instance=True       做连接字符串即可。

    在配置文件中连接字符串
    <connectionStrings><add name="ConnectionString" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|data.mdf;User Instance=true" providerName="System.Data.SqlClient"/></connectionStrings>

     

    而WinForm中的DataDirectory值则是当前项目的路径,因此Winform中mdf文件不用放到App_Data中,放到项目根目录下就可以。但是新问题随之又来了,WinForm程序运行的时候连接的是bin/Debug下的mdf文件,而不是项目中的mdf文件,这是和ASP.net程序行为不同的地方。每次程序发生Build行为的时候,项目中的mdf就会覆盖bing/Debug下的mdf文件,也就是有两个mdf文件的存在,项目中的mdf相当于“源文件”。虽然可以通过修改文件的“BuildToOuput”属性来部分解决问题,但是仍然不是很完美。

    有一个比较很直接的想法,就是让程序去连接项目中的mdf文件,而不是连接bin/Debug下那个。 经过查询资料找到了修改方法,在Program.cs文件 Main函数 最开始加入如下代码: string dataDir = AppDomain.CurrentDomain.BaseDirectory;             if (dataDir.EndsWith(@"\bin\Debug\")                 || dataDir.EndsWith(@"\bin\Release\"))             {                 dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;                 AppDomain.CurrentDomain.SetData("DataDirectory", dataDir);             }
    原理简单分析:连接字符串中的DataDirectory的值就是通过AppDomain.CurrentDomain.SetData赋值过去的,如果当前程序的目录以"\bin\Debug\"或者"\bin\Release\"则认为它是运行在VisualStudio环境中,就取项目的目录然后赋值给DataDirectory这个key。既然是CurrentDomain.SetData,估计对于非默认AppDomain中的数据库连接代码可能会不起作用(只是猜测,没验证),这就要需要创建子AppDomain的时候再去赋值了。
    上面的代码还是有一点潜在的bug的,比如正式的运行的时候exe被很杯具的放到了某个bin\Debug目录下,就会有问题,不过想想正式生产环境运行的时候肯定不会用这种AttachDbFilename方式,这种方式只存在于开发环境,因此也就睁一只眼闭一只眼了

    参考:http://www.rupeng.com/forum/thread-11988-1-1.html         http://www.cnblogs.com/dajianshi/archive/2007/07/06/808495.html

     
    分类: WinForm

    转载于:https://www.cnblogs.com/lzhitian/archive/2012/06/06/2537805.html

    展开全文
  • 随笔背景:在很多时候,很多入门不久朋友都会问我:我是从其他语言转到C#开发有没有一些基础性资料给我们学习学习呢,你框架感觉一下太大了,希望有个循序渐进教程或者视频来学习就了。 其实也许我们...

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了。

    其实也许我们每天面对的太多东西了,觉得很多都稀松平常了,即使很细微的地方,可能我们都已经形成习惯了。反过来,如果我们切换到其他领域,如IOS、android,那么开始我们可能对里面很多设计的规则不甚了解,开始可能也是一头雾水。

    本篇继续上一篇《循序渐进开发WinForm项目(1) --数据库设计和项目框架的生成》,继续介绍如何循序渐进开发Winform项目,继续分析介绍Winform的项目代码,从而让我们更加了解其中的分层和项目框架的组成等内容。

    1、数据访问接口的定义

    上面我们分析了实体类的定义,本节继续分析其他部分的内容,如数据访问接口成的定义如下所示。

    namespace WHC.TestProject.IDAL
    {
        /// <summary>
        /// 客户信息
        /// </summary>
        public interface ICustomer : IBaseDAL<CustomerInfo>
        {
        }
    }

    这里面的代码很简单,没有多余的代码行,那么里面究竟发生了什么呢,其中的IBaseDAL又是什么定义呢?

    其实,IBaseDAL就是定义了很多我们开发用到的基础接口,如标准的增删改查,以及衍生出来的一些其他接口,如分页查询,条件查询等接口内容。这个ICustomer就是用来定义一些除了标准接口不能实现外的业务接口。

    IBaseDAL通过传入一个实体类,从而方便给基类接口提供强类型的数据类型指定,提高我们的开发效率,减少出错的机会。

    我们可以在VS里面查看IBaseDAL的定义,如下所示:

    可以看到里面很多相关的接口定义,有返回实体T的,也有返回List<T>的,还有DataTable类型等等,这些基础接口,经过我们多个项目的应用实践,已逐步稳定并能够提供很好的接口支持,方便我们快速调用处理。

    即使我们在没有实现任何业务接口的情况下,仅仅利用标准的基类API,也基本上能够完成绝大多数的数据操作功能了。

    2、数据访问接口实现类的定义

     我们分析完IDAL的数据访问接口成的定义后,继续了解一下,如何基于这个接口进行访问层的实现设计的。数据访问的实现层在项目中的位置如下所示(以基于SqlServer的DALSQL层进行分析)。

     

    它的类代码定义如下所示。

    namespace WHC.TestProject.DALSQL
    {
        /// <summary>
        /// 客户信息
        /// </summary>
        public class Customer : BaseDALSQL<CustomerInfo>, ICustomer
        {

    数据访问接口实现层和接口定义层一样,都有一个基类,如基于SqlServer实现的基类为BaseDALSQL,这个基于SqlServer的数据访问基类,它也是继承自一个超级基类(大多数的实现在这里)AbstractBaseDAL。他们之间的继承关系如下所示

    而我们刚才在项目工程的图里面看到,BaseDALSQL、IBaseDAL、AbstractBaseDAL这些类库由于具有很大的通用性,为了减少在不同的项目中进行复制导致维护问题,因此我们全部把这些经常使用到的基类或者接口,抽取到一个独立的类库里面,为了和普通的DotNET公用类库命名进行区分(WHC.Framework.Commons),我们把它命名为WHC.Framework.ControlUtil。

    BaseDALSQL基类的定义如下所示。

    这样做的好处是,在所有的模块里面,避免复制导致的版本维护问题,同时也减少代码的重复生成,增量生成的全部代码,可以一次性复制到整个项目工程里面,而不会导致基础类库的替换,因为这些基类不在生成目录里面,所有生成的类文件,都是和业务表相关的,如下所示。

    具体的数据访问实现类(如Customer),它把数据库信息转换为实体类,有一个函数,在代码生成的时候已经生成;同时在把实体类的属性保存到数据库也有一个类似CRM的映射关系,从而实现可空的字段获取和更新操作。

            /// <summary>
            /// 将DataReader的属性值转化为实体类的属性值,返回实体类
            /// </summary>
            /// <param name="dr">有效的DataReader对象</param>
            /// <returns>实体类对象</returns>
            protected override CustomerInfo DataReaderToEntity(IDataReader dataReader)
            {
                CustomerInfo info = new CustomerInfo();
                SmartDataReader reader = new SmartDataReader(dataReader);
                
                info.ID = reader.GetString("ID");
                info.Name = reader.GetString("Name");
                info.Age = reader.GetInt32("Age");
                info.Creator = reader.GetString("Creator");
                info.CreateTime = reader.GetDateTime("CreateTime");
                
                return info;
            }
    
            /// <summary>
            /// 将实体对象的属性值转化为Hashtable对应的键值
            /// </summary>
            /// <param name="obj">有效的实体对象</param>
            /// <returns>包含键值映射的Hashtable</returns>
            protected override Hashtable GetHashByEntity(CustomerInfo obj)
            {
                CustomerInfo info = obj as CustomerInfo;
                Hashtable hash = new Hashtable(); 
                
                hash.Add("ID", info.ID);
                 hash.Add("Name", info.Name);
                 hash.Add("Age", info.Age);
                 hash.Add("Creator", info.Creator);
                 hash.Add("CreateTime", info.CreateTime);
                     
                return hash;
            }

    3、业务逻辑层的实现分析

     分析完成了数据访问层的接口和实现类后,我们来进一步看看业务逻辑层的实现分析,由于数据访问层的本意是基于特定的数据库实现,因此业务逻辑层就是抽象不同的数据库,让它们根据配置,指向不同的数据库实现类,从而实现多数据库的支持。

    namespace WHC.TestProject.BLL
    {
        /// <summary>
        /// 客户信息
        /// </summary>
        public class Customer : BaseBLL<CustomerInfo>
        {
            public Customer() : base()
            {
                base.Init(this.GetType().FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
            }
        }
    }

    业务逻辑层的代码也很简单,在构造函数里面Init一下即可,之所以使用这个Init操作,其实为了确定BLL层的业务对象名称和指定在哪个程序集里面进行构造的需要,让给基类进行必要的创建工作。

    在BaseBLL的Init函数里面,我们根据子类传入的相关参数,由于我们约定了数据访问类的命名空间,因此只根据数据库配置的不同需要,替换部分名称,就可以具体的构造出一个数据访问类了。

                #region 根据不同的数据库类型,构造相应的DAL层
                AppConfig config = new AppConfig();
                string dbType = config.AppConfigGet("ComponentDbType");
                if (string.IsNullOrEmpty(dbType))
                {
                    dbType = "sqlserver";
                }
                dbType = dbType.ToLower();
    
                string DALPrefix = "";
                if (dbType == "sqlserver")
                {
                    DALPrefix = "DALSQL.";
                }
                else if (dbType == "access")
                {
                    DALPrefix = "DALAccess.";
                }
                else if (dbType == "oracle")
                {
                    DALPrefix = "DALOracle.";
                }
                else if (dbType == "sqlite")
                {
                    DALPrefix = "DALSQLite.";
                }
                else if (dbType == "mysql")
                {
                    DALPrefix = "DALMySql.";
                }
                #endregion
    
                this.dalName = bllFullName.Replace(bllPrefix, DALPrefix);//替换中级的BLL.为DAL.,就是DAL类的全名
                baseDal = Reflect<IBaseDAL<T>>.Create(this.dalName, dalAssemblyName);//构造对应的DAL数据访问层的对象类

    这样精确构造出来的数据库访问访问对象,并把它转换为基类接口,那么就可以在BaseBLL类里的基类接口进行调用了。

    而构造业务对象,通过BLLFactory<T>的泛型工厂,更能够精确构造出对应的业务对象类,这样构造出来的对象具有强类型,非常方便使用。

    以上就是业务逻辑层,数据访问层和数据访问接口层的设计关系,为了高效进行开发工作,我们一定要使用强类型的接口调用,这样可以大大减少出错机会,而返回的基类接口,由于传入了特定的具体类型T,也能够构造出强类型的列表或者对象。因此,合理利用泛型,能够是我们的开发体验更加美好,更加高效。 

    本文转自博客园伍华聪的博客,原文链接:循序渐进开发WinForm项目(2)--项目代码的分析,如需转载请自行联系原博主。



    展开全文
  • 随笔背景:在很多时候,很多入门不久朋友都会问我:我是从其他语言转到C#开发有没有一些基础性资料给我们学习学习呢,你框架感觉一下太大了,希望有个循序渐进教程或者视频来学习就了。 其实也许我们...
  • 最近学生们学习了2个月C#和WINFORM没有学习数据库。故设计了这样一个小游戏,基本覆盖了C#语言常用语法,已经面向对象基本思想。本实训课程分为8个阶段来实践,效果非常 此游戏开发为在课堂上即兴编写...
  • 随笔背景:在很多时候,很多入门不久朋友都会问我:我是从其他语言转到C#开发有没有一些基础性资料给我们学习学习呢,你框架感觉一下太大了,希望有个循序渐进教程或者视频来学习就了。 其实也许我们...
  • 哇咔咔,长时间没来博客园写点东西...三态树就是子节点全部选中、部分选中、全部未选中时父节点三种选中状态,windows系统的好多功能都用这种三态树,但是让人郁闷winform没有直接提供这种控件,只提供了...
  • 由于现在会使用WinForm的人是越来越少了,可能时候做点小东西就只好用ASP.NET去完成了(喜欢控制台朋友请不要顶针),如果是这样,悲剧就发生了:一个小工具(或者小演示项目),发给朋友去用,总不至于让人家...
  • 前两天一个简单C/S项目用到分页,因为是Winform下,没有现成,自己也懒得写,就找了下,看到了ycmoon一个控件 http://www.cnblogs.com/ycmoon/archive/2010/01/07/1640689.html 参考后,做了简化,只保留了...
  • 确实通过通用权限管理系统我们能快速的解决针对winform项目中的权限管理模块,节省这部分时间来更好的完善项目的的实际业务模块。  真的很不错。 吉日嘎拉、湖南胡勇等等吧。忽然发现这些只是
  • 起因是在当前这家公司的项目中,一个图片加载的WInform控件,在加载几十张图片以后,界面会出现明显的卡顿,这种用户体验实在是让人难以接受,想要优化,又看不到源码,所以,不如重新写一个。...
  • winform编程问题

    2009-10-25 11:19:16
    最近最一个项目需要一个等待面板在上面显示 就像360那样子   winform有没有好网络连接组件?   谢谢各位大大留下宝贵意见 

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 140
精华内容 56
关键字:

有没有好的winform项目