Parichay (一个简单小巧的 Asp.Net MVC 社交网络入门项目)
Parichay (一个简单小巧的 Asp.Net MVC 社交网络入门项目)
下载次数
1. 简介和背景
您想为您的网站添加简单的消息、好友和群组互动功能,但又不想采用完整的 CMS 解决方案?您是否正在使用微软的 Asp.Net 基于角色的安全机制,并希望提供网站用户之间的互动?或者您想了解社交网络的基本知识,以便在需要时能够轻松地将它们添加到您的网站?Parichay 就是为了帮助您实现这一目标而做的尝试。
尽管《Asp.Net 社交网络》一书是一本非常棒的读物,但它仍然包含了大量信息,并且其代码包含了一个相当庞大(当然也高效)的社交网络框架。在 Parichay 中,我们将尝试遵循 KISS(Keep It Short and Simple,保持简短和简单)原则。
因此,Parichay 是一个简短而简单的 Asp.Net 社交网络和消息网站入门应用程序。该应用程序的设计遵循 KISS(保持简短和简单)原则。我们将使用 Asp.Net MVC 3(带 razor 视图引擎)作为 Web 应用程序框架。NHibernate 将被用作数据持久化框架——这将使我们的项目能够移植到多种数据库。
当然,如果您正在寻找一条成熟、经过充分验证的道路,您会更倾向于选择一个完整的 CMS 解决方案,并将其社交网络功能作为一个插件模块添加。尽管如此,本文将为您提供一些关于在开发自己的社交网络模块时可以考虑的思路。此外,由于该应用程序被设计为一个独立的模块,并且对安全层的依赖性最小,因此它可以轻松地转换为 CMS 模块。
(请注意:此应用程序没有一键安装程序。请务必转到“安装”部分了解安装信息。基本上,您需要:
- 创建一个数据库(例如,创建数据库 parichaytest)
- 使用提供的 SQL 脚本创建表(MySql => 1_ParichaySecurity.sql & 2_ParichayData.sql,SQLServer => SQLServer_DbScript.sql)
- 更新 Web.Config 中的 NHibernate 连接字符串 )**
2. 概述
如前所述,我们将使用默认的 Asp.Net 基于角色的安全机制。我们将为其添加一个简单的基于权限的安全层,作为一个 httpmodule。(有关此模块的更多信息,请参阅本文。
以下是本文和我们将要讨论的组件的基本概述:
简介和背景(已讨论)
- 概述
- 模块(表 => 数据库对象及其对应的 Controller-Actions)
- 成员详情:(用户详情、密码和密码问答管理、恢复等)
- 成员提醒:(网站活动和响应的简单追踪。)
- 成员消息:(状态更新分享和接收回复/响应。)
- 成员好友:(与系统中的其他现有/外部用户建立联系)
- 成员请求:(查找并向现有用户发送好友/群组/其他请求。)
- 成员邀请:(邀请外部好友)
- 成员群组:(讨论群组、创建公开可见/私有群组、管理群组{目前所有群组仅限管理员管理})
- 成员上传:(将图片上传到网站)
- 系统管理:(阻止/解锁/管理/删除用户、角色和权限)
- 系统日志:(网站故障的追踪)
- 兴趣点(一些有趣且可重用的代码片段)
- Using the Code
- 安装
- 历史
要快速了解表结构和控制器,请访问:
现在,让我们逐一讨论这些模块。3. 模块
3.1) 成员详情 (AccountController)
(用户详情、密码和密码问答管理、恢复等)
用户注册我们的网站后,首先要做的就是——在 **member_details
** 表中创建一个用户配置文件(代码如下)。我们整个消息系统都依赖于这个表。安全模块可以独立运行。
在我们的 Parichay.Data 模块中,只有 member_details
表依赖于 aspnet_membershipuser
表的主键。这是为了确保在 Data 模块增长时,安全模块可以继续独立工作。整个 Parichay.Data 模块可以被移植并包装到其他安全模块中(例如,如果您想将其移植到 CMS 插件)。
用户注册后,会立即为该用户创建一个空的
条目。member_de
tails
member_d
的主键来自 etails
aspnet_membershipusers
的主键(Id)。
// AccountController>>Register action
FormsService.SignIn(model.UserName, false /* createPersistentCookie */); //Signs in the user
try
{
MembershipUser thisUsr = Membership.GetUser(model.UserName,true);
//Creates a default empty profile for the user
MemberDetails toCreate = new MemberDetails();
toCreate.Id = Int32.Parse(thisUsr.ProviderUserKey.ToString());//Id should be membership userkey
toCreate.PEmail = thisUsr.Email;
toCreate.Givennm = model.Givennm;
toCreate.bShowPrvInfo = false;
Data.Helper.NHibernateHelper.Save<MemberDetails>(toCreate);
}
catch (Exception ex1)
{
Data.Helper.NHibernateHelper.Log(new Exception("Problem creating default user profile==>", ex1));
}
3.2) 成员提醒 (AlertController)
(网站活动和响应的简单追踪。)
当用户首次进入网站时,您希望提供的互动体验是关于他在系统中的活动和响应的更新。这可以使他及时了解正在发生的事情。存储此信息的表是 member_alert
。因此,从现在开始,任何针对用户的活动/响应都可以存储到此表中,并应显示在他的提醒中(在主页和“通知”部分)。这是表结构:
P_UserId
是外键,指向 member_details
表。所以,一旦您有了个人资料,您就有资格接收提醒了!!!Message 字段存储消息,Version 是提醒的时间戳。(有一个 Alert_Type_Id
字段,目前尚未与任何查找表关联,因为我们希望保持系统轻量级。此字段可用于为特定类型的活动创建提醒模板。这样我们就可以只传递参数和活动代码,模板应生成必要的消息)。
这是用于向用户发送提醒的 Alert 方法(位于 BaseController
类中):
//BaseController>>Alert
protected void Alert(int to, string message)
{
try
{
Data.Entity.MemberAlert obj = new Data.Entity.MemberAlert();
obj.Ishidden = 0;
obj.Message = message;
obj.PUser = Data.Helper.NHibernateHelper.UniqueResult<Data.Entity.MemberDetails>(null, "Id", to);
//obj.AlertType = new Alerttype() {Id=0 };
Data.Helper.NHibernateHelper.Save<Data.Entity.MemberAlert>(obj);
TempData["message"] = "Requested User Notified.";
}
catch (Exception ex1)
{
Data.Helper.NHibernateHelper.Log(new Exception("Error Notifying UserKey:" + to.ToString(), ex1));
TempData["message"] = "Unable to Notify. Error: " + ex1.Message;
}
}
我认为代码本身解释得很清楚。我们将一个提醒与主键(来自 member_details
表)关联起来。消息是我们想要发送的任何消息。默认情况下,它不会被隐藏(obj.Ishidden=0)。然后,我们调用我们的 NHibernateHelper
类来保存对象。
3.3) 成员消息 (MessageController)
(状态更新 - 分享和接收回复/响应。)
现在,用户已经注册了我们的网站,并且会收到关于他活动的提醒——我们将要给他的第一个活动是消息和回复。显然,如果发送者是用户自己,那么接收者就是他自己,这就变成了一个状态更新。用户也可以回复他的状态——在这种情况下,第二条消息将有一个 ParentId 指向第一条消息。因此,member_messages 表通过两个键——SenderId 和 RecipientId——与
表关联。 member_details
这里还有一个可空的键“ParentId”,它指向 member_messages
表的主键。这意味着除了简单的状态更新(其中 sender=recipient 且 parentId =null)之外,还将有回复。回复将具有原始消息的主键作为其 parentId。我们可以将此树状结构扩展到多个级别,但目前我们只提供两级树状结构。所有后续对状态更新(或父消息)的回复都将按日期排序。因此,最新的回复显示在底部。
下面是添加消息的代码。显而易见,回复消息也只是添加一条消息。但这次是 Parent Id 指向另一条消息,而 Recipient Id 指向父消息的 SenderId。
//MessageController>>AddMessage
public ActionResult AddMessage(MemberMessage model)
{
var result = new JsonResponse();
if (string.IsNullOrEmpty(model.Text))
{
result.isSuccessful = false;
TempData["message"] = result.errorMessage = "Message cannot be blank.";
}
else
{
//update the database
if (IsUserAuthenticated)
{
try
{
MemberDetails thisUsr = NHibernateHelper.UniqueResult<MemberDetails>(null, "Id", LoggedInUserKey);
var msg = new MemberMessage
{
Text = model.Text.Replace("\r\n", " "),
Modifiedon = DateTime.Now.ToUniversalTime(),
Createdon = DateTime.Now.ToUniversalTime(),
Sender = thisUsr,
Type = ((int)model.Type),
Isprivate = 0,
Source = "parichay",
Recipient = thisUsr
};
if (model.ParentId != null)
{
msg.ParentId = NHibernateHelper.UniqueResult<MemberMessage>(null, "Id", model.ParentId.Id);
}
if (model.Recipient != null)
{
MemberDetails recUsr = NHibernateHelper.UniqueResult<MemberDetails>(null, "Id", model.Recipient.Id);
if (recUsr == null)
{
throw new Exception("This user do not have profile information. Message cannot be sent.");
}
else
{
msg.Recipient = recUsr;
}
}
NHibernateHelper.Save<MemberMessage>(msg);
//Sending Notify Now
try
{
if (msg.Sender.Id != msg.Recipient.Id)
{
string msgUrl = "<a href='" + Url.Action("MsgDtl", "Message", new { id = msg.Id }) + "' >reply</a>";
Alert(msg.Recipient.Id, string.Format("You have received a ({0}) to from: {1}", msgUrl, msg.Sender.Givennm));
}
}
catch (Exception ex1)
{
Data.Helper.NHibernateHelper.Log(new Exception("Error Sending Message Alert=>", ex1));
}
result.Id = msg.Id.ToString();
result.isSuccessful = true;
TempData["message"] = result.responseText = "Message Sent Successfully";
}
catch (Exception excp1)
{
Data.Helper.NHibernateHelper.Log(new Exception("Error Sending Message=>", excp1));
result.isSuccessful = false;
TempData["message"] = result.errorMessage = "Failed to save message. Error: " + excp1.Message;
}
}
else
{
result.isSuccessful = false;
TempData["message"] = result.errorMessage = "You must be logged in to post a message";
}
}
if (IsJsonRequest())
{ return Json(result); }
else
{
return RedirectToAction("Index",((Parichay.AppConstants.ReturnContollerHomes)model.rUrl).ToString());
}
}
3.4) 成员好友 (FriendController)
(邀请好友加入系统,向系统中现有用户发送好友请求)
假设我们注册的用户想要连接更多用户——有两种情况:1. 用户已经 加入 系统——在这种情况下,您可以向他发送一个 “请求”。2. 用户 尚未加入——在这种情况下,您可以向他发送一个 “邀请”。
因此,一个邀请将分两步完成——首先,将向外部用户发送一个“加入”系统的邀请。一旦他加入(或注册)并确认了您的加入“邀请”——他将自动创建一个“请求”来成为您的好友(或加入您的群组——这取决于您的邀请最初来自哪里)。新用户仍然可以在注册后接受/拒绝您的好友“请求”。“请求”和 “邀请”将在下一节讨论。让我们来看看
表。member_friends
member_friends
表定义了两个用户之间的关系。 member_friends
表通过两个键 UserId
和 FriendId
指向 member_details
表。(无论你是哪一方,只要你的 ID 在这里,你们就是好友。)
只有当接收方用户“确认”了请求方的添加好友请求后,两个用户在 member_details 表中的 ID 才会被添加到这里。这发生在接收方确认请求时。在下一节(成员请求)中,您将找到当接收方按下 Connect Controller 的“AckRequest”页上的“确认”按钮时会发生什么。
在 FriendController 中,一个有趣的部分是查找用户页面,它实际上提供了发送好友请求给找到的用户的链接。这是 Find 操作的代码以及在 UsrSrch.cshtm 视图页面中使用的绑定:
//FriendController>>Find
[HttpPost]
public ActionResult Find(string givenEmail)
{
//string srchQuery = "from MemberDetails p where p.Givennm like ?";
//System.Collections.IList rtrn = Data.Helper.NHibernateHelper.Find(srchQuery, "%"+givenEmail+"%", NHibernate.NHibernateUtil.String,false);
IList<MemberDetails> rtrn = Data.Helper.NHibernateHelper.FetchProjection<MemberDetails>(new string[] { "Givennm", "Id", "PicId", "Institute", "Addr" }, "Givennm", "%" + givenEmail + "%", 0, 10, true, null, false);
ViewBag.LoggedInUserKey = LoggedInUserKey;
ViewBag.searchResults = rtrn;
return View();
}
//Friend>>UsrSrch.cshtml
@foreach (Parichay.Data.Entity.MemberDetails item in Model)
{
if (item != null)
{
<tr>
<td>.........
@Html.ActionLink("Request Friend", "VRequest","Connect",new { id = (item.Id),tId=(int)ViewBag.LoggedInUserKey, type=(int)Parichay.AppConstants.RequestTypes.Friend},null)}
.....
...</tr>
}
3.5) 成员请求 (ConnectController)
(查找并向现有用户发送好友/群组/其他请求。)
如下所示,member_r
表有两个外键——equests
senderId
和 RecipientId
映射到 member_details
表。这意味着这种互动发生在我们网站的“现有”发送者和“现有”接收者之间。一旦发送者发起“请求”,发送者和接收者都可以在“邀请与请求”面板中看到该请求。接收者可以接受/拒绝该请求。相应的提醒将显示出来。
现在,在同一个控制器中,我们有 AckRequest Action。这个 Action 执行实际的连接人的任务 :) 一旦接收方按下确认——请求的类型将被识别(是好友请求还是群组请求),并相应地创建一个相关的关系,如下所示:
//ConnectController>>AckRequest
switch (model.submitButton)
{
case ("Accept"):
{
if (source.Type == (int)AppConstants.RequestTypes.Friend)
{
return AddFriend(source);
}
else if (source.Type == (int)AppConstants.RequestTypes.Group)
{
return JoinGroup(source);
}
break;
}
3.6) 成员邀请 (ConnectController)
(邀请外部好友)
对于还没有加入我们网站的好友,系统中的现有用户可以发送邀请。目标电子邮件 ID 被记录在 member_invitations
表中。下次被邀请者加入(或注册)时——他可以确认发送者的邀请,发送者将收到通知。(member_invitation
的“Type
”字段根据您开始请求的位置设置——无论您是邀请他作为好友还是加入您的群组。TargetPageId
包含用户 ID 或目标群组的 ID)。以下是 AckInvite 方法的代码,该方法根据原始邀请的“
”创建相应的请求:Type
//ConnectController>>AckInvite
switch (model.submitButton)
{
case ("Confirm"):
{
Alert(source.Senderid.Id, string.Format("Hello {0} your freind {1} (e-mail: {2}) has accepted your invitation to join Parichay.", source.Senderid.Givennm, LoggedInUserName, source.Email));
source.BecameUserId = LoggedInUserKey;
source.Status = 0;
try
{
CreateRequest(source);
Data.Helper.NHibernateHelper.Delete<MemberInvitations>(source);
...
....//This is where the appropriate request is created after acknowledge of invitation
//ConnectController>>CreateRequest
private void CreateRequest(MemberInvitations model)
{
MemberRequests itm = new MemberRequests();
Guid val;
do
{
val = System.Guid.NewGuid();
} while (Data.Helper.NHibernateHelper.UniqueResult<MemberRequests>("Guid", "Guid", val.ToString()) != null);
itm.Guid = val.ToString();
itm.Senderid = model.Senderid;
itm.Recipientid = Data.Helper.NHibernateHelper.UniqueResult<MemberDetails>(null, "Id", LoggedInUserKey);
itm.TargetPageid = model.TargetPageid;
itm.Type = model.Type;
itm.Createdate = DateTime.Now;
itm.Status = 1;
Data.Helper.NHibernateHelper.Save<MemberRequests>(itm);
}
3.7) 成员群组 (GroupController)
(讨论群组、创建公开可见/私有群组、管理群组{由管理员})
正如我们在下图中看到的——每个 member_groups
都有 member_groupmembers
和 member_groupmessages
。member_groupmessages
通过三个外键——SenderId
、RecipientId
——与 member_
表相关联。并且,它通过第三个外键 details
GroupId
与 member_groups
表相关联。同样,我们有 member_groupmembers
表,它是 member_groups
和 member_details
之间的多对多映射表。此外,还有一个外键 OwnerId 将 member_groups 直接关联到 member_details。
(抱歉,目前所有群组都是公开的。加入群组的功能尚未启用。因此,群组邀请和请求也是如此。我会尽快启用它们。所以,就像我们邀请或请求好友一样,我们可以邀请/请求我们的好友加入我们的群组。)
GroupController 的 PostMessage
方法与上面的 MessageController 非常相似,因为其功能显然是相同的。但是,在接受群组请求后(在 ConnectController>>AckRequest 中),群组成员的创建方式如下:
//ConnectController>>JoinGroup
private ActionResult JoinGroup(MemberRequests thisReq)
{
try
{
if (thisReq.Senderid.Id == LoggedInUserKey)
{
throw new Exception("You are already a member of your group.");
}
MemberGroupmembers itm = new MemberGroupmembers();
itm.MemberDetails = Data.Helper.NHibernateHelper.UniqueResult<MemberDetails>(null, "Id", thisReq.Recipientid.Id);
itm.Group = Data.Helper.NHibernateHelper.UniqueResult<MemberGroups>(null, "Id", thisReq.TargetPageid);
itm.bRole = false;
itm.bStatus = true;
.....
Data.Helper.NHibernateHelper.Save<MemberGroupmembers>(itm);
......
}
3.8) 成员上传 (MediaController)
(您上传到网站的内容)
这个表需要稍微介绍一下。我们所有的文件上传都存放在这里。每次上传都通过 P_UserId
键与 member_details
表关联。可能有趣的是图像的缩放功能,将其转换为缩略图。以下是用于此的代码。
MediaController 中一个非常有趣的方法是 SaveAvImage,它用于将图片上传到 member_uploads 数据库。这是代码:
//MediaController>>AddAvatar public ActionResult AddAvatar() { try { SaveAvImage(); } //MediaController>>SaveAvImage foreach (string inputTagName in Request.Files) { HttpPostedFileBase file = HttpContext.Request.Files[inputTagName]; if (isImage(file.FileName, true)) { MemberDetails usr = NHibernateHelper.UniqueResult<MemberDetails>(null, "Id", LoggedInUserKey); if (file.ContentLength > 0) { TempData["message"] = "Resizing the file '" + file.FileName + "' to avatar Image now."; MemberUploads attch = new MemberUploads(); attch.Owner = usr; attch.Attachmt = file.FileName; attch.FileContentT = file.ContentType; attch.CreateD = DateTime.Now; attch.FileDetail = Utilities.CreateAvatar(100, file.InputStream); attch.FileSize = attch.FileDetail.Length; NHibernateHelper.Save<MemberUploads>(attch); ..... }}}}
Utilities>>CreateAvatar 方法取自Gunnar Peipman 的博客,在此处。用于将上传的图像调整为方形缩略图。
3.9) 系统管理 (AdminController)
(阻止/解锁/管理/删除用户、角色和权限)
如前所述,我们正在使用默认的 Asp.Net 基于角色的安全机制。在这里,我们使用了自定义的 asp.net 成员提供程序和自定义角色提供程序。这些提供程序取自此教程并进行了定制。并且,已通过 HttpModule 添加了额外的安全层,如此项目中所述。用户管理、角色、权限的 UI 屏幕可以在 Aadhaar 教程中理解。此处仅转载屏幕截图。
3.10) 系统日志(追踪您应用程序的故障)
我们系统的 Logger 使用与 Log4Net 类似的表(system_log
),但它使用 NHibernateHelper 类记录系统异常。相同的表可用于合并 log4net 和内部系统 Logger 的日志记录。
4. 兴趣点(一些有趣的的代码片段)
这里有一些您会在代码中注意到的有趣技巧(并且可能喜欢重用)。灵感来自各种来源和我自己的直觉。这要归功于非常乐于助人的 Asp.Net 社区和独立博主分享的所有宝贵信息。
4.1) 选择性数据访问(来自 NHibernateHelper 类)
NHibernateHelper 类中有两个选择性数据访问方法我想告诉您——UniqueResult<T> 方法和 FetchProjection<T> 方法,它们在系统中被频繁使用。这些方法用于为我们创建一个自定义的 ICriteria 查询。
model.myInfo
=
Parichay.Data.Helper.NHibernateHelper.FetchProjection<MemberDetails>(new
string[] { "Id", "Nicknm", "Surnm", "Givennm", "TitleC", "GenderC",
"Institute", "CtryC", "ShowPrvInfo" }, "Id", uId, 0, 1, false, null,
false)[0];
以下是 FetchProjection<T> 方法的签名
public
static IList<T> FetchProjection<T>(string[] fieldNames,
string[] propertyNames, object[] propertyValues, int? pageIndex, int?
pageSize, bool isSoftSearch, string sortField, bool isAscSort)
{
…
//If parameters are provided, add the parameters with values
if ((null != propertyValues) && (null != propertyNames))
{......}
…..
//If Projection is sought, apply filter for projection
…{ProjectionList proj = Projections.ProjectionList();
for (int i = 0; i < fieldNames.Length; i++)
{
proj.Add(Projections.Property(fieldNames[i]), fieldNames[i]);
}
criteria = criteria.SetProjection(proj);
}
…
if (null != sortField)
if (isAscSort)
criteria.AddOrder(Order.Asc(sortField));
else
criteria.AddOrder(Order.Desc(sortField));
…
//Set Paging Option
if(pageIndex.HasValue&&pageSize.HasValue)
criteria = criteria.SetFirstResult(pageSize.Value * pageIndex.Value).SetMaxResults(pageSize.Value);
…...
}
如上所示,FetchProjection 方法根据我们的自定义 ICriteria 查询设置了 Projection 的选项 => 投影的字段名、要比较的属性及其值、排序、分页选项。要关闭任何功能,我们只需为不需要的参数提供 null 即可。
第二个方法 UniqueResult 为我们创建一个基于 ICriterea 的查询,以获取唯一的 Result 对象,或者如果我们只想获取某个特定字段,我们可以这样使用它:
//for fetching the entire Object of class MemberInvitations
Data.Helper.NHibernateHelper.UniqueResult<MemberInvitations>(null, "Guid", id);
//To fetch only one column Id
Data.Helper.NHibernateHelper.UniqueResult<MemberInvitations>(“Id”, "Guid", id);
以下是该方法的签名:
public static T UniqueResult<T>(string fieldName, string[] propertyNames, object[] propertyValues)
{
if ((null != propertyValues) && (null != propertyNames))
{
for (int i = 0; i < propertyValues.Length; i++)
{
criteria = criteria.Add(Expression.Eq(propertyNames[i], propertyValues[i]));
}}.....
......
if (!string.IsNullOrEmpty(fieldName))
{
ProjectionList proj = Projections.ProjectionList();
proj.Add(Projections.Property(fieldName), fieldName);
criteria = criteria.SetProjection(proj);
object rtrn = criteria.UniqueResult();
if (rtrn != null)
{
object obj = (T)System.Activator.CreateInstance(typeof(T));
Helper.PropertyInspector.SetPropertyValue(ref obj, fieldName, rtrn);
return ((T)obj);//PropertyInspector class is mentioned below
}
else
{ return default(T); }
}
else
{
return (T)criteria.UniqueResult();
}
}
4.2) 反射的使用(来自 PropertyInspector 类)
此类使用反射来设置选择性属性,以防我们的查询仅获取对象的某些部分数据。例如,上面提到的 UniqueResult 方法返回 T 类型的对象,仅设置了通过“fieldName”引用的一个属性。
更多信息,感谢Philippe Leybaert 在此。
4.3) Email 类
这是用于创建电子邮件的有趣类之一。设置 to 属性会将地址添加到电子邮件中。Send 方法执行发送操作。
更多信息,感谢Jake Howlette 在此。
4.4) ImageResult 和 JsonResponse 类。
存储在 member_upload 类中的图像以 ImageResult 的形式返回,以便它们可以与 <Img src=”...” /> 属性一起使用。
更多信息,感谢Sean 在此。
4.5) Asp.Net MVC3 和 CheckBoxFor 布尔属性。
我们试图将标志保留为 Int 值。期望以后我们可能会将其绑定到外键或可选值。但是为了将其保留为布尔值(以便与复选框绑定),我们向 NHibernate 对象的 Int32 属性添加了布尔属性,如下所示:
private Int32 _ShowPrvInfo;
public virtual Int32 ShowPrvInfo
{
get { return _ShowPrvInfo; }
set { _ShowPrvInfo = value; }
}
//The corresponding boolean property for above Int32
public bool bShowPrvInfo { get { return (_ShowPrvInfo != 0); } set { _ShowPrvInfo = (value) ? 1 : 0; } }
4.6) 枚举作为查找表的替代
为了减少对查找表的需求,我们在
类中使用 AppConstrants
和 Dictionary<int,string>
作为查找。这是一个存储用于重定向的 Controller 名称的枚举示例。enums
这是我们如何从枚举中解析 int(用作主键)和 string(以检索值)。
//Parsing INT to ENUM
int rUrl = model.rUrl;
(Parichay.AppConstants.ReturnContollerHomes)rUrl;
//Back from ENUM to INT
int requestType = (int)Parichay.AppConstants.RequestTypes.Friend
//Safe Parsing STRING to ENUM
Parichay.AppConstants.ReturnContollerHomes rUrl = Parichay.AppConstants.ReturnContollerHomes.Message;
bool flat = Enum.TryParse<Parichay.AppConstants.ReturnContollerHomes>(Url.RequestContext.RouteData.Values["controller"].ToString(),out rUrl);
4.7) 无限记录计数分页
为了执行无限分页,而无需触发计数查询,我们使用基本的无限分页,根据页面计数启用/禁用“上一页”/“下一页”按钮。cshtml 文件位于 Shared/pg.cshtml
@model System.Int32
@{
int pg = 0;
bool nxtFlag= Int32.TryParse(Request.QueryString["p"],out pg);
}
<b>Page:</b> @if(pg>0){@Html.ActionLink("<<Prev", ViewContext.Controller.ValueProvider.GetValue("action").RawValue.ToString(), new { @p = pg -1 })}else{<span><b><<Prev</b></span>} | @Html.ActionLink("First",ViewContext.Controller.ValueProvider.GetValue("action").RawValue.ToString()) | @if ((pg >= 0) && (Model != 0))
4.8) 系统日志记录器
我们系统的 Logger 使用与 Log4Net 类似的表(system_log),但它使用 NHibernateHelper 类记录系统异常。相同的表可用于合并 log4net 和内部系统 Logger 的日志记录。
4.9) Message、Friends、Groups 表
当然,非常感谢 Emad Ibrahim,member_messages、member_freinds 和 groups 的表结构灵感来自于他用 Asp.Net MVC2、Linq to SQL 和 Ajax 编写的开源微型博客项目 Yonkly。
4.10) Utilities.CreateAvatar 方法
一个有趣的工具方法 Utilities.CreateAvatar 取自 Gunnar Peipman 的博客在此。它用于将上传的图像调整为方形缩略图。
5. 代码使用
当前应用程序已在 MySql 数据库上进行了测试。您要做的第一件事是创建数据库并运行表脚本。MySql 数据库脚本已随解决方案一起提供。
对于 MySql 以外的数据库,您可能更倾向于启用 NHibernate 助手类中的 createschema 功能,它应该可以从映射文件中创建数据库架构。或者,您可以自己创建表脚本,并更改应用程序 web.config 文件中的 Nhibernate 参数。
为了初始填充数据库,您可以运行初始填充脚本,该脚本将创建名为 sysadmin 的用户,默认密码为“password”,然后将此用户添加为网站的管理员。
*目前,您只需使用用户名“sysadmin”注册即可,应用程序会将您添加到“Admin”角色。以下循环已添加到“Account>>Register”控制器以确保这一点。
6. 安装:-
- 请注意,此应用程序未创建自动安装程序。请先创建 MySql 数据库。例如,命名为“parichaytest”并安装提供的两个 SQL 脚本。
- 1ParichaySecurity.sql 包含安全模式。
- 2ParichayMessaging.sql 包含消息表模式。
- 更改应用程序 web.config 文件中 NHibernate 的“connection string”名称,以指向您的数据库。
- 例如,在 Web.Config 文件中为 MySql 数据库启用并更新以下设置 。 (更新 DB 名称、用户名、密码等):-
<property name="show_sql">true</property>
<property name="use_outer_join">true</property>
<property name="use_proxy_validator">false</property>
<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
<property name="connection.isolation">ReadCommitted</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MySQLDialect</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="connection.connection_string">Server=localhost;Database=parichaytest;User ID=**Your DB UserId**;Password=**yourdbpasswrd**;Convert Zero Datetime=true</property>
- 在 Web.Config 文件中为 SQL Server 启用并更新以下设置:-
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Data Source=.\sqlexpress;Initial Catalog=parichaytest;User ID=**Your DB UserId**;Password=**yourdbpasswrd**</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="show_sql">true</property>
- 构建并运行应用程序。
- 请记住使用“sysadmin”用户名注册。系统将为您分配“Admin”角色。在为您创建 sysadmin 用户后。您可以注释掉“Account>>Register”控制器操作中的以下代码块:
if
((string.Equals(model.UserName,"sysadmin",StringComparison.InvariantCultureIgnoreCase))&&(Roles.GetAllRoles().Length
== 0))
{
Roles.CreateRole("Admin");
Roles.AddUserToRole(model.UserName, "Admin");
}
当前系统已在 MySql 数据库上进行了测试。对于其他数据库,您可以从映射文件中生成架构。要做到这一点,您需要在“Parichay.Security”和“Parichay.Data”项目的 NHibernateHelper 类中包含以下代码:
cfg.Configure();
cfg.AddAssembly(typeof (aClassFromYourProject).Assembly);
new SchemaExport(cfg).Execute(false, true, false, false);
- 设置好数据库后,请记住在应用程序的 Web.Config 文件中更新 NHibernate 连接字符串。
一旦我能够用不同的数据库测试该应用程序,我将更新 SqlScripts。