I would like to elaborate on Stephen Cleary's answer. He is correct in using the partial class/methods to handle business rules in EF. However, he did not go into much detail about what to do within that partial class/method. I created a URL shortening service on my blog to use as an example for this. My ShortURL entity has only two columns/properties. Url and ID.
I wanted to validate that the URL being shortened is a valid URL before it actually stores it in the database through EF. So I created a partial class and method like so:
public partial class ShortURL
{
partial void OnUrlChanging(string url)
{
if (!Regex.IsMatch(url, @"(^((http|ftp|https):\/\/|www\.)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)"))
throw new Exception("Not a valid URL.");
}
}
This stopped EF from changing the property, leaving it NULL. But that's all it did. It didn't give me an easy way to get at the error message and display it to the user (that I am aware of EDIT: According to http://www.sellsbrothers.com/posts/Details/12700 IDataErrorInfo is the only way to get the error message to display properly in ASP.NET MVC) so I followed another example I found in the dark recesses of the web somewhere and I made my partial class inherit from IDataErrorInfo. I then implemented the interface and included a private dictionary object to store error messages in.
public partial class ShortURL : IDataErrorInfo
{
private Dictionary<string, string> errors = new Dictionary<string, string>();
partial void OnUrlChanging(string url)
{
if (!Regex.IsMatch(url, @"(^((http|ftp|https):\/\/|www\.)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)"))
errors.Add("Url", "Not a valid URL.");
}
public string Error
{
get { return string.Empty; } //I never use this so I just return empty.
}
public string this[string columnName]
{
get
{
if (errors.ContainsKey(columnName))
return errors[columnName];
return string.Empty; //Return empty if no error in dictionary.
}
}
}
Now, I have a fully-functioning way to store, retrieve, and display error messages. Now back in my controller (in MVC) I am able to do if (!ModelState.IsValid)
[HttpPost]
public ViewResult URLShortener(ShortURL shortURL)
{
if (!ModelState.IsValid)
return View();
shortURL.Url = shortURL.Url.ToLower().StartsWith("www.") ? "http://" + shortURL.Url : shortURL.Url;
shortURLRepository.AddShortURL(shortURL);
object model = "http://www.u413.com/" + ShortCodes.LongToShortCode(shortURL.UrlID);
//Not related to this answer but I had to cast my string as a generic object because the View() method has a (string, string) constructor that does something totally different. My view actually uses string as the model. I know I know, I could have just used ViewBag.
return View("ShowUrl", model);
}
There ya go. A working example of how to not only extend EF's partial methods, but also how to propagate the validation back to the UI. Let me know if anything needs improving or if there was something I missed.
EFfrom Visual Studio 2010