意欲凌风翔 月照一孤舟

IT for Business for Market

  博客园 :: 首页 :: 联系 :: 订阅 订阅 :: 管理
  3 Posts :: 0 Stories :: 0 Comments :: 0 Trackbacks

2008年3月19日 #

 

我们在开发中会涉及到数据库的访问,我们姑且不去讨论是否要使用存储过程,网上有太多的利弊分析了。如果您使用存储过程,并且还在手工的进行窗口间切换,看看是参数什么数据类型,是不是out参数,有没有返回值……,然后再到VS环境中去一一把这些参数Parameters.Add进去,是不是有点烦。

无事就做了一个小工具,没什么技术含量,但对于数据访问却对我帮助不小。

源文件下载

1、适合更新的存储过程映射到C#


2、适合返回结果集的存储过程映射到C#


3、开发上建议(可看可不看):

将生成代码拷贝下来,放入到一个DA_Helper

它有下面这些成员

        private SqlCommand itsCommand;

        private SqlConnection itsConn;

        public SqlConnection ItsConn

        {

            get { return itsConn; }

            set { itsConn = value; }

        }

        private DataSet itsDataSet = new DataSet();

        public DataSet ItsDataSet

        {

            get { return itsDataSet; }

            set { itsDataSet = value; }

        }

        public DA_Helper (SqlConnection itsConn)

        {

            this.itsConn = itsConn;

        }

外部使用时,先实化一个DA_Helper类,然后调用生成的方法即可,对于上述第一种情况,可直接调用,对于第二种情况,调用后,访问ItsDataSet属性即可。

特点:

1、根据存储过程名,会自动识别存储过程参数个数,每个参数名称,类型,长度,及是否为out参数,使之产生对应的C#方法,存储过程中参数类型一一对应到C#中应有的类型。在开发中,利用VS智能感知就可以进行参数识别了,让存储过程的调用与调用C#中的实例方法一样简单。

2、自动会为存储过程建立一个返回值,但你的存储过程的结果是以这种方式设计的话,它同样满足你的要求,在这个时候,你只需要将对应C#方法的返回值赋给某一整型变量即可。

缺点:

1、 没有CodeSmith强大,没有EnterpriseLibrary系统

2、 SQL Server2000不存在错误,因为SQL Server2005多了自定义类型并且存储过程命名时多了“架构”的概念,也许存在不正常现象。这一块也希望有人能够改进,并与我分享。

优点:

直接映射存储过程到C#方法,并有强类型参数支持编译时检查(EnterpriseLibrary的数据访问应用程序块是采用“动态参数发现params object[]”的机制,没有类型检查,会出现编译时不出错,但运行时可能出错的现象)

posted @ 2008-03-19 10:12 意欲凌风翔 月照一孤舟 阅读(372) | 评论 (0)编辑

2008年3月6日 #

 

Plug-In系统最重要的特点就是实现在运行时动态加载所需Plug-In对象,使程序解耦,黄忠成介绍Plug-In系统时提出三个要求:

一、对Plug-In对象的声明;

二、Plug-In系统与Plug-In对象的沟通契约;

三、不同Plug-In对象契约有级别之分,针对UserControlPlug-In对象与针对FormPlug-In对象的契约是不同的。

由于本人所做项目的业务逻辑需求,经常阶段性增加一些模块,但模块是有着层次性与相似度的。

例如:一份体检报告的一级菜单可能由:一般检查、体格检查、实验室检查、器械检查等组成,针对实验室检查这一项又由血常规、尿检、心电图等不同项目组成。以上不管是一级菜单还是二级项目都有些不同的数据格式与表现形式。

根据医院或者市场需求,新增新的菜单或新的项目是不可避免的,由于这些新增的东西与已存在的功能相对还是独立的,所以如果你作为单独模块再次开发一次,也是无可厚非,也不至于形成意大利面条,但我要说的是,你如何保证代码的重用、更好的维护,一些共性的东西还是会牵一发而动全身。

此时Plug-In系统就是我们需要的,经实践,下述的Plug-In系统已在项目中成功运用。

还是针对黄忠成提出的三个要求,对这个Plug-In系统进行介绍一下:

首先、对Plug-In对象的声明是放在配置文件中,.NET配置文件给我们提供一些自定义的sections配置节,可以在那里面对我们所需要的程序集与类的信息进行描述。

其次、Plug-In系统与Plug-In对象的沟通是存在一个Plug-In对象的父类,该父类采用Template Method对其基本业务逻辑进行描述,除了便于沟通,另一个优点是保持了Plug-In对象该有的共性。

关于Plug-In对象契约级别,这里只对UserControlPlug-In对象进行介绍。需要说明的是,在此Plug-In定义参考了黄忠成的说法,具体实践还是有一些区别的。

实作时的思路:(部份源代码)

一、根据业务逻辑定义一个Plug-In对象的父类Report_JianCe_Panel,保持好应有的共性:包含一些保护字段给子类使用;包含基本业务流程:采用Template Method包含一些虚方法让子类去覆写。

       public partial class Report_JianCe_Panel : UserControl

    {

       public Report_JianCe_Panel()

        {

            InitializeComponent();

        }

        ///<summary>

        ///给子类使用

        ///</summary>

        protected string itsReportID;

        ///<summary>

        ///报告单号,给外界使用

        ///</summary>

        public string ItsReportID

        {

            get { return itsReportID; }

            set { itsReportID = value; }

        }

        protected static Hashtable itsDataSets = new Hashtable();

       

        protected string itsItem;

        ///<summary>

        ///所检测的项目

        ///</summary>

        public string ItsItem

        {

            get { return itsItem; }

            set { itsItem = value; }

        }

        ///<summary>

        ///需要加载的数据源

        ///</summary>

        protected DataSet itsDataSource;

        ///<summary>

        ///模板方法,主业务逻辑

        ///</summary>

        public void CreatePanel()

        {

            //一加载数据源

            LoadDataSource();

            //二针对数据进行格式化

            PaintPanel();

        }

        protected virtual void LoadDataSource()

        {

            //共性操作

        }

        protected virtual void PaintPanel()

        {

            //共性操作

        }

二、根据父类对创建各自业务需求的子类,比如JianCe_Panel_1,覆写父类的虚方法时还需要根据情况决定是否先行调用一下base

public partial class JianCe_Panel_1 :Report_JianCe_Panel

    {

        public JianCe_Panel_1()

        {

            InitializeComponent();

        }

               

        ///<summary>

        ///具体实作时,最好使用类型化DataSet

        ///</summary>

        protected override void LoadDataSource()

        {

            if (!itsDataSets.Contains(this.ItsItem))

                itsDataSets.Add(this.ItsItem, LoadJianCe_PanelData());

            this.itsDataSource = (DataSet) itsDataSets [this.ItsItem];           

        }

        protected override void PaintPanel()

        {

            //覆写父类的方法

        }

        //其他自已的操作与逻辑

    }

三、将需要Plug-In对象的类写入配置文件,以后会根据配置文件的信息生成Menu或者TreeView

<framework>

 <sections>

<sectionhandler="程序集名1,类名1"/>

<sectionhandler="程序集名2,类名2"/>

 </sections>

</framework>

四、建立一个创建Plug-In对象的类,姑且叫它Report_JianCe_PanelFactory,它的作用是根据配置文件中程序集与类的信息,按需delay load使用reflection创建该TypeInstance,因为reflection时速度是受影响的,所在缓存Assembly是必要的。

1、加载并缓存Type

    public static class TypeResolverHelper

    {

        private static Hashtable _assemblyCaches = null;

        ///<summary>

        ///根据配置节信息取得相关程序集

        ///</summary>

        ///<param name="assemblyString">配置节的内容</param>

        ///<returns></returns>

        public static Type ResolveType(string assemblyString)

        {

            if (_assemblyCaches == null)

                _assemblyCaches = new Hashtable();

            if (assemblyString != null && assemblyString.Length > 0)

            {

                string[] s = assemblyString.Split(',');

                Assembly assem = (Assembly)_assemblyCaches[s[1].Trim()];

                if (assem == null)

                {

                    assem = Assembly.LoadWithPartialName(s[1].Trim());

                    if (!_assemblyCaches.Contains(s[1].Trim()))

                    {

                        lock (_assemblyCaches.SyncRoot)

                        {

                            _assemblyCaches.Add(s[1].Trim(), assem);

                        }

                    }

                }

                return assem.GetType(s[0]);

            }

            return null;

        }

    }

2、加载并缓存具体对象

     public sealed class Report_JianCe_PanelFactory

    {

        //用于存放类名与类型信息

        private Dictionary<string, object[]> _plugIns = new Dictionary<string, object[]>();

        private static Report_JianCe_PanelFactory _instance;

       

        ///<summary>

        /// Singleton

        ///</summary>

        public static Report_JianCe_PanelFactory Instance

        {

            get

            {

                lock (_lock)

                {

                    if (_instance == null)

                        _instance = new Report_JianCe_PanelFactory();

                    return _instance;

                }

            }

        }

        ///<summary>

        ///取得反射出来的具体类

        ///</summary>

        ///<param name="id"></param>

        ///<returns></returns>

        public Report_JianCe_Panel this[string id]

        {

            get

            {

                if (!_plugIns.ContainsKey(id)) return null;

                object[] o = (object[])_plugIns[id];

                if (o[1] == null)

                {

                    Report_JianCe_Panel obj =

                         (Report_JianCe_Panel)Activator.CreateInstance((Type)o[0], false);

                    if (obj != null)

                        o[1] = obj;//将产生出的对象缓存

                }               

                return (Report_JianCe_Panel)((object[])_plugIns[id])[1];

            }

        }

        public bool Contains(string id)

        {

            return _plugIns.ContainsKey(id);

        }

    }

五、主业务逻辑中使用Plug-In对象的父类,并将factory创建出来的实例赋给它。

Report_JianCe_Panel panel = Report_JianCe_PanelFactory["mypanel"];

至此,我们就可以针对panel进行相关操作了,既保持了共性又保持松耦合,利于扩展与维护。

希望各位朋友进行交流与指导!

很少写博客,那位朋友知道如何展折代码,请告之,谢谢!

参考资料:

黄忠成《Framework的设计与应用》

posted @ 2008-03-06 10:03 意欲凌风翔 月照一孤舟 阅读(215) | 评论 (0)编辑

 

    授权管理是软件中很重要的一部份,网上有许多精彩文章,包括自建数据库达到这样一个目的,我在此主要是讲述针对.NET2.0平台提供的机制进行开发。这里的授权管理不包括身份验证,而只是针对已有的特定身份决定他可以访问的资源与使用的功能。

一、             从面向方面谈谈授权管理

    谈到授权管理,它像日志机制、帮助机制等一样,在架构分析中不属于直接需求的功能部份,而是缠绕在各个功能模块中,很难使用OOA/OOD的方法对它进行分析设计,按照Ivar Jacobson (UML三友中之一)提出来的观点,这一部份叫基础结构,他使用AOSD(一种基于用例的面向方面的技术)达到分离关注点,使功能模块开发与基础结构开发分开,在用例建模的时候使用extend扩展来描述它们之间的关系。

    一般情况下对于用例文档中描述的extend扩展点,本人在设计时是倾向于使用event来达到扩展需求,但对于授权机制这种扩展用例执行后的结果影响基本用例的时候,event机制是不适宜的,也许可以考虑delegate机制。但.NET为我们提供了更耦合的面向方面的机制: Attribute,为什么不用呢?

二、             声明式编程Attribute

    首先说明一下什么是Attribute,举两个常见例子,在Web Service中,如果我们需要将一个类的实例能够返回给请求方的时候,我们会在这个类上面加一个[Serializable()]Attribute

[Serializable()]       

public class TestSimpleObject {

//一些属性

}

表明这个类的实例是可以序列化为流的。

      Attribute可以在许多类型上贴上标签,比如字段、接口、方法等,具体可以参考MSDN,下面举个贴在方法上的例子:

    单元测试中,如果你想针对某一个功能模块设计了一个测试用例,让测试框架去跑,这时候你得给这个测试方法贴一个[TestMethod()]Attribute,否则测试管理器不会认识你所写的这个测试方法。

[TestMethod()]

        public void Money_testCurrency()

        {

            Assert.AreEqual("USD", Money.doller(1).currency());

            Assert.AreEqual("CHF", Money.franc(1).currency());

        }

    我们可以自己写Attribute,然后把它贴在目标类型上,但必须自己再写一个consumer (消费者)来读取自定义Attribute中的信息,这时候一般会用到Reflection。但针对授权机制而言,.NET2.0已经为我们定义了一部份授权相关的Attribute,一般情况下就够使用了。在展示这个Attribute之间,我们先说明几个概念。

三、             主体的标识与角色

    在给使用软件的用户进行分类时,我们会提到角色,什么是角色呢,MSDN上是这么定义的“角色是指在安全性方面具有相同特权的一组命名主体”,这样我们就不必给每个用户去分派不同的权限了,只需要建立几种角色就可以了。

    在ASP.NET中,我们可以很轻松的利用它自带的成员管理机制去分派不同的角色,WinForm中我们也可以进行编程的方式,将得到的用户主体标识,查询并构造出他所属的角色。在我的其他文章中将会详细说明一下这种身份验证机制。

    目前我们的假设是已得到登录用户的主体标识与角色。

四、             如何针对不同的角色控制他的操作行为

      PrincipalPermissionAttribute给我们提供这样一种机制,凡是贴上它的成员方法将必须满足它所允许的角色才能访问这个成员方法。

     [PrincipalPermissionAttribute(SecurityAction.Demand, Role = "User")]

      public static void PrivateInfo()

      {  

         //Print secret data.

         Console.WriteLine(""n"nYou have access to the private data!");

      }

    上例中是必须满足角色为“User”的用户才具有访问这个成员方法的权限,其他角色的用户访问时将会抛出安全异常,这时候,如果我们捕获异常,并针对它进行处理,就可以达到控制不同角色进行同样操作,但出现不同行为结果。

    但是不是每个类的成员方法都这样去做呢,当然不是,那样太低效了,以至于我怀疑是不是真正做到了关注点的分离。我的实践是,针对每个功能模块,会设计出针对每个任务的Façade模式,在这个Façade的类中定义这些安全代码。

    不是太难是不是,比delegate更好的达到了松耦合,维护也是相当的简单。但是,这只是功能模块与授权机制的分离,界面怎么办?

五、             ASP.NET中其他的实践

1、页面导航信息处理

    其实当我们访问页面导航栏(TreeViewMenu)时,建议使用Web.sitemap提供的数据源,这是一个XML文件,我们可以设置SiteMapNode属性roles用于此节点哪些角色可见,并在Web.Config配置文件中相应的配置节中设置一下securityTrimmingEnabled="true",这样便可达到不同角色的用户见到的导航信息是不一样的。

2、具体页面的访问性

    其实在ASP.NET已为开发人员提供了很好的基于角色的安全访问机制,还记得我们可以给每个网站下的文件夹允许或拒绝相关角色访问吗!

    我们可以针对不同角色区别一下共有任务与特殊任务,设计好页面流程图,将不同任务分别做成UserControl(粗细粒度根据具体情况而定),这些UserControl是页面的基本元素,由它们组装成不同的页面,根据角色具有的权限放在对应访问权限的文件夹下面,确保该权限的用户只能访问这些页面,同时保证了UserControl重用,缺点就是必须得针对多个角色建立多个文件夹。有更好的方案望不吝指教!

结语

    开通博客已半年多了,但这是第一篇文章,文笔不好,技术有限,更不会排版,望各位朋友海函!
posted @ 2008-03-06 09:54 意欲凌风翔 月照一孤舟 阅读(639) | 评论 (0)编辑