Summary:
If you want common behavior, you need an interface. And from the first one is a small step to re-organizing the program.
Body:
When you make a component for other people, you must make the possibility for those to
1. customize your software with their software
2. provide a default implementation
For the messaging component, the first customization is to provide a way for the site using the messaging to use their users.
The first step is to create a custom project which can provide guidance to the users that want to implement. VS provides XML documentation –I have checked and put also “warnings as error”
Next , I have create the User interface:
/// <summary>
/// the user that can send messages
/// TODO version 2: make pagination
/// </summary>
/// <typeparam name="UserPrimaryKey">the type of primary key</typeparam>
public interface IUser<UserPrimaryKey>
{
/// <summary>
/// the user primary key
/// </summary>
UserPrimaryKey Key { get; set; }
/// <summary>
/// the user name to be displayed
/// </summary>
string UserNameToDisplay { get; set; }
/// <summary>
/// other info for the user
/// </summary>
string OtherInfo { get; set; }
/// <summary>
/// find friends
/// </summary>
/// <param name="search">find user by name</param>
/// <returns></returns>
IEnumerable<KVPNew<UserPrimaryKey, string>> FindFriends(string search);
/// <summary>
/// find friends online
/// </summary>
/// <param name="search"></param>
/// <returns></returns>
IEnumerable<KVPNew<UserPrimaryKey, string>> FindFriendsOnline(string search);
/// <summary>
/// sends a message
/// </summary>
/// <param name="message"></param>
void SendMessage(IUserMessage<IUser<UserPrimaryKey>, UserPrimaryKey> message);
}
Two methods are interesting : SendMessage and FindFriends
Let’s take first SendMessage. It was obviously clear that I need another interface – the message to be sent. So the next interface created:
/// <summary>
/// the message to be sent to the user
/// TODO version 2: allow messaging for more than 1 user
/// TODO version 2: message importance
/// </summary>
/// <typeparam name="TheUser">user interface</typeparam>
/// /// <typeparam name="UserPrimaryKey">user primary key</typeparam>
public interface IUserMessage<TheUser, UserPrimaryKey>
where TheUser : IUser<UserPrimaryKey>
{
/// <summary>
/// subject of the message
/// </summary>
string Subject { get; set; }
/// <summary>
/// body of the message
/// </summary>
string Body { get; set; }
/// <summary>
/// to the user
/// </summary>
TheUser To { get; set; }
/// <summary>
/// from the user
/// </summary>
TheUser From { get; set; }
/// <summary>
/// cc - as in email
/// </summary>
TheUser CC { get; set; }
/// <summary>
/// date of message
/// </summary>
DateTime DateInserted { get; set; }
/// <summary>
/// if recipient have read
/// </summary>
bool MessageRead { get; set; }
// <summary>
// bcc - as in email
// </summary>
//TheUser BCC { get; set; }
}
Let’s take the other : IEnumerable<KVPNew<UserPrimaryKey, string>> FindFriends(string search);
It is clear that a user can send emails to friends( maybe to anybody if we make an intranet site for an enterprise) and I must somehow have the names and id’s of the friends. I could use KeyValuePair – but it is a struct and can not be compared with null . More, usually I need more data to be transferred – so I have created long ago a KVPNew class:
/// <summary>
/// this is the correspondent class of KeyValuePair structure from .net
/// The features:
/// 1. it is a class- can be compared fastly with null
/// 2. can be used in a search and display <paramref name="AdditionalData">AdditionalData </paramref>
/// </summary>
/// <typeparam name="TKEY">Key - it is compared in equals and GetHashCode</typeparam>
/// <typeparam name="TValue">Value to be displayed</typeparam>
public class KVPNew<TKEY, TValue>
{
public KVPNew() { }
public KVPNew(TKEY key, TValue value):this()
{
this.Key = key;
this.Value = value;
}
public TKEY Key { get; set; }
public TValue Value { get; set; }
public string AdditionalData { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
var o = obj as KVPNew<TKEY, TValue>;
if (o == null)
return false;
return this.Key.Equals(o.Key);
}
public override int GetHashCode()
{
return this.Key.GetHashCode();
}
}
And, being to interfaces, I have created also an interface for admin people to find users
/// <summary>
/// used by application to load plugins to find users
/// because each application can have it’s own way to find users
/// <summary>
/// used by application to load plugins to find users
/// because each application can have it's own way to find users
/// used by admins to find users
/// Improvement version 2 : pagination
/// </summary>
/// <typeparam name="T">user </typeparam>
/// <typeparam name="UserKey">user key </typeparam>
public interface IUsersFind<T,UserKey>
where T:IUser<UserKey>
{
/// <summary>
/// used to find users online to send message
/// </summary>
/// <param name="UserThatMakesTheSearch"> the user that makes the search </param>
/// <param name="Search">search string - could be null</param>
/// <returns> a list of users</returns>
IEnumerable<KVPNew<T, string>> FindUsersOnline(T UserThatMakesTheSearch, string Search);
/// <summary>
/// used to find users (online or not )to send message
/// </summary>
/// <param name="UserThatMakesTheSearch"> the user that makes the search </param>
/// <param name="Search">search string - could be null</param>
/// <returns> a list of users</returns>
IEnumerable<KVPNew<T, string>> FindUsers(T UserThatMakesTheSearch, string Search);
/// <summary>
/// find a user by his key
/// </summary>
/// <param name="key">the user key</param>
/// <returns></returns>
IUser<UserKey> FindUser(UserKey key);
}
/// <summary>
/// operations with the database
/// </summary>
/// <typeparam name="User"></typeparam>
/// <typeparam name="UserPrimaryKey"></typeparam>
public interface IUserList<User, UserPrimaryKey>: IDisposable
where User : IUser<UserPrimaryKey>
{
/// <summary>
/// fast delete all from database - good for testing
/// </summary>
void FastDeleteAll();
/// <summary>
/// add to internal list
/// </summary>
/// <param name="u">the user to be added </param>
void Add(User u);
/// <summary>
/// save changes for new and old members
/// </summary>
void SaveNew();
/// <summary>
/// save changes for old members
/// </summary>
void SaveExisting();
/// <summary>
/// for retrieving
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
User Find(int i);
}
The final interfaces are in this picture:
Next time we will create the database EF4.1 files from DatabaseFirst and we will modify .tt templates to behave nicely with relationships.
Homework:
-
Add to IUserMessage a BCC field
-
What if To from IUserMessage will be directed to more than one person ?How do you implement it?
-
Do you think that is something missed from KVPNew class?( Hint == operator and GetHashCode)