Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

Wednesday, May 09, 2012

Testing for StructureMap Default Instances

Have you ever received the good ole “No Default Instance defined for PluginFamily”? error while using StructureMap? I thought I’d share a quick test you can write to catch this error before you deploy the bug. This test is particularly useful when you mostly depend on the default conventions.

In the example provided, I’ll be verifying that I can get a default instance of all my controllers. So I’ve setup 5 controllers and 2 have constructors with some type of dependency.

Something like this:

So now I can setup my test, which looks like this:


If you’re not familiar with TestCaseSource, I blogged about it a few weeks ago on the Headspring blog, so you can read about it there. The test queries the assembly containing HomeController for all types that are a subclass of Controller and are not abstract. After it gets the type, it tries to get an instance of it from the object factory. If it fails, the test fails with what specifically it couldn’t return and which controller couldn’t be instantiated.

Here’s what the test looks like when they all pass:

CropperCapture[7]

Here’s what the tests look like when the two with dependencies fails:

CropperCapture[8]

Here’s a detailed view of one of the error messages:

CropperCapture[9]

Notice it says IAuthenticationService and it failed on the AccountController, so we know exactly where to start looking. The only other thing I probably need to mention is the TestBase, which contains the StructureMap initialization. You could simply register your StructureMap registry file there. Here’s what the example looks like:

If anyone has any suggestions on how to improve the test or if StructureMap already has a test built-in that I’m not familiar with, please share.

kick it on DotNetKicks.com

Monday, May 09, 2011

Seriously WebForms like ASP.NET MVC




I used my little WebFormContrib library again today. Some days I love revisiting old code because you realize how ignorant of some practices you were in the past. Hopefully none of you download it and say…geez this guy is Mr. Ignoramus. If you do, keep it to yourself. Kidding, please comment and inform the ignorant (me).

Anyhow, it’s been about 6 months since I’ve had to use WebForms, but today I had to and it wasn’t bad. I was able to tie my view into my pages and controls and used AutoMapper to map back to the domain from it. To me, WebFormContrib makes WebForms kinda fun again…cause it makes it seem new. I really do think it’s a great stepping stone to using MVC just because you kinda get used to the syntax. I also think it’s a decent library because I didn’t have to go relearn how it worked, I just referenced the library and then started working on my little WebForm app. I set it up just like I do my MVC apps. I also had to add a couple things and it was easily extendable, which I’m sure you all know is a good thing.

Anyhow, if you have no idea what I’m talking about, please read my previous posts on WebFormContrib.

Also, how could I post without a code sample? In “The Original” post, I mentioned the first thing I wanted to refactor was the validation section. Well, I did. Here’s the new and improved ModelIsValid() method:

        internal bool ModelIsValid(TModel view)
{
ErrorMessages =
new List<string>();
foreach (var property in typeof(TModel).GetProperties())
{
var value = property.GetValue(view, null);

var attributes = property.GetCustomAttributes(typeof(IValidationAttribute), false);
foreach (IValidationAttribute valatt in attributes)
if (!valatt.IsValid(value))
ErrorMessages.Add(valatt.Message);
}

return ErrorMessages.Count == 0;
}

Previously, it looked like this:

        internal bool ModelIsValid(TModel view)
{
ErrorMessages =
new List<string>();
foreach (var property in typeof(TModel).GetProperties())
{
var attributes = property.GetCustomAttributes(typeof(RequiredAttribute), false);
if (attributes.Length > 0)
{
var value = property.GetValue(view, null);
if (value.ToSafeString() == string.Empty)
ErrorMessages.Add(((
RequiredAttribute)attributes[0]).Message);
}
}

if (ErrorMessages.Count == 0)
return true;

return false;
}

Obviously…I was a moron. I still don’t think it’s perfect, but it is a serious improvement. It’s funny how you don’t see how to refactor something until you need to extend it. As soon as I created the length validator and added the code here I thought…wow this is dumb and then OH! do it this way. Anyhow, I thought I’d share the slight improvement.

You can download the source and samples here…or just download the DLL here.

Thanks for reading!

Shout it

kick it on DotNetKicks.com

Thursday, November 25, 2010

WebFormContrib – Web Forms more like ASP.NET MVC




Lately I’ve been stuck working on projects using Web Forms and after reading one of Jimmy Bogard’s recent blog posts, I was inspired to create a small library similar to MVC Contrib, but for Web Forms. Obviously my little library won't be nearly as good as MVC Contrib because I'm not as talented as the guys at HeadSpring. However, I'm hoping this will be a good start to something great. Anyhow, the main thing that stuck out to me was the strongly-typed views, which I never used to do with Web Forms. With all that said, I thought I’d share what I created.

The project I’m currently working on is built on a 3rd party system and it’s difficult to add 3rd party references into our add-ons. So, I added a very basic validator into this thing, which will most likely be refactored, but works for now.

Onto the code I suppose…

So, first things first, let’s look at my WebForm view, which in this case is a UserControl, but the same thing can be done with a regular aspx Page.

<%@ Control Language="C#" AutoEventWireup="true" Inherits="WebFormContrib.Sample.Core.UI.Model.SampleControl" %>
<%
@ Import Namespace="WebFormContrib.Controls" %>
<%
@ Import Namespace="WebFormContrib.Helpers" %>
<h1>Employee Form</h1>
<%=ErrorMessages.ToList("Important Message").CssClass("err") %>
<div id="success" runat="server" enableviewstate="false" visible="false">Employee saved.</div>
<
p><%=Html<TextBox>(f=>f.FirstName).Label("First Name:<br/>").Width("300px").CssClass("tb") %></p>
<
p><%=Html<TextBox>(f=>f.LastName).Label("Last Name:<br/>").Width("300px") %></p>
<
p><%=Html<TextBox>(f=>f.EmailAddress).Label("Email Address:<br/>").Width("300px") %></p>
<
p>Do you want to receive emails?<br />
<%=Html<YesNoRadioButtonList>(f=>f.ReceiveEmails) %></p>

<
p><%=Html<TextArea>(f=>f.Bio).Width("500px").Label("Bio:<br/>") %></p>

<
asp:Button ID="bSend" runat="server" Text="Save Employee" CausesValidation="true" OnClick="Save" />

There are a few things to notice. First is the fact that there’s no code-behind page. Everything is inherited from my core project, just like I setup my MVC apps. You can see how I do that in this post.

As you can see, this is a total rip-off of the MVC type of HTML page. I left in a couple ASP controls too. There are a couple of controls that didn’t exist before like YesNoRadioButtonList and TextArea. These two objects were added as helpers.

All these extensions that make it look like fluentHtml are just extension methods on string. For example:

public static string Label(this string html, string label)
{
return string.Format("<label for=\"{0}\">{1}</label> {2}", html.GetId(), label, html);
}

So what does the SampleControl look like? It looks like this:

    public class SampleControl : BaseUserControl<EmployeeView>
{
private IEmployeeRepository _repository;
protected void Page_Load(object sender, EventArgs e)
{
_repository =
DI.CreateRepository();

if (Page.IsPostBack)
return;

loadEmployee();
}

private void loadEmployee()
{
var e = _repository.GetBy(Page.User.Identity.Name);
ViewModel =
EmployeeView.MapEmployeeToView(e); //Could user AutoMapper
}

protected void Save(object sender, EventArgs e)
{
if (!Page.IsValid || !ModelIsValid(ViewModel))
return;

var form = ViewModel.MapToEmployee(); //Could user AutoMapper
_repository.Save(form);

C<
HtmlGenericControl>("success").Visible = true;
}
}

This is all there is to my little employee edit user control. You should notice I set the ViewModel to a mapped version of my domain model to my view. In the save, I’m having to call both Page.IsValid and ModelIsValid…although since I don’t have any ASP validators, I could technically just call the ModelIsValid(ViewModel). Other than that, I map back to my domain model from my view model and call save. Then I use a generic control finder built into the BaseUserControl<T> to get my ASP success generic html control. You can also see that I added comments on where you might benefit from AutoMapper with this method.

So my view in this case looks like this:

    public class EmployeeView
{
[
Required("First Name is required.")]
public string FirstName { get; set; }
[
Required("Last Name is required.")]
public string LastName { get; set; }
public string Bio { get; set; }
public string EmailAddress { get; set; }
public bool ReceiveEmails { get; set; }

public Employee MapToEmployee()
{
var employee = new Employee();
employee.Name.FirstName = FirstName;
employee.Name.LastName = LastName;
employee.EmailAddress = EmailAddress;
employee.Biography = Bio;
return employee;
}

public static EmployeeView MapEmployeeToView(Employee employee)
{
var view = new EmployeeView();
view.FirstName = employee.Name.FirstName;
view.LastName = employee.Name.LastName;
view.EmailAddress = employee.EmailAddress;
view.Bio = employee.Biography;
return view;
}
}

The Required attributes are also contained in the WebFormContrib dll. It uses similar syntax to the MVC 2 validators. The BaseUserControl<T> looks like this:

    public class BaseUserControl<TModel> : BaseUserControl where TModel : new()
{
private readonly BaseWebForm<TModel> _baseWebForm;
public BaseUserControl()
{
_baseWebForm =
new BaseWebForm<TModel>();
}

protected IList<string> ErrorMessages
{
get { return _baseWebForm.ErrorMessages; }
}

protected bool DisableAll
{
get { return _baseWebForm.DisableAll; }
set { _baseWebForm.DisableAll = value; }
}

protected TModel ViewModel
{
get
{
try { _baseWebForm.Request = Request; }
catch { }
return _baseWebForm.ViewModel;
}
set { _baseWebForm.ViewModel = value; }
}

protected bool ModelIsValid(TModel view)
{
return _baseWebForm.ModelIsValid(view);
}

protected string Html<TControl>(Expression<Func<TModel, object>> func) where TControl : Control, new()
{
return _baseWebForm.Html<TControl>(func);
}
}

This inherits from a BaseUserControl, which inherits from UserControl and has another method associated that I’ll show later. Basically, this provides all the functionality of BaseWebForm<TModel>, which I will show last. The BaseWebForm<TModel> is the meat of the whole project. The try{} catch{} around the _baseWebForm.Request is only there so my unit tests won’t fail when trying to get the HttpRequest. So the BaseUserControl looks like this:

    public class BaseUserControl : UserControl
{
protected TControl C<TControl>(string id) where TControl : Control
{
return (TControl)FindControl(id);
}
}

Pretty basic stuff here…looks for the control and then casts to the specified type. BasePage<T> and BasePage look exactly the same as these two, except the BasePage inherits from Page instead of UserControl.

Now…BaseWebForm<T> I’m going to breakdown. It inherits from Control and T must allow new().

        internal TModel ViewModel
{
get
{
if (_view != null)
return _view;

_view =
new TModel();

foreach (var property in typeof (TModel).GetProperties())
try
{
if (Request.Form[property.Name] != null)
property.SetValue(_view, Request.Form[property.Name]);
}
catch (NullReferenceException) {}

return _view;
}
set
{
_view =
value;
}
}

First things first…the ViewModel…I loop through the properties of the view model object and pull out the key from Request.Form and then set the value of the property on the _view object. The try {} catch{} here is also for unit testing purposes.

The ModelIsValid looks like this:

       internal bool ModelIsValid(TModel view)
{
ErrorMessages =
new List<string>();
foreach (var property in typeof(TModel).GetProperties())
{
var attributes = property.GetCustomAttributes(typeof(RequiredAttribute), false);
if (attributes.Length > 0)
{
var value = property.GetValue(view, null);
if (value.ToSafeString() == string.Empty)
ErrorMessages.Add(((
RequiredAttribute)attributes[0]).Message);
}
}

if (ErrorMessages.Count == 0)
return true;

return false;
}

This will be the first thing I refactor. The thing I hate most is that the actual validation of the RequiredAttribute happens here instead of in the attribute. However, it works for now. If anyone has any suggestions, please add a comment.

Finally, the Html<TControl> looks like this:

    internal string Html<TControl>(Expression<Func<TModel, object>> func) where TControl : Control, new()
{
var id = func.ToId();
var control = new TControl { ID = id };
control.SetValue(func.Compile().Invoke(ViewModel));

using (var textwriter = new StringWriter())
using (var htmlwriter = new HtmlTextWriter(textwriter))
{
if (DisableAll)
control.Disable();
control.RenderControl(htmlwriter);
return textwriter.ToString();
}
}

This one required TControl to be of type Control and allow new(). I get the Id or PropertyName from func using an extension method and then I create the TControl object. Afterward, I set the value of it from ViewModel then I render it and return the string.

Here are the tests I have:

    [TestFixture]
public class BaseControlTests
{
[
Test]
public void Generic_C_should_return_any_object_by_id_contained_in_user_control_controls()
{
var testcontrol = new TestUserControl();
var textbox = testcontrol.GetTestUsingC<TextBox>("testid");

Assert.That(textbox, Is.TypeOf(typeof (TextBox)));
Assert.That(textbox.ID, Is.EqualTo("testid"));
}
}

public class TestUserControl : BaseUserControl
{
public TestUserControl()
{
Controls.Add(
new TextBox{ID="testid"});
}

public T GetTestUsingC<T>(string id) where T : Control
{
return C<T>(id);
}
}
    [TestFixture]
public class BaseControlTTests
{
[
Test]
public void Html_should_render_html_textbox_with_an_id_of_FirstName()
{
var testcontrol = new TestWebForm();
var textbox = testcontrol.ExposedHtml<TextBox>(f => f.FirstName);

Assert.That(textbox, Is.EqualTo("<input name=\"FirstName\" type=\"text\" id=\"FirstName\" />"));
}

[
Test]
public void Html_should_render_html_literal()
{
var testcontrol = new TestWebForm();
var literal = testcontrol.ExposedHtml<Literal>(x => x.LastName);
Assert.That(literal, Is.EqualTo(""));
}

[
Test]
public void Html_should_render_html_textarea_with_an_id_of_bio()
{
var testcontrol = new TestWebForm();
var textarea = testcontrol.ExposedHtml<TextArea>(x => x.Bio);
Assert.That(textarea, Is.EqualTo("<textarea name=\"Bio\" rows=\"10\" cols=\"20\" id=\"Bio\"></textarea>"));
}

[
Test]
public void ModelIsValid_should_return_false()
{
var testcontrol = new TestWebForm();

Assert.That(testcontrol.ExposedModelIsValid(), Is.False);
}

[
Test]
public void ModelIsValid_should_return_true()
{
var testcontrol = new TestWebForm();

testcontrol.ExposedViewModel.FirstName =
"test";
Assert.That(testcontrol.ExposedModelIsValid(), Is.True);
}

[
Test]
public void ModelIsValid_should_return_false_and_error_message_should_contain_one_record()
{
var testcontrol = new TestWebForm();

Assert.That(testcontrol.ExposedModelIsValid(), Is.False);
Assert.That(testcontrol.ExposedErrorMessages.Count, Is.EqualTo(1));
Assert.That(testcontrol.ExposedErrorMessages[0], Is.EqualTo("First Name is Required."));
}

[
Test]
public void Html_should_return_disabled_objects_if_disableall_is_true()
{
var testcontrol = new TestWebForm();
testcontrol.ExposedDisable =
true;

var textbox = testcontrol.ExposedHtml<TextBox>(f => f.FirstName);
Assert.That(textbox, Is.EqualTo("<input name=\"FirstName\" type=\"text\" readonly=\"readonly\" id=\"FirstName\" disabled=\"disabled\" />"));
}
}

public class TestWebForm : BasePage<TestViewModel>
{
public string ExposedHtml<TControl>(Expression<Func<TestViewModel, object>> func) where TControl : Control, new()
{
return Html<TControl>(func);
}

public bool ExposedModelIsValid()
{
return ModelIsValid(ViewModel);
}

public IList<string> ExposedErrorMessages
{
get { return ErrorMessages; }
}

public TestViewModel ExposedViewModel
{
get { return ViewModel; }
}

public bool ExposedDisable
{
get { return DisableAll;}
set { DisableAll = value; }
}
}

public class TestViewModel
{
[
Required("First Name is Required.")]
public string FirstName { get; set;}
public string LastName { get; set;}
public string EmailAddress { get; set;}
public string Bio { get; set; }
}

Well, that’s it for now. Please let me know what you think. You can download the code here. You can browse it here. You can download the dll here.

Please let me know if you have any suggestions or comments. Have a great day! Thanks for reading!

Happy Thanksgiving!


Shout it

kick it on DotNetKicks.com

Tuesday, October 12, 2010

Fluent Search Interface with some Func




UPDATED: 10/18/2010 11:42PM

So…how do you start a fluent interface? By typing how you’d want your code to read, at least that’s how I do it. So I created a unit test project and started typing how I’d like my interface to work. This is what I came up with:

Search<Person>.With(myPersonList).For("deran").By("FirstName").Result;

Alright well, that was simple enough. Now how do I make it work? First I’ll create my Search class with the With method:

    public class Search<T>
{
internal Search(IEnumerable<T> value)
{ }

public static ISearchRequired<T> With(IEnumerable<T> source)
{
return new Search<T>(source);
}
}

You can see here that I’m returning a ISearchRequired<T>, which looks like this:

    public interface ISearchRequired<T>
{
ISearchExtensions<T> For(string value);
ISearchExtensions<T> Not(string value);
}

The reason I have this interface is so I can control what the developer using my fluent interface sees. Each of these items returns the ISearchExtensions, which has more options. First I want to show the finished Search<T> class:

    public class Search<T> : SearchExtensions<T>, ISearchRequired<T>
{
internal Search(IEnumerable<T> value)
:
base(value)
{ }

public static ISearchRequired<T> With(IEnumerable<T> source)
{
return new Search<T>(source);
}

public ISearchExtensions<T> Not(string value)
{
_searchCriteria.Add(
new SearchCriteria { Value = value, NotEqual = true });
return this;
}

public ISearchExtensions<T> For(string value)
{
_searchCriteria.Add(
new SearchCriteria { Value = value });
return this;
}
}

Here’s the ISearchExtensions<T> interface:

    public interface ISearchExtensions<T>
{
ISearchExtensions<T> By(Expression<Func<T, string>> valueExpr);//changed from (string value)
ISearchExtensions<T> AndFor(string value);
ISearchExtensions<T> OrFor(string value);
ISearchExtensions<T> AndNot(string value);
IEnumerable<T> Result { get; }
}

You can also see in my completed Search<T> class, I have a SearchCriteria object that looks like this:

    public class SearchCriteria
{
public Expression<Func<T, string>>/*changed from string*/ By { get; set; }
public string Value { get; set; }
public LogicalOperator LogicalOperator { get; set; }
public bool NotEqual { get; set; }
}

public enum LogicalOperator
{
None, And, Or
}

Simple stuff so far…now for the fun stuff…the actual implementation, which looks like this:

public class SearchExtensions<T> : ISearchExtensions<T>
{
protected IList<SearchCriteria> _searchCriteria;
private readonly IEnumerable<T> _source;
internal SearchExtensions(IEnumerable<T> source)
{
_source = source;
_searchCriteria =
new List<SearchCriteria>();
}

public ISearchExtensions<T> By(Expression<Func<T, string>> valueExpr)
{
//REMOVED: var value = NoMagicStringHelper.GetPropertyFrom(valueExpr);

foreach (var sc in _searchCriteria.Where(x => x.By == null))
sc.By = value;

return this;
}


public ISearchExtensions<T> AndFor(string value)
{
_searchCriteria.Add(
new SearchCriteria { Value = value, LogicalOperator = LogicalOperator.And });
return this;
}

public ISearchExtensions<T> OrFor(string value)
{
_searchCriteria.Add(
new SearchCriteria { Value = value, LogicalOperator = LogicalOperator.Or });
return this;
}

public ISearchExtensions<T> AndNot(string value)
{
_searchCriteria.Add(
new SearchCriteria { Value = value, NotEqual = true });
return this;
}

public IEnumerable<T> Result
{
get
{
var predicate = PredicateBuilder.True<T>();
predicate = _searchCriteria.Where(x => !x.NotEqual)
.Aggregate(predicate, (current, criteria) =>
criteria.LogicalOperator ==
LogicalOperator.Or ?
current.Or(StartsWith(criteria.By, criteria.Value)) :
current.And(StartsWith(criteria.By, criteria.Value)));
predicate = _searchCriteria.Where(x => x.NotEqual)
.Aggregate(predicate, (current, criteria) =>
current.AndNot(StartsWith(criteria.By, criteria.Value)));
return _source.AsQueryable().Where(predicate);

//changed from:
//var predicate = PredicateBuilder.True<T>();
//predicate = _searchCriteria.Where(x => !x.NotEqual)
// .Aggregate(predicate, (current, criteria) =>
// criteria.LogicalOperator == LogicalOperator.Or ?
// current.Or(Contains(criteria.By, criteria.Value)) :
// current.And(Contains(criteria.By, criteria.Value)));
//predicate = _searchCriteria.Where(x => x.NotEqual)
// .Aggregate(predicate, (current, criteria) =>
// current.AndNot(Contains(criteria.By, criteria.Value)));
//return _source.AsQueryable().Where(predicate);
}
}






private static Expression<Func<T, bool>> StartsWith(Expression<Func<T, string>> searchBy, string searchValue)
{
var search = Expression.Constant(searchValue.ToLower(), typeof(string));
var param = Expression.Parameter(typeof(T), "x");
var containsMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var containsExp = Expression.Call(getProperty(searchBy, param), containsMethod, search);

return Expression.Lambda<Func<T, bool>>(containsExp, param);
}

//changed from:
//private static Expression<Func<T, bool>> Contains(string searchBy, string searchValue)
//{
// var search = Expression.Constant(searchValue, typeof (string));
// var searchByParam = Expression.Parameter(typeof (T), searchBy);
// var searchByExp = Expression.Property(searchByParam, searchBy);
// var methodInfo = typeof (string).GetMethod("Contains", new[] {typeof (string)});
// var containsExpression = Expression.Call(searchByExp, methodInfo, search);
// return Expression.Lambda<Func<T, bool>>(containsExpression, searchByParam);
//}

}

I think most of these are pretty self explanatory, with the exception of Result & Contains. Result takes advantage of a PredicateBuilder that I’ll show in a minute. Basically, it grabs all the search criteria that is equal and checks whether or not the command is or or and and builds the predicate. The next part grabs all the not equal criteria and builds its predicate. Finally it returns the course AsQueryable() where predicate. The AsQueryable is important because it’s what allows us not to have to call Compile() anywhere in our expression building.

Thanks to Rob White commenting below, I also added a NoMagicStringHelper class mentioned above in the new By() method. Here it is:

private static Expression getProperty(Expression<Func<T, string>> searchBy, Expression param)
{
Expression propExp = null;
var first = true;
foreach (var s in searchBy.Body.ToString().Split('.').Skip /*parameter*/(1))
if (first)
{
propExp =
Expression.Property(param, s);
first =
false;
}
else
{
if (s.StartsWith("ToString"))
propExp =
Expression.Call(propExp, "ToString", null);
else
propExp = Expression.Property(propExp, s);
}

return Expression.Call(Expression.Coalesce(propExp, Expression.Constant(String.Empty)), "ToLower", null);
}

//DELETED:
//public class NoMagicStringHelper
//{
// public static string GetPropertyFrom<T>(Expression<Func<T, string>> expr)
// {
// MemberExpression me = null;
// var body = expr.Body;
// if (body is MemberExpression) me = body as MemberExpression;
// else if (body is UnaryExpression)
// {
// var ue = body as UnaryExpression;
// me = ue.Operand as MemberExpression;
// }
// if (me == null) throw new NotImplementedException("Only Member and Unary Expressions implemented.");
// return me.Member.Name;
// }
//}

The Contains method just builds the Contains expression based on the search criteria. I used this same approach back in May, for more details, check it out.

The PredicateBuilder is pretty cool, I found it here on the C# 4.0/3.01 in a Nutshell site. It looks like this:

public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }

public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());

return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}

public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());

return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}

public static Expression<Func<T, bool>> AndNot<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, Expression.Not(invokedExpr)), expr1.Parameters);
}
}

I added the AndNot<T> method to handle the Not and NotFor methods of my interface.

And of course, I have some tests to go along: I added more tests, but this post is long enough. You can download the source and check them out though.

    [TestFixture]
public class SearchTests
{
private List<Person> myPersonList;

[
TestFixtureSetUp]
public void Setup()
{
myPersonList =
new List<Person>();
myPersonList.Add(
new Person() { FirstName = "pappa", LastName = "racer" });
myPersonList.Add(
new Person() { FirstName = "speed", LastName = "racer" });
myPersonList.Add(
new Person() { FirstName = "george", LastName = "jetson" });
}

[
Test]
public void Result_should_return_search_results()
{
var result = Search<Person>.With(myPersonList).For("speed").By(x=>x.FirstName).Result;

Assert.That(result.Count(), Is.EqualTo(1));
}
[
Test]
public void Result_should_return_search_results_not_containing_speed()
{
var result = Search<Person>.With(myPersonList).Not("speed").By(x => x.FirstName).Result;

Assert.That(result.Count(), Is.EqualTo(2));
}
[
Test]
public void Result_should_return_search_results_containing_speed_or_pappa()
{
var result = Search<Person>.With(myPersonList).For("speed").OrFor("pappa").By(x => x.FirstName).Result;

Assert.That(result.Count(), Is.EqualTo(2));
}
[
Test]
public void Result_should_return_search_results_containing_pappa_or_speed_by_firstname_or_jetson_by_lastname()
{
var result = Search<Person>.With(myPersonList).For("pappa").OrFor("speed").By(x => x.FirstName).OrFor("jetson").By(x=>x.LastName).Result;

Assert.That(result.Count(), Is.EqualTo(3));
}
[
Test]
public void Result_should_return_search_results_containing_pappa_or_george_by_firstname_and_not_racer_by_lastname()
{
var result = Search<Person>.With(myPersonList).For("pappa").OrFor("george").By(x => x.FirstName).AndNot("racer").By(x=>x.LastName).Result;

Assert.That(result.Count(), Is.EqualTo(1));
}
}

internal class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}

You can download the source code here.

Thanks for reading and as always, please let me know what you think!

Shout it

kick it on DotNetKicks.com

Related Posts Plugin for WordPress, Blogger...