用户实体类User
数据操作类UserDao。实现AdoDaoSupport接口。
SQL资源文件CommandSQL.resx
逻辑功能借口类 IGeneralService。
逻辑功能类 GeneralService,实现IUserService接口。
InterceptorMethod, 实现IMethodInterceptor接口, 用于在方法执行前后,附加功能。
InterceptorThrow, 实现IThrowsAdvice接口, 用于异常处理。
InterceptorTransaction, 实现IAfterReturningAdvice接口, 用于事物处理。
GeneralAttribute继承System.Attribute。用户标记需要代码注入的方法。
编写InterceptorMethod类,继承Attribute接口。可以在这个类中声明一些常量来标识,通过[InterceptorMethod (RequireUserLogon=true)] 来初始化标识。
使用时,在方法前添加标记[InterceptorMethod]
有四种代码注入形式:
接口名 |
注入方式 |
需要实现的方法 |
IMethodInterceptor |
头尾注入 |
Invoke |
IAfterReturningAdvice |
结尾注入 |
AfterReturning |
IThrowsAdvice |
抛异常注入 |
AfterThrowing(不是接口方法) |
IMethodBeforeAdvice |
开始注入 |
|
下面写出三种常用代码注入。
⑴ InterceptorMethod.cs
执行方法前注入,一般用于方法执行前进行检查Session和记录日志等。
namespace Li.Aspects { /// <summary> /// 代码注入,前后型 /// </summary> public class InterceptorMethod : IMethodInterceptor { #region IMethodInterceptor 成员 public object Invoke(IMethodInvocation invocation) { //得到当前实例化的ServiceMethodAttribute类的对象 var attribute = (invocation.Method.GetCustomAttributes(typeof(GeneralAttribute), false).FirstOrDefault() as GeneralAttribute); User currentUser = (HttpContext.Current.Session["User"] as User); if (attribute != null && attribute.RequireUserLogon == true && currentUser == null) { throw new SessionTimeoutException(); } return invocation.Proceed(); } #endregion } }
⑵ InterceptorThrow.cs,用于处理程序中的异常。
namespace Li.Aspects { public class InterceptorThrow : IThrowsAdvice { public void AfterThrowing(MethodInfo method, object[] args, object target, Exception exception) { if (exception != null) { throw exception; } } } }
⑶ InterceptorTransaction.cs,用于事务处理。
namespace Li.Aspects { /// <summary> /// 方法后注入,用于进行事物管理 /// </summary> public class InterceptorTransaction : IAfterReturningAdvice { #region IAfterReturningAdvice 成员 public void AfterReturning(object returnValue, System.Reflection.MethodInfo method, object[] args, object target) { var attribute = (method.GetCustomAttributes(typeof(TransactionAttribute), false).FirstOrDefault() as TransactionAttribute); if (attribute != null && attribute.ReadOnly == false && (method.ReturnType == typeof(bool) && bool.Equals(returnValue, false) || method.ReturnType == typeof(decimal) && decimal.Equals(returnValue, -1) || method.ReturnType == typeof(int) && int.Equals(returnValue, -1))) { TransactionInterceptor.CurrentTransactionStatus.SetRollbackOnly(); } } #endregion } }
用来标记,是否需要检查session。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Li.Attributes { /// <summary> /// 标记类,用于标记代码注入 /// </summary> public class GeneralAttribute : Attribute { public GeneralAttribute() { RequireUserLogon = true; } public Boolean RequireUserLogon { set; get; } } }
使用Spring提供的类TransactionAttribute
1,在方法上添加标记
标记为ReadOnly = true,只读事物
标记为TransactionPropagation.Required,写操作事物。
2,事物处理类
每个方法执行完毕后,都会执行InterceptorTransaction类中的AfterReturning方法,通过方法的一些条件(如是否有
TransactionAttribute标签,返回值等)判断是否是否回滚。
一些判断条件:
a) 当attribute不为空
b) 当attribute不是只读事物
c) 当method.ReturnType类型是boo,值是false
d) 当method.ReturnType类型是数值类型,值是-1
则表示操作失败,进行回滚。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Spring.Data.Generic; using Timecard.Data.Domain; using System.Data; using Oracle.DataAccess.Client; using Timecard.Data.Resources; namespace Timecard.Data.Access { /// <summary> /// 用户,数据操作类 /// </summary> class UserDao : AdoDaoSupport { #region 构造方法 public UserDao() { } #endregion /// <summary> /// 根据用户名,得到用户对象 /// </summary> /// <param name="userName"></param> /// <returns></returns> public User GetUserForUserName(string userName) { DataTable dataTable = AdoTemplate.Execute<DataTable> ( delegate(IDbDataAdapter dataAdapter) { if (dataAdapter is OracleDataAdapter) { OracleCommand commandToUse = (dataAdapter.SelectCommand as OracleCommand); commandToUse.CommandText = CommandSQL.GetUserForUserName; commandToUse.BindByName = true; commandToUse.Parameters.Add(":USER_NAME", OracleDbType.NVarchar2, ParameterDirection.Input).Value = userName; DataSet dataSet = new DataSet(); dataAdapter.Fill(dataSet); return dataSet.Tables[0]; } else { throw new Exception("Dao内部出现错误"); } } ); return (from dataRow in dataTable.AsEnumerable() select new User { UserKey = dataRow.Field<decimal?>("USER_KEY"), UserName = dataRow.Field<string>("USER_NAME"), Password = dataRow.Field<string>("PASSWORD"), RoleKey = dataRow.Field<decimal>("ROLE_KEY"), Person = new Person { PersonKey = dataRow.Field<decimal?>("PERSON_KEY"), PersonName = dataRow.Field<string>("PERSON_NAME").toNullString(), PersonState = dataRow.Field<decimal?>("PERSON_STATE"), PersonNickname = dataRow.Field<string>("PERSON_NICKNAME").toNullString(), PersonPhoto = dataRow.Field<byte[]>("PERSON_PHOTO").Base64Encode(), UnitKey = dataRow.Field<decimal?>("UNIT_KEY") } }).FirstOrDefault(); } } }
GetUserForUserName的SQL写法:
/* Formatted on 2010-5-27 9:24:29 (QP5 v5.114.809.3010) */ SELECT U.USER_KEY, U.PERSON_KEY, U.USER_NAME, U.PASSWORD, U.ROLE_KEY, P.PERSON_NAME, P.PERSON_STATE, P.PERSON_NICKNAME, P.PERSON_PHOTO, P.UNIT_KEY FROM UIMS.USER_TBL U LEFT JOIN UIMS.PERSON_TBL P ON U.PERSON_KEY = P.PERSON_KEY WHERE U.USER_NAME = :USER_NAME
IGeneralService.cs基本功能接口类:
namespace Timecard.Services.Interfaces { /// <summary> /// 基本操作接口 /// </summary> public interface IGeneralService { Object Logon(string userNameString, string passwordString); Boolean Logout(); } }
服务层,登陆功能类GeneralService.cs:
namespace Timecard.Services { class GeneralService : IGeneralService { private UserDao userDao; private PersonDao personDao; public string HashInstance{ get;set;} #region 构造方法 public GeneralService(UserDao userDao, PersonDao personDao) { this.userDao = userDao; this.personDao = personDao; } #endregion #region IGeneralService 成员 /// <summary> /// 登录方法 /// </summary> /// <param name="userNameString"></param> /// <param name="passwordString"></param> /// <returns></returns> [ServiceMethodAttribute(RequireUserLogon = false), TransactionAttribute(ReadOnly = true)] public Object Logon(string userNameString, string passwordString) { try { string userName = userNameString.TrimOrNull(); User user = null; user = userDao.GetUserForUserName(userName); if (user == null) { throw new NotHasUserException(); } if (!Cryptographer.CompareHash(HashInstance, passwordString, user.Password)) { throw new InvalidPasswordException(); } else { HttpContext.Current.Session.Add("User", user); return user; } } catch { throw new InvalidPasswordException(); } } /// <summary> /// 注销 /// </summary> [ServiceMethodAttribute(RequireUserLogon=false), TransactionAttribute(ReadOnly = true)] public Boolean Logout() { HttpContext.Current.Session.Clear(); return true; } #endregion } }
文件属性,生成操作:嵌入资源。不改此项会报错。
①Service类库配置文件
<?xml version="1.0" encoding="utf-8"?> <objects xmlns="http://www.springframework.net"> <object id="generalService" type="Timecard.Services.GeneralService, Timecard.Services"> <constructor-arg ref="personDao" /> <constructor-arg ref="userDao" /> <property name="HashInstance" value="HashProvider" /> </object> </objects>
②DataAccess类库配置文件
<?xml version="1.0" encoding="utf-8"?> <objects xmlns="http://www.springframework.net" xmlns:db="http://www.springframework.net/database"> <object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"> <property name="ConfigSections" value="dbSettings" /> </object> <db:provider id="dbProvider" provider="Oracle.DataAccess.Client" connectionString="DATA SOURCE=${db.source};PERSIST SECURITY INFO=True;USER ID=${db.user};PASSWORD=${db.password};MAX POOL SIZE=100" /> <object id="adoTemplate" type="Spring.Data.Generic.AdoTemplate, Spring.Data"> <property name="DbProvider" ref="dbProvider" /> <property name="DataReaderWrapperType" value="Spring.Data.Support.NullMappingDataReader, Spring.Data" /> </object> <object id="transactionManager" type="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data"> <property name="DbProvider" ref="dbProvider" /> </object> <object id="userDao" type="Timecard.Data.Access.UserDao, Timecard.DataAccess"> <property name="AdoTemplate" ref="adoTemplate" /> </object> </objects>