using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.SqlClient; using Csla; using Csla.Data; namespace ProjectTracker.Library { [Serializable()] public class Project : BusinessBase<Project>//源自业务基类,泛型提供强类型的Save及Clone { #region Business Methods 业务方法 private byte[] _timestamp = new byte[8]; private ProjectResources _resources = ProjectResources.NewProjectResources();//当前对象的子集合对象 /// <summary> /// 主键,类的私有变量:采用加“m”前缀,例如mWorkerName;m+属性名--在本例中使用的是下划线首字母小写,这样会在复制代码和写"_"的时候代码麻烦 /// </summary> private Guid _id; [System.ComponentModel.DataObjectField(true, true)]//此特性是用来识别一下属性既是主键,又是可以唯一标识此对象的识别值(410) public Guid Id//此属性只读 { //制定方法如何实现,指定此方法不能内联,我想是提供给反射使用的一个特性。 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]//此特性并没有介绍但基类的属性都有所有默认添加 get { CanReadProperty(true);//此方法在当前用户无权读取这个属性的时候抛出SecurityException异常,你也可以通过此方法的返回值判断用户是否有权限(410) return _id; } } /// <summary> /// 项目名字 /// </summary> string _name = string.Empty; public string Name//此属性可读写(411) { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] get { CanReadProperty(true);//是否有权限读取此属性 return _name; } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] set { CanWriteProperty(true); if (value == null) value = string.Empty;//如果值是空,付给empty if (!_name.Equals(value))//如果即将付的值与当前的值不同时,赋值才是有意义的 { _name = value;//先赋值后验证的目的是,验证已经假设被验证的属性值在当前的属性中了 PropertyHasChanged();//1调用对数据验证规则的检查AddBusinessRules,2,IsDirty标记已经修改,3,出发属性更改事件提供给UI刷新的警示(411),发生变化就要调用此方法 } } } /// <summary> /// 起始时间 /// </summary> SmartDate _started; public string Started { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] get { CanReadProperty(true); return _started.Text; } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] set { CanWriteProperty(true); if (value == null) value = string.Empty; if (!_started.Equals(value)) { _started.Text = value; ValidationRules.CheckRules("Ended");//手动的激活特定的,比如这个是Ended相关的验证规则 PropertyHasChanged(); } } } /// <summary> /// 结束时间 /// </summary> SmartDate _ended = new SmartDate(false); public string Ended { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] get { CanReadProperty(true); return _ended.Text; } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] set { CanWriteProperty(true); if (value == null) value = string.Empty; if (!_ended.Equals(value)) { _ended.Text = value; PropertyHasChanged(); } } } /// <summary> /// 项目描述 /// </summary> string _description = string.Empty; public string Description { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] get { CanReadProperty(true); return _description; } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] set { CanWriteProperty(true); if (value == null) value = string.Empty; if (!_description.Equals(value)) { _description = value; PropertyHasChanged(); } } } public ProjectResources Resources //返回子对象集合的引用 { get { return _resources; } } public override bool IsValid //因为此对象有子对象,所以扩充了IsValie检查是否有效,返回true代表有效,返回false代表无效 { get { return base.IsValid && _resources.IsValid; } } public override bool IsDirty //由于有子对象,所以扩成此IsDirty方法,验证自身或者子对象是否有修改过 { get { return base.IsDirty || _resources.IsDirty; } } protected override object GetIdValue()//提供Id,是为了支持Object的,Equals().toString(),GetHashCode() { return _id; } #endregion #region Validation Rules 验证规则 //将验证规则与业务对象的属性联系在一起 protected override void AddBusinessRules() { //AddRule的第一个参数是委托变量,也就是说是方法的变量 ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired, "Name"); ValidationRules.AddRule(Csla.Validation.CommonRules.StringMaxLength, new Csla.Validation.CommonRules.MaxLengthRuleArgs("Name", 50)); ValidationRules.AddRule<Project>( StartDateGTEndDate<Project>, "Started"); ValidationRules.AddRule<Project>( StartDateGTEndDate<Project>, "Ended"); ValidationRules.AddDependantProperty("Started", "Ended", true); //base.AddBusinessRules(); } private static bool StartDateGTEndDate<T>( T target, Csla.Validation.RuleArgs e) where T : Project { if (target._started > target._ended) { e.Description = "开始日期不能晚于结束日期."; return false; } else return true; } #endregion #region Authorization Rules 授权规则 //CSLA设置授权规则时候自动调用 //也许是在CanRead,和CanWrite方法时候被调用 protected override void AddAuthorizationRules() { //制定什么属性可以由什么权限读写 //对于没有给出read权限定义的属性代表所有人都可以对其进行访问 //第二个参数是一个参数数组所以你可以为某个属性指定多个角色 //也许是在CanRead,和CanWrite方法时候被调用 AuthorizationRules.AllowWrite( "Name", "ProjectManager"); AuthorizationRules.AllowWrite( "Started", "ProjectManager"); AuthorizationRules.AllowWrite( "Ended", "ProjectManager"); AuthorizationRules.AllowWrite( "Description", "ProjectManager"); } //下面的四个方法用来为UI提供数据,告诉UI那些功能可以对当前用户隐藏 public static bool CanAddObject() { return Csla.ApplicationContext.User.IsInRole( "ProjectManager");//这个方法会判断当前用户是不是ProjectManager是返回True } public static bool CanGetObject() { return true; } public static bool CanDeleteObject() { bool result = false; if (Csla.ApplicationContext.User.IsInRole( "ProjectManager")) result = true; if (Csla.ApplicationContext.User.IsInRole( "Administrator")) result = true; return result; } public static bool CanEditObject() { return Csla.ApplicationContext.User.IsInRole("ProjectManager"); } #endregion #region Factory Methods 工厂方法 public static Project NewProject() { //看,他调用,刚才定义的CanAddObject方法去判断当前用户是否在,可以创建一个对象的权限组中,如果没有执行破除一个异常 if (!CanAddObject()) throw new System.Security.SecurityException( "当前用户没有权限添加工程"); //调用数据门户,数据访问方法 return DataPortal.Create<Project>(); } /// <summary> /// 通过ID,获得一个对象 /// </summary> /// <param name="id"></param> /// <returns></returns> public static Project GetProject(Guid id) { if (!CanGetObject()) throw new System.Security.SecurityException( "当前用户没有权限去查看工程"); return DataPortal.Fetch<Project>(new Criteria(id)); } /// <summary> /// 通过ID,删除一个对象 /// </summary> /// <param name="id"></param> public static void DeleteProject(Guid id) { if (!CanDeleteObject()) throw new System.Security.SecurityException( "当前用户没有权限删除工程"); DataPortal.Delete(new Criteria(id)); } //私有的默认构造,强迫UI通过工厂方法创建对象 private Project() { /* require use of factory methods */ } public override Project Save() { //如果已经标记为删除标记,那么会继续判断是否有删除权限 if (IsDeleted && !CanDeleteObject()) throw new System.Security.SecurityException("当前用户没有权限删除工程"); //是新的,会继续判断能否添加 else if (IsNew && !CanAddObject()) throw new System.Security.SecurityException("当前用户没有权限添加工程"); //是否可以编辑 else if (!CanEditObject()) throw new System.Security.SecurityException("当前用户没有权限更新工程"); //如果一切正常调用Save return base.Save(); } #endregion #region Data Access 数据访问 [Serializable()] private class Criteria { private Guid _id; public Guid Id { get { return _id; } } public Criteria(Guid id) { _id = id; } } //由于DataPortal_create中没有对数据库的访问,所有标记了Runlocal特性,使得该方法只需在本地运行即可 //如果代码中有访问数据库的方法,那么就不要用这个特性,使数据门户可以根据情况将此方法的运行选择在应用服务器还是本地 [RunLocal()] protected override void DataPortal_Create() { //非常值得注意的是主键是在这个时候被初始化的 _id = Guid.NewGuid(); _started.Date = DateTime.Today; //(424)检查一下验证规则,对属性的设置也会引发此操作,然后上面是直接操作私有成员变量的,所以不会引发 //在此调用CheckRules对所有的属性进行验证规则的检查,这样检查只运行一次。 ValidationRules.CheckRules(); } //可以看到此方法没有标记任何特性 //此方法要访问数据库所以不能标记本地,由于她没有任何数据库更新操作所以也无需事务性的保护 //此方法调用结束后会隐含的调用MarkOld()方法 private void DataPortal_Fetch(Criteria criteria) { //通过连接字符串建立SQL连接对象,Using符号代表,范围释放的意思,SqlConnection对象会在此范围后被释放掉 using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); //SQLCommand将在using后被释放 using (SqlCommand cm = cn.CreateCommand()) { //存储过程 cm.CommandType = CommandType.StoredProcedure; //存储过程名 cm.CommandText = "getProject"; //为存储过程添加参数 cm.Parameters.AddWithValue("@id", criteria.Id); //SafeDatareader将在using后被释放,顺路说一下SafeDataReader是CSLA定义的集成DataReader的安全Reader //提供对Null值的阻止和SmartDate数据类型的支持 //Using有两个作用, 保证对象的存在,以及在此范围内有效,过后就释放资源 using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader())) { dr.Read(); _id = dr.GetGuid("Id"); _name = dr.GetString("Name"); //GetSmartDate方法可以避免Null,以及将空置转换为何时的值 _started = dr.GetSmartDate("Started", _started.EmptyIsMin); _ended = dr.GetSmartDate("Ended", _ended.EmptyIsMin); _description = dr.GetString("Description"); //获取时间戳用于随后的更新操作 //1字段名,Fieldoffset,要装载的变量,bufferoffset,长度 dr.GetBytes("LastChanged", 0, _timestamp, 0, 8); // 将dr定位到下一个结果集 dr.NextResult(); _resources = ProjectResources.GetProjectResources(dr); } } } } //这是一个事务安全的,此种方法标记事务是非常简单且有效的 //数据门户会包裹这段代码,保证事务的完整运行成功,而且无需定义Ado的事务 [Transactional(TransactionalTypes.TransactionScope)] protected override void DataPortal_Insert() { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandText = "addProject"; //将cm做参数跳转到统一处理方法 DoInsertUpdate(cm); } } // update child objects _resources.Update(this); } [Transactional(TransactionalTypes.TransactionScope)] protected override void DataPortal_Update() { //如果是修改过的才有意义去更新此对象 if (base.IsDirty) { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandText = "updateProject"; cm.Parameters.AddWithValue("@lastChanged", _timestamp); //将cm做参数跳转到统一处理方法 DoInsertUpdate(cm); } } } // update child objects _resources.Update(this); } private void DoInsertUpdate(SqlCommand cm) { //存储过程 cm.CommandType = CommandType.StoredProcedure; //参数 cm.Parameters.AddWithValue("@id", _id); cm.Parameters.AddWithValue("@name", _name); cm.Parameters.AddWithValue("@started", _started.DBValue); cm.Parameters.AddWithValue("@ended", _ended.DBValue); cm.Parameters.AddWithValue("@description", _description); //一下要特殊介绍,此位置是定义一个从数据库存储过程中返回的参数,她的数据库类型是Timestamp,描述为Output输出的 //此属性的意思是“时间戳”,代表一条记录的不同时间下的版本,用版本的这个词很合适 SqlParameter param = new SqlParameter("@newLastChanged", SqlDbType.Timestamp); param.Direction = ParameterDirection.Output; cm.Parameters.Add(param); //执行语句无需返回值 cm.ExecuteNonQuery(); //执行后返回的值 _timestamp = (byte[])cm.Parameters["@newLastChanged"].Value; } [Transactional(TransactionalTypes.TransactionScope)] protected override void DataPortal_DeleteSelf() { //依然是调用下面的方法删除的,只是他此时可以获得自己的Id DataPortal_Delete(new Criteria(_id)); } [Transactional(TransactionalTypes.TransactionScope)] private void DataPortal_Delete(Criteria criteria) { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText = "deleteProject"; cm.Parameters.AddWithValue("@id", criteria.Id); cm.ExecuteNonQuery(); } } } #endregion #region Exists /// <summary> /// 静态方法,业务对象可以直接调用 /// </summary> /// <param name="id">要查找的业务对象id</param> /// <returns></returns> public static bool Exists(Guid id) { //本地声明对象 ExistsCommand result; //结果返回给本地,DataPortal通过数据门户调用服务器端方法在服务器执行,(NewExistscommand在本地创建), //提供类型的泛型参数用于初始化基类,参数用来执行 result = DataPortal.Execute<ExistsCommand>(new ExistsCommand(id)); return result.Exists;//将执行对象的属性结果返回 } /// <summary> /// 私有的,用于提供给上面的静态方法使用的(456) /// </summary> [Serializable()] private class ExistsCommand : CommandBase { private Guid _id;//执行需要存储的id,这样对移动对象是非常有用的 private bool _exists;//执行的结果,同样是移动对象的概念,将结果从应用服务器带回客户端 //公共属性 public bool Exists { get { return _exists; } } //构造函数 public ExistsCommand(Guid id) { _id = id; } protected override void DataPortal_Execute() { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText = "existsProject"; cm.Parameters.AddWithValue("@id", _id); int count = (int)cm.ExecuteScalar();//执行返回的数量 _exists = (count > 0); //这句语句很简化哦 } } } } #endregion } } 代码下载:CSLA3.0中文学习演示程序1.2.rar
冯瑞涛