自定义 Fluent NHibernate 成员资格和角色提供程序

分享于 

16分钟阅读

Web开发

  繁體

介绍

本文的目的是提供使用 Fluent NHibernate的微软( 和角色) 提供程序的成员资格实现。 NHibernate 提供了一个简化的NHibernate 实现,而不使用 XML(hbm) 映射文件。 点击这里了解有关 NHibernate的更多信息。

如果我在代码中发现明显的错误,请告诉我,我会尝试修复这些错误。

背景

关于流畅hibernate的背景,请阅读 Fluent NHibernate Wiki。

环境

这个应用程序是使用VS2008和 SQL Server 2005开发的,代码是用 C# 编写的。 这个应用程序也应该在VS2005中工作,虽然我没有在VS2005中测试它。

基础知识

microsoft提供程序提供了一种在 ASP.NET 应用程序中集成用户管理的简单方法。 但是,如果不使用 ms sql server或者不希望处理默认提供的表和存储过程分数,则必须提供提供程序的自定义实现以满足你的需求。

本文介绍了成员资格和角色提供者的基本流畅 NHibernate 实现。 我们将实现一个简化的提供程序,它只使用实际提供程序所使用的表的子集。

数据库

在这个实现中,我只使用了三个表 UsersRolesUsersInRoles。 下表显示了这些表的结构和定义:

Fluent-MembershipProvider/database.JPG

" Users"表中列出了系统中所有用户的列表,并且" Role"表包含所有角色。 UsersInRoles is许多表,其中包含 UsersRole 关联的表。 在提供者的to实现中,主键是 guid,但在我们的情况下,我已经用了简单的整数。

数据库脚本作为源代码的一部分包含在其中。

流畅的NHibernate 实现

如果你不熟悉流行的NHibernate,我建议通过基本的示例项目来理解这个 section.This的NHibernate 解决方案。

Fluent-MembershipProvider/solution.JPG 实体

实体包含用户和角色的业务域表示。 用户有一个用于添加和删除角色的构造函数和方法。 这些方法还从反向关系中添加/删除。 用户拥有对它所拥有角色的引用,角色会引用有多少用户与角色相关联。 这里使用 Utils.MinDate() 来设置数据库(。SQL Server 中的)的默认最小日期。

publicclass Users
 {
 publicvirtualint Id { get; privateset; }
 publicvirtualstring Username { get; set; }
 publicvirtualstring ApplicationName { get; set; }
 publicvirtualstring Email { get; set; }
 publicvirtualstring Comment { get; set; }
 publicvirtualstring Password { get; set; }
 publicvirtualstring PasswordQuestion { get; set; }
 publicvirtualstring PasswordAnswer { get; set; }
 publicvirtualbool IsApproved { get; set; }
 publicvirtual DateTime LastActivityDate { get; set; }
 publicvirtual DateTime LastLoginDate { get; set; }
 publicvirtual DateTime LastPasswordChangedDate { get; set; }
 publicvirtual DateTime CreationDate { get; set; }
 publicvirtualbool IsOnLine { get; set; }
 publicvirtualbool IsLockedOut { get; set; }
 publicvirtual DateTime LastLockedOutDate { get; set; }
 publicvirtualint FailedPasswordAttemptCount { get; set; }
 publicvirtualint FailedPasswordAnswerAttemptCount { get; set; }
 publicvirtual DateTime FailedPasswordAttemptWindowStart { get; set; }
 publicvirtual DateTime FailedPasswordAnswerAttemptWindowStart { get; set; }
 publicvirtual IList<roles> Roles { get; set; }
 public Users()
 {
 this.CreationDate = Utils.MinDate();
 this.LastPasswordChangedDate = Utils.MinDate();
 this.LastActivityDate = Utils.MinDate();
 this.LastLockedOutDate = Utils.MinDate();
 this.FailedPasswordAnswerAttemptWindowStart = Utils.MinDate();
 this.FailedPasswordAttemptWindowStart = Utils.MinDate();
 this.LastLoginDate = Utils.MinDate();
 }
 publicvirtualvoid AddRole(Roles role)
 {
 role.UsersInRole.Add(this);
 Roles.Add(role);
 }
 publicvirtualvoid RemoveRole(Roles role)
 {
 role.UsersInRole.Remove(this);
 Roles.Remove(role);
 }
 } 

Role ( 有关详细信息,请参见项目文件) 存在类似的实体类。

映射

映射文件夹包含与XML映射相同的Fluent。 在 UsersMap"有 has"中通过" UsersInRoles"表标识到角色的关系。 相反,RolesMap HasManyToMany 与" UsersInRoles"有逆关系。

publicclass UsersMap: ClassMap<users>
 {
 public UsersMap()
 {
 Id(x => x.Id);
 Map(x => x.Username);
 Map(x => x.ApplicationName);
 Map(x => x.Email);
 Map(x => x.Comment);
 Map(x => x.Password);
 Map(x => x.PasswordQuestion);
 Map(x => x.PasswordAnswer);
 Map(x => x.IsApproved);
 Map(x => x.LastActivityDate);
 Map(x => x.LastLoginDate);
 Map(x => x.LastPasswordChangedDate);
 Map(x => x.CreationDate);
 Map(x => x.IsOnLine);
 Map(x => x.IsLockedOut);
 Map(x => x.LastLockedOutDate);
 Map(x => x.FailedPasswordAttemptCount);
 Map(x => x.FailedPasswordAnswerAttemptCount);
 Map(x => x.FailedPasswordAttemptWindowStart);
 Map(x => x.FailedPasswordAnswerAttemptWindowStart);
 HasManyToMany(x => x.Roles )
. Cascade.All()
. Table("UsersInRoles");
 }
 }

Role ( 有关详细信息,请参阅解决方案文件) 存在类似的映射类。

成员资格提供程序

成员资格提供程序的实际实现在类 FNHMembershipProvider 中。 此类从 Security.MembershipProvider 类继承。 它为基类中可用的所有方法提供重写。 在这个类中,我通过 NHibernate 会话工厂将所有数据访问代码替换为流畅的数据访问代码。 我们将在这个类中实现它的他几种方法,这里我们将看到两个方法。 在 GetUserNameByEmail 中,我们看到一个示例,该示例使用 NHibernate 查询条件,返回强类型实体用户:

publicoverridestring GetUserNameByEmail(string email)
 {
 Entities.Users usr = null;
 using (ISession session = SessionFactory.OpenSession())
 {
 using (ITransaction transaction = session.BeginTransaction())
 {
 try {
 usr = session.CreateCriteria(typeof(Entities.Users))
. Add(NHibernate.Criterion.Restrictions.Eq("Email", email))
. UniqueResult < Entities.Users>();
 }
 catch (Exception e)
 {
 if(WriteExceptionsToEventLog)
 WriteToEventLog(e, "GetUserNameByEmail");
 thrownew ProviderException(exceptionMessage);
 }
 }
 }
 if (usr == null)
 returnstring.Empty;
 elsereturn usr.Username; ;
 }

CreateUser 过程创建新的成员资格用户,它使用 Session.Save 将用户信息保存到数据库:

publicoverride MembershipUser CreateUser(string username,
 string password,
 string email,
 string passwordQuestion,
 string passwordAnswer,
 bool isApproved,
 object providerUserKey,
 out MembershipCreateStatus status)
 {
 ValidatePasswordEventArgs args =
 new ValidatePasswordEventArgs(username, password,true);
 OnValidatingPassword(args);
 if (args.Cancel)
 {
 status = MembershipCreateStatus.InvalidPassword;
 returnnull;
 }
 if (RequiresUniqueEmail && GetUserNameByEmail(email)!= "")
 {
 status = MembershipCreateStatus.DuplicateEmail;
 returnnull;
 }
 MembershipUser u = GetUser(username, false);
 if (u == null)
 {
 DateTime createDate = DateTime.Now;
 //provider user key in our case is auto intusing (ISession session = SessionFactory.OpenSession())
 {
 using (ITransaction transaction = session.BeginTransaction())
 {
 Entities.Users user = new Entities.Users();
 user.Username = username;
 user.Password = EncodePassword(password);
 user.Email = email;
 user.PasswordQuestion = passwordQuestion;
 user.PasswordAnswer = EncodePassword(passwordAnswer);
 user.IsApproved = isApproved;
 user.Comment = "";
 user.CreationDate = createDate;
 user.LastPasswordChangedDate = createDate;
 user.LastActivityDate = createDate;
 user.ApplicationName = _applicationName;
 user.IsLockedOut = false;
 user.LastLockedOutDate = createDate;
 user.FailedPasswordAttemptCount = 0;
 user.FailedPasswordAttemptWindowStart = createDate;
 user.FailedPasswordAnswerAttemptCount = 0;
 user.FailedPasswordAnswerAttemptWindowStart = createDate;
 try {
 int retId = (int)session.Save(user);
 transaction.Commit();
 if ((retId <1))
 status = MembershipCreateStatus.UserRejected;
 else status = MembershipCreateStatus.Success;
 }
 catch(Exception e)
 {
 status = MembershipCreateStatus.ProviderError;
 if(WriteExceptionsToEventLog)
 WriteToEventLog(e, "CreateUser");
 }
 }
 }
 //retrieve and return user by user namereturn GetUser(username, false);
 }
 else status = MembershipCreateStatus.DuplicateUserName;
 returnnull;
 } 
RoleProvider

角色提供程序 FNHRoleProvider的实现涉及重写 Security.RoleProvider 类。 它具有创建/删除角色。向角色添加/删除用户以及查询角色的方法。 下面显示的方法基于给定的角色名称创建了一个角色。

publicoverridevoid CreateRole(string rolename)
 {
 if (rolename.Contains(","))
 thrownew ArgumentException("Role names cannot contain commas.");
 if (RoleExists(rolename))
 thrownew ProviderException("Role name already exists.");
 using (ISession session = SessionFactory.OpenSession())
 {
 using (ITransaction transaction = session.BeginTransaction())
 {
 try {
 Entities.Roles role = new Entities.Roles();
 role.ApplicationName = this.ApplicationName;
 role.RoleName = rolename;
 session.Save(role);
 transaction.Commit();
 }
 catch (OdbcException e)
 {
 if (WriteExceptionsToEventLog)
 WriteToEventLog(e, "CreateRole");
 elsethrow e;
 }
 }
 }
 }

每个提供者都有 initialize 方法,它从 web.config 文件读取配置值,该文件使用缺省设置初始化每个提供者。

这里 static 类用于配置与数据库的连接并创建会话工厂。 我使用到 SQL Server 2005的连接,但你可以轻松地将数据库更改为任何它的他任何受支持的数据库( MySql,Oracle,Jet,MsSqlCe,PostGre,SqlLite )。 这就是 NHibernate的优点。 你可以在这里阅读关于Fluent的数据库配置的更多信息,请参见参考资料。
publicstatic ISessionFactory CreateSessionFactory(string connstr)
 {
 return Fluently.Configure()
. Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005
. ConnectionString(connstr)
 )
. Mappings(m => m.FluentMappings.AddFromAssemblyOf
 <inct.fnhproviders.membership.fnhmembershipprovider>())
. BuildSessionFactory();
 }

示例应用程序

这是一个示例应用程序( FNHCustomProviders.SampleApp ),它演示了流畅的NHibernate 成员资格提供程序的使用。 应用程序包含用于执行以下操作的简单aspx页:

  • 创建用户
  • 管理密码/忘记密码
  • 管理用户和角色

管理用户和角色页允许你添加/删除角色,并将/删除用户分配/删除角色。

Fluent-MembershipProvider/application.JPG

连接各个部分

  • 数据库:使用脚本创建数据库和表
  • FNH成员资格提供程序:编译为 INCT.FNHProviders.dll。 你应该在你的 bin文件夹中提供以下支持程序集,以便成功构建。
  • Antlr3.Runtime.dll
  • Castle.Core.dll
  • Castle.Core.xml
  • Castle.DynamicProxy2.dll
  • Castle.DynamicProxy2.xml
  • FluentNHibernate.dll
  • Iesi.Collections.dll
  • Iesi.Collections.xml
  • INCT.FNHProviders.dll
  • log4net.dll
  • log4net.xml
  • nhibernate-mapping.xsd
  • NHibernate.ByteCode.Castle.dll
  • NHibernate.ByteCode.Castle.xml
  • NHibernate.dll
  • NHibernate.xml
  • FNHCustomProviders.SampleApp: 需要对 FluentNHibernateFNHCustomProviders的引用。 你应该在 webconfig 中设置以下几节来运行应用程序。 这应该在 system.web。
  • 如果要使用电子邮件密码功能,则需要在其中设置邮件设置部分:

Fluent-MembershipProvider/XML.JPG

  • 最后,你需要更改连接字符串以指向你自己的数据库。

更新

我已经向现有项目添加了配置文件提供程序。 我所实现的概要是简单的,但足以满足大多数需求。 它作为单独的zip文件 MembershipWithPorfileProvider-Part2.zip 添加到项目中,并包含成员。角色和概要文件提供者。 这还包含要修改的web配置节以及用于配置文件的附加表。 寻找其他的settings.txt,在zip中。 它包含web配置更改和表脚本。

更新配置文件提供程序

我认识到,关于配置文件提供程序的使用存在一些问题。 我添加了一个带有用法示例的zip文件。 要增加项目的属性( 例如在 Web.Config 中添加UserProfileBase类( 用法 profile.zip ) ( b ),请使用配置文件提供程序的继承属性指向UserProfileBase的位置,以便使用类似的方法:

//get a profile  UserProfileBase profile = UserProfileBase.GetUserProfile(Page.User.Identity.Name);
 string i = profile.UserName;//creates profile ; profile.City = "stl";
 profile.BirthDate = System.DateTime.Now.AddYears(-30);
 profile.FirstName = "Suhel";
 profile.Gender = "M";
 profile.Language = "English";
 profile.LastName = "s";
 profile.Occupation = "Director";
 profile.State = "mo";
 profile.Street = "123 main";
 profile.Subscription = "yes";
 profile.Website = "www.incedeit.com";
 profile.Zip = "1234";
 profile.Country = "US";
 profile.Save();

引用

历史记录

  • 29th 2010年01月: 初始帖子
  • 第七个 2010年06月: 文章已经更新

相关文章