We continue Part 1 with a discussion of our server-side design and implementation. Since we will be making RESTful calls from our Android mobile client, I prefer an MVC design and implementation, allowing light-weight, stateless calls, and returning Json serialized results. I choose ASP.NET MVC with REST for ASP.NET MVC for its support of my requirements, in C#, and automatic support of LINQ, Entity Framework, and ASP.NET Membership.
Let’s first discuss the services we require for our client to call.
RESTful Services
We will create RESTful services using ASP.NET MVC, exposing basic CRUD service calls that our client will use. Most of the services map directly to our entities, and are as follows:
- SocialUsers
- ReminderEvents
- SocialUserProfiles
- SocialUserMessages
Note that the acceptance of a join message results in the creation of a ConnectTo record establishing the actual connection between the users. A service exposing the ConnectedTo entity directly is, therefore, unnecessary.
Also, we will create a Search service that will allow flexible filtering and searching of Social Users via their relevant attributes.
Now we need to decide on our architecture. I believe a clean, layered architecture works well for our needs. I depict this using MVC as follows:
In implementing our service that supports the basic features of SocialUsers, let’s review our relevant use cases. We need to be able to
- list our connections and
- accept a join request.
In implementing our service that supports searching for SocialUsers, we create a simple service providing the ability to
- search for users given a variety of user attributes.
The other use cases are supported by the SocialUserMessages and other services.
Note, we are assuming our system already supports logon/logoff, registration, and the usual features common to software applications. I want to focus on the meat and potatoes of our project, not the salt and pepper!
The service layer of our MVC system will expose services via controllers. These controllers are very smart because they can distinguish and respond to different request formats. Of particular importance to us, is the ability of the services to return serialized responses in the form of JSON and XML. Upon making a service request, our mobile client will deserialize these responses into the actual data. For more information on deserialization, see my post on Android: How to Deserialize both XML and JSON.
REST for ASP.NET MVC provides a nice little jewel called the WebApiEnabled attribute that provides this serialization functionality for us through the magic of declarative programming. An example of this is shown in the SocialUsersController coming up in the next section (first line).
Implementing the SocialUsers Service
All services will return serialized DTO(s) as their responses. For example, the SocialUser and SocialUserProfile DTOs are as follows:
[Serializable]
[DataContract]
public class SocialUser
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Username { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public SocialUserProfile Profile { get; set; }
}
[Serializable]
[DataContract]
public class SocialUserProfile
{
[DataMember]
public int Id { get; set; }
[DataMember]
public int SocialUserId { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
public string Phone { get; set; }
[DataMember]
public string Zipcode { get; set; }
[DataMember]
public DateTime Birthdate { get; set; }
[DataMember]
public string FavColor { get; set; }
[DataMember]
public string FavBook { get; set; }
[DataMember]
public string FavMusic { get; set; }
}
That should be enough set up information. Let’s get down to business!
In order to list our connections (lines 6 – 11 below) and accept a join request (lines 15 – 30 below), our SocialUsers service will provide the following:
[WebApiEnabled]
public class SocialUsersController : Controller
{
//
// GET: /Index/
public ActionResult Index(int id, int? pageNum, int? pageSize)
{
ISocialUserRepository repository = new SocialUserRepository();
return View(repository.Retrieve(id, pageNum, pageSize).AsSerializable<Entities.DTOs.SocialUser>());
}
//
// GET: /ConnectionsIndex/
public ActionResult ConnectionsIndex(int id, int? pageNum, int? pageSize)
{
ISocialUserRepository repository = new SocialUserRepository();
return View(repository.RetrieveConnections(id, pageNum, pageSize).AsSerializable<Entities.DTOs.SocialUser>());
}
//
// POST: /SocialUsers/CreateConnection
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateConnection(int socialUserId, int connectingToSocialUserId)
{
try
{
ISocialUserRepository repository = new SocialUserRepository();
repository.CreateConnection(socialUserId, connectingToSocialUserId);
return RedirectToAction("ConnectionsIndex");
}
catch
{
return View("ConnectionsIndex");
}
}
}
SocialUsers Service – Quick Results Check
Now let’s see some results. The simple Index(…) method above returns this XML text, and this JSON text. Our mobile client can easily work with either of these!
The simple ConnectionsIndex(…) method above returns this XML text, and this JSON text. Looking good so far!
Implementing the Search Service
Next, we need to be able to search for users. We accomplish this as follows:
[WebApiEnabled]
public class SearchController : Controller
{
//
// GET: /Search/
public ActionResult Index(int? pageNum, int? pageSize, string username, string firstName, string lastName, string email)
{
ISearchRepository repository = new SearchRepository();
return View(repository.Retrieve(pageNum, pageSize, username, firstName, lastName, email).AsSerializable<Entities.DTOs.SocialUser>());
}
}
Search Service – Quick Results Check
Now let’s see some results. We will search for user’s whose first name begin with “M”. The simple Index(…) method above returns this XML text, and this JSON text.
Cool. Now we’re rolling.
Implementing the SocialUserMessages Service
In order to build a proper social network, not only is it necessary to locate folks to join, but one needs the ability to ask folks if they would like to join. Simple accept and reject responses are all that is necessary.
Our message service will provide several methods as follows:
- Index(int id) // retrieve messages
- Create(int senderUserId, int recipientUserId)
- Details(int id) // retrieve details of single message
- Accept(int id, bool acceptRequest) // true accepts request, false rejects it
We will default the join message subject to “You have been sent a Join request!”, and the actual message to “<Your name here> would like to join your social network. Do you accept?”
First, let’s define a SocialUserMessage DTO as follows:
[DataContract]
public class SocialUserMessage
{
[DataMember]
public int Id { get; set; }
[DataMember]
public int MessageTypeId { get; set; }
[DataMember]
public string Subject { get; set; }
[DataMember]
public string Message { get; set; }
[DataMember]
public int SocialUserSenderId { get; set; }
[DataMember]
public int SocialUserRecipientId { get; set; }
[DataMember]
public bool Accepted { get; set; }
}
We’ll now take a look at retrieving messages. Check out the following calls to the Index(…) and Details(…) methods within the SocialUserMessages Service:
[WebApiEnabled]
public class MessagesController : Controller
{
//
// GET: /Messages/
public ActionResult Index(int id)
{
ISocialUserMessageRepository repository = new SocialUserMessageRepository(HttpContext.Request.PhysicalApplicationPath);
return View(repository.Retrieve(id).AsSerializable<Entities.DTOs.SocialUserMessage>());
}
//
// GET: /Messages/Details/5
public ActionResult Details(int id)
{
ISocialUserMessageRepository repository = new SocialUserMessageRepository(HttpContext.Request.PhysicalApplicationPath);
return View(repository.Details(id).AsSerializable<Entities.DTOs.SocialUserMessage>().FirstOrDefault());
}
}
In order for a social user to join another, a message must be sent from the inquiring user to the target user. The following service methods supports this:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Create(int socialUserId, int messageTypeId)
{
IList<Entities.DTOs.SocialUserMessage> msg = new List<Entities.DTOs.SocialUserMessage>(1);
msg.add(new SocialUserMessageRepository().Create(socialUserId, messageTypeId));
return return View(msg.AsSerializable<Entities.DTOs.SocialUserMessage>().FirstOrDefault());
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(SocialUserMessage msg)
{
if (ModelState.IsValid)
{
return new View(new SocialUserMessage().Save(msg).AsSerializable<Entities.DTOs.SocialUserMessage>().FirstOrDefault());
}
return View(NotifyInvalidModelState());
}
Question: How is our Create(…) method different from the strategy implied by using a WebService? Think RESTful vs. WSDL!
And lastly, to satisfy an actual join between two users, the target user must accept the inquiring user’s message. This is done as follows:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Accept(int id, bool acceptRequest)
{
if (ModelState.IsValid)
{
return new View(new SocialUserMessage().Accept(id, acceptRequest).AsSerializable<Entities.DTOs.SocialUserMessage>().FirstOrDefault());
}
return View(NotifyInvalidModelState());
}
SocialUserMessages Service – Quick Results Check
Now let’s see some more results. We will simply display the details of one message. The simple Details(…) method above returns this XML text, and this JSON text.
Now see Part 3 for the client-side design and implementation details on the Android platform!







