Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Parichay (A Simple & Small Asp.Net MVC Social Network Starter)

0.00/5 (No votes)
23 Feb 2012 14  
Parichay (A Simple & Small Asp.Net MVC Social Network Starter)

Downloads:

1. Introduction and Background

Do you want to add the simple messaging, friends and group interaction to your website, but don't want to go for a complete CMS based solution? Are you using microsoft’s Asp.Net role based security and want to provide interaction between your website users? Or do you want to know the basics of social networking so that you can easily add them to your website if needed? Parichay is an attempt to help you address your intention.

Although Asp.Net Social Networking book is an amazing read, but still it carries a lot of information and the code contains quite a big (and of course efficient) framework for social network. In Parichay, we would try to Keep It Short and Simple(KISS)

Therefore, Parichay is a Short and Simple Asp.Net Social Networking and messaging website starter app. The application has been designed with KISS(Keep it Short and Simple) principle in mind. We will use Asp.Net MVC 3 (with razor) as web application framework. NHibernate will be used for data persistence framework - which will give our project the portability to several databases.

Of course, if you are looking for a well attested, well beaten track, you would prefer to go with a complete cms based solution, and add social networking to it as a plugin module. Still, this article will give you an Idea about what you mayconsider if you are develping your own social networking module. Further, this app can easily be converted into a CMS module as it has been designed as an independent module with least of dependencies to the security layer.

**(Please Note:- This application doesn’t have one-click installer. Remember to go to the INSTALLATION section for information about installation. Basically - you'll need to:-

  • create a database(e.g. create database parichaytest)
  • create tables using SQLs provided (MySql => 1_ParichaySecurity.sql & 2_ParichayData.sql, SQLServer=> SQLServer_DbScript.sql)
  • Update the NHibernate connection string in Web.Config )**


2. Overview

As mentioned earlier, we will be using the default Asp.Net role based security. We will be adding a simple layer of permission based security to it as an httpmodule. (more info. about this module can be found in this article.

Here is the basic overview of this article and components we'll be talking about:-

  1. Introduction and Background (Discussed Above)

  2. Overview
  3. Modules (Tables=>Db Objects and their respective Controller-Actions)
    1. Member Details: (User Details, Password & Password Q&A mgmt., Recovery etc.)
    2. Member Alerts: (Simple track of your website activity and responses.)
    3. Member Messages: (Status Updates sharing and receiving replies/responses to your thoughts.)
    4. Member Friends: (Connecting with other Existing/External users to the system)
    5. Member Requests: (finding and sending Friend/Group/Other request to existing users.)
    6. Member Invitations: (Inviting friends from outside)
    7. Member Groups: (Discussion groups, Making publicly visible/private groups, Managing groups{currently all groups are public only managed by admin})
    8. Member Uploads: (Uploading images to the site)
    9. System Administration: (Blocking/Unblocking/Managing/Deleting users, Roles and Permissions)
    10. System Logs: (The track of website hiccups)
  4. Points of Interest (Some Interesting and re-usable pieces of code for developers)
  5. Using the Code
  6. Installation
  7. History

For a quick overview of the table structure and controllers, here is the link to :-

  1. Parichay.Data Table Diagram (for messaging system only)
  2. Parchay.MVC Controllers Class Diagram
  3. This is how our Home Screen will look like.
    1. User Edit Info
    2. An Alert for Activity (Sender's View), (Recipient's View)
    3. My Messages
    4. Find A Friend, Send a Friend Request
    5. Acknowledge Request (Sender view), (Recipient View)
    6. Group Home, Create A Group
Lets proceed now to discuss these modules one by one.

3. Modules

3.1) Member Details (AccountController):

(User Details, Password & Password Q&A Management, Recovery etc.)

The first thing which is done as a user signs up to our website is - user’s profile is created into the **member_details** table (PS the code below). Our entire messaging system has dependencies tied to this table only. Security module can continue to work independently.

member_details.png account_controller.png

Within our Parichay.Data module, only member_details table has the dependency over aspnet_membershipuser table’s primary key. This is to ensure that Security module can continue to work independently as Data Module grows. The entire Parichay.Data module can be ported and wrapped around to some other security module also. (e.g. - If you might want to port it to a CMS plugin.)

As soon as the user signs up, an empty member_details entry is created for the user. The primary key for member_details comes from the aspnet_membershipusers's primary key (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) Member Alerts (AlertController)

(Simple track of your website activity and responses.)

The first experience of interaction you want to provide an incoming user are the updates and responses about his activity in the system. This keeps the user updated about what’s going on in here. The table which stores this information is member_alert . So from now on, any activity/response to user can be stored to this table and it should display in his Alerts. (On the home page and in the NOTICES section. Here is the table:-

member_alert.png alert_controller.png

The P_UserId is a foreign key, pointing to member_details table. So once you have a profile, you are eligible for alerts!!! Message field stores the message, and Version is timestamp of the alert. (There is Alert_Type_Id fields which is not currently tied to any lookup table yet, as we wanted to keep our system light. This field can be used for templating the alerts for specific type of activity. So that we can Just pass in the parameters and activity code, the template should generate the necessary message ).

Here is the method Alert which is used to send alerts to a user (its located in the BaseController class):-

//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;

            }
        } 

I think the code is quite self explainatory. We tie an alert to the primary key (from the member_details table. The message is whatever message we want to sent. By default it is not hidden (obj.Ishidden=0). And then we call our pretty NHibernateHelper class to Save the object.

3.3) Member Messages (MessageController)

(Status Updates - sharing and receiving replies/responses.)

member_messages.png message_controller.png

Now, as a user is already signed-up to our website and he'll be alerted of his activities here - the first activity we’re gonna give to him is Messaging and replies. Obviously, if the sender is himself the recipient of a message - that becomes a status update. A user can reply to his status as well - in that case the second message will have a ParentId pointing to first message. Therefore, member_messages table is tied to the member_details table through two keys - the SenderId, and the RecipientId.

And there is another nullable key here “ParentId” which points back to the member_messages table's primary key. Which means in addition to simple status updates (where sender=recipient and parentId =null) there is going to be replies. The replies will have the Primary Key of the orginal message as their parentId. We can grow this tree to many levels, but right now we are only going to give a two level tree. All the subsequent replies to a status update (or parent message) will be ordered by the date. So the latest reply comes to bottom.

Below is the code for adding a message. As obvious, Replying a message is also simply adding a message. But this time = the Parent Id points to another message, and the recipient Id points to the SenderId of the parent message.

//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) Member Friends (FriendController)

(Inviting a friend to the system, Sending a friend Request to existing user in the system)

member_friends.png friend_controller.png

Let’ say our signed-up user wants to connect to more users - there are two scenarios- 1. Either the user has already joined the system - In this case you can send him a “Request”. 2. The user is not yet joined - In this case you send him an “Invitation”.

Hense, an invitation will complete in two steps - firstly and invitation will be sent to the external user to “Join” the system .And once he joins(or signs-up) and acknowledges your Join "Invitation"- a “Request” is automatically created for him to become your friend (or join your group -depending upon where your invitation originated in the first place). The new user can still accept/decline your Friend "Request" after signing-up. “Requests” and “Invitations” are discussed in next section. Lets take a look at the member_friends table.

The member_friends table defines a relationship between two users. member_friends table points to member_details table through two keys UserId and FriendId. ( It doesn’t matter which side you are, once your ID’s are here, you are friends.)

And ID’s of two users in member_details table are added here only if the recipient user “Confirms” the freind request of requesting user. That happens when the recipient acknowledges the request. In the next section (Member Requests) you'll find what happens when the recipient presses “Confirm” button on “AckRequest” page of the Connect Controller.

In the FriendController, one of the interesting section is the find user page, which actually gives you the link to send a friend request to a found user. Here is the code for the Find action and the binding as used in UsrSrch.cshtm view page:-

//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) Member Requests (ConnectController)

(finding and sending Friend/Group/Other request to existing users.)

As shown below, the table member_requests has two foreign keys - the senderId and the RecipientId mapped to member_details table. This means that this interaction is between an “existing” sender and an “existing” recipient in our website. Once a sender initiates a "Request", the sender and recipient can see the Request in Invitations & Requests panel. The recipient can accept/decline the request. The respective alerts are shown.

member_requests.png connect_controller.png

Now in the same controller, we have AckRequest Action. This Action performs the actual task of connecting people :) Once the recipient presses confirm - the type of request is Identified(whether it is a friend or a group request) and a respective relationship is created accordingly as shown below:

//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) Member Invitations (ConnectController)

(Inviting friends from outside)

member_invitations.png

For a friend, who has not yet joined our website, an existing user in the system can place an invitation. The target e-mail ID is recorded into the member_invitations table. Next time the recipient of the Invitation joins in (or signs up) - he can acknowledge the sender's invitation and the sender gets notified.(The "Type" field of member_invitation is set up depending on where you started your request - wheather you invited him as a friend or to join your group. The TargetPageId contains the ID of the user or the Id of the target group) . Below is the code of AckInvite method which creates the appropriate request on the basis of "Type" of the original invitation :-

//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) Member Groups (GroupController)

(Discussion groups, Making publicly visible/private groups, Managing groups{by admin})

As we see in the image below - each member_groups has member_groupmembers and member_groupmessages . The member_groupmessages are related to member_details table through two foreign keys - SenderId, RecipientId. And it is related to member_groups table through the third foreign key GroupId. In the same way, we have member_groupmembers table which is the many to many mapper table between member_groups and member_details .Also , there is another foreign key- OwnerId which relates member_groups directly to the member_details.

(I’m sorry, Currently, all the groups are public. The feature to join the group has not been enabled yet. And therefor the group invitations and requests. I will try to enable them ASAP. So, just as we invite or request a friend, we can invite/request our friends to our groups.)

member_groups_messages.png

member_groupmembers.png group_controller.png

The PostMessage method of GroupController is pretty much like that of the MessageController above, as obviously the function is same. However, upon accepting the group request (in ConnectController>>AckRequest), here is how the group member is created:-

//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) Member Uploads (MediaController)

(Your uploads to the site)

This table requires quite a little introduction. All our file uploads are placed here. Every upload is tied to member_details table through P_UserId key. What might be interesting to read is the resizing of image to thumbnail. Below is the code used for it.

member_uploads.png media_controller.png

One of very interesting methods in MediaController is SaveAvImage which is used to upload an image to the member_uploads database. Here is the code:-

//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);
..... 

}}}}  

The Utilities>>CreateAvatar method is taken from Gunnard Peipman's blog here.

3.9) System Administration (AdminController)

(Blocking/Unlocking/Managing/Deleting users, Roles and Permissions)

As mentioned, we are using the Default Asp.Net role based security. Here we are using custom asp.net membership providers and custom role providers. The providers have been taken from this tutorial and customized. And Additional layer of security has been added as an HttpModule as described in this project. The UI screens for the User Management, Roles, Permissions can be understood in the aadhaar tutorial. Here we just reproduce the screens.

3.10) System Logs (The track of your application’s hiccups)

The Logger for our system uses the similar table (system_log) as Log4Net, however it records the system exceptions using NHibernateHelper class. The same table can be used to combine the logging for both log4net as well as inner System Logger

4. Points of Interest (Some Interesting pieces of code)

Here are some of interesting tricks you will notice in the code(and perhaps would like to re-use). The inspiration has come from various sources and from my intuitions. The credit goes to the amazing helpful Asp.Net community and independent bloggers for sharing all the valuable information.

4.1) The selective Data Access (from NHibernateHelper class)

There are two of the selective Data Access methods from NhibernateHelper class which I would like to tell you about - the UniqueResult<T> method, and the FetchProjection<T> method which are used quite often in the system. These methods are used to create a custom ICriteria based query for us.

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]; 

Here is the signature for FetchProjection<T> method:

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);
…...
}

As we see above, FetchProjection methods sets up options for Projection => the projected fieldNames, properties to Compare and their values, Sorting, Paging options for our custom ICriteria query accordingly. All we need to do in order to turn off any feature is provide a null for not needed parameter.

The second method UniqueResult creates a ICriterea based query for us to fetch either a UniqueResult object or if we wish to fetch out only a particular field, we can use it like this:-

//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);

Here is the signature of the method:-

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) Use of Reflections (from PropertyInspector class)

This class uses Reflections to set the selective properties in case of our query fetches partial data for the object. For example the UniqueResult method above returns the object of type T, setted with only one property which is referred through “fieldName”.

More info. with thanks to Philippe Leybaert here.

4.3) The Email class

This is one of the interesting class which is used to create E-mails. Setting the to property adds to addresses to the e-mail. The Send method performs the send.

More info. with thanks to Jake Howlette here.

4.4) The ImageResult and JsonResponse classes.

The Images stored in member_upload class are returned as ImageResult so that they can be used with <Img src=”...” /> attribute.

More info. with thanks to Sean here.

4.5) Asp.Net MVC3 and CheckBoxFor boolean properties.

We tried to keep the flags as Int values here. In expectation that later point of time we might tie them to foreign key or may be selectable values. But to keep them as boolean (to bind them with checkboxes), we add boolean properties to the Int32 properties of our NHibernate Objects like this:-

 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) Enum as replacement for Lookup Tables

In order to cut down the on Lookup Tables, we are using Dictionary<int,string> and enums int the AppConstrants class for lookup. Here is an example of an enum which stores the Controller names for redirection.

Here is how, we parse int (to replace as primary key), and string(to retrieve the value) from enum.

//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) Indefinite Record count Paging

For performing indefinite paging, without the need to fire the count query, we use the basic indefinite pages enabling/disabling the Previous/Next buttons on the basis of page count. The cshtml is located in Shared/pg.cshtml

@model System.Int32

@{
    int pg = 0;
    bool nxtFlag= Int32.TryParse(Request.QueryString["p"],out pg);
}
<b>Page:</b> @if(pg>

4.8) System Logger

The Logger for our system uses the similar table (system_log) as Log4Net, however it records the system exceptions using NHibernateHelper class. The same table can be used to combine the logging for both log4net as well as inner System Logger.

4.9) The Message,Friends,Groups tables

And of-course many thanks to Emad Ibrahim, the table structure for member_messages, member_freinds and groups are inspired from his open source microblog project Yonkly written in Asp.Net MVC2, Linq to SQL and is Ajax capable.

4.10) The Utilities.CreateAvatar method

One of the interesting utility method Utilities.CreateAvatar has been taken from Gunnard Peipman's blog here. It is used to resize the uploaded image to a square thumbnail.

5. Using the code

The current application has been tested over MySql database. The first thing you will do is to create the database and run the table scripts. The MySql database script has been provided with the solution.

For databases, other than MySql you might prefer to enable the createschema functionality to NHibernate helper class which should create the database schema from the Mapping Files. Or you can create the table scripts yourself and change the Nhibernate parameters in the web.config file of the application.

For initial seeding of the database, you can either run the initial seeding script which will create the user with name sysadmin and default password "password", and then add this user as Admin of the website.

*Currently you can simply sign-up with username "sysadmin" and the application will add you to the "Admin" role. The following loop has been added to the "Account>>Register" controller to ensure this:

6. Installation:-

  • Please note that auto installer has not been created for this app. Kindly create the MySql Database first. e.g.- with name "parichaytest" and install the two SQL scripts provided.
  • The 1ParichaySecurity.sql contains the schema for Security.
  • 2ParichayMessaging.sql contains the schema for Messaging tables.
  • Change the name of NHibernate "connection string" in the web.config of the application to point to your database
    • e.g., Enable & Update following settings for MySql Database in Web.Config file. (Update the DB name, Username, Password etc):-
       <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>
    • Enable & Update following settings for SQL Server in Web.Config file:-
<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>
  • Build and run the application.
  • Remember to sign up with "sysadmin" username. The system will assign you the "Admin" role. Once the sysadmin is already created for you. You can comment out the following code block in the "Account>>Register" Controller action:-
if 
((string.Equals(model.UserName,"sysadmin",StringComparison.InvariantCultureIgnoreCase))&&(Roles.GetAllRoles().Length
 == 0))
                    {
                        Roles.CreateRole("Admin");
                        Roles.AddUserToRole(model.UserName, "Admin");
                    }

The current system has been tested with MySql database. For other databases you can either generate the schema from mapping files. To do this, you will need following code in NHibernateHelper classes of "Parichay.Security" and "Parichay.Data" projects:-

cfg.Configure();
cfg.AddAssembly(typeof (aClassFromYourProject).Assembly);
new SchemaExport(cfg).Execute(false, true, false, false); 

Information can be found here and here

  • After setting up the database, remember to update the NHibernate connection string in Web.Config file of the application.

Once I am able to test the application with different database, I will be updating the SqlScripts.

7. History

  • 21-Feb-2011 For security, the basic Asp.Net role based security has been used along with additional layer of security as described in the Aadhaar project at codeproject.
  • 21-Feb-2011 Parichay added to codeplex. If you find it interesting to contribute, please contact.
  • 23-Feb-2011 All UInt values in NHibernate mapping replaced with Int32. The system is now tested with MS-SQLServer. The scripts will be provided soon as the table relationships are created.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here