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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | /// <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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /// <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 ; }
}
|
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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /// <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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /// <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)