Skip to content

5. Specifications: data filters

Dmitry Savchenko edited this page Jul 15, 2016 · 5 revisions

Another task that often occurs in real projects is to clean requested data. Incoding Framework uses WhereSpecifications for convenient code writing and complying an encapsulation principle for cleaning data from Query. In the written code add a possibility to clean data from GetPeopleQuery by FirstName and LastName. First, add to Example.Domain -> Persistences -> Human.cs file abstract class Where and two classes into this abstract class: ByFirstName and ByLastName

Human.cs with specs
``` namespace Example.Domain { #region << Using >>
using System;
using System.Linq.Expressions;
using Incoding;
using Incoding.Data;

#endregion

public class Human : IncEntityBase
{
    #region Properties

    public virtual DateTime Birthday { get; set; }

    public virtual string FirstName { get; set; }

    public new virtual string Id { get; set; }

    public virtual string LastName { get; set; }

    public virtual Sex Sex { get; set; }

    #endregion

    #region Nested Classes

    public class Map : NHibernateEntityMap<Human>
    {
        #region Constructors

        protected Map()
        {
            IdGenerateByGuid(r => r.Id);
            MapEscaping(r => r.FirstName);
            MapEscaping(r => r.LastName);
            MapEscaping(r => r.Birthday);
            MapEscaping(r => r.Sex);
        }

        #endregion
    }

    public abstract class Where
    {
        #region Nested Classes

        public class ByFirstName : Specification<Human>
        {
            #region Properties

            readonly string firstName;

            #endregion

            #region Constructors

            public ByFirstName(string firstName)
            {
                this.firstName = firstName;
            }

            #endregion

            public override Expression<Func<Human, bool>> IsSatisfiedBy()
            {
                if (string.IsNullOrEmpty(this.firstName))
                    return null;

                return human => human.FirstName.ToLower().Contains(this.firstName.ToLower());
            }
        }

        public class ByLastName : Specification<Human>
        {
            #region Properties

            readonly string lastName;

            #endregion

            #region Constructors

            public ByLastName(string lastName)
            {
                this.lastName = lastName;
            }

            #endregion

            public override Expression<Func<Human, bool>> IsSatisfiedBy()
            {
                if (string.IsNullOrEmpty(this.lastName))
                    return null;

                return human => human.LastName.ToLower().Contains(this.lastName.ToLower());
            }
        }

        #endregion
    }

    #endregion
}

public enum Sex
{
    Male = 1,

    Female = 2
}

}

<p>Now use the written specifications in GetPeopleQuery. .Or()/.And() relations allow to merge atomic specifications that helps to use the created specifications many times and fine-tune necessary data filters (in the example we use .Or() relation)</p>
<h6>GetPeopleQuery.cs with specs</h6>

namespace Example.Domain { #region << Using >>

using System.Collections.Generic;
using System.Linq;
using Incoding.CQRS;
using Incoding.Extensions;

#endregion

public class GetPeopleQuery : QueryBase<List<GetPeopleQuery.Response>>
{
    #region Properties

    public string Keyword { get; set; }

    #endregion

    #region Nested Classes

    public class Response
    {
        #region Properties

        public string Birthday { get; set; }

        public string FirstName { get; set; }

        public string Id { get; set; }

        public string LastName { get; set; }

        public string Sex { get; set; }

        #endregion
    }

    #endregion

    protected override List<Response> ExecuteResult()
    {
        return Repository.Query(whereSpecification: new Human.Where.ByFirstName(Keyword)
                                        .Or(new Human.Where.ByLastName(Keyword)))
                         .Select(human => new Response
                                          {
                                                  Id = human.Id,
                                                  Birthday = human.Birthday.ToShortDateString(),
                                                  FirstName = human.FirstName,
                                                  LastName = human.LastName,
                                                  Sex = human.Sex.ToString()
                                          }).ToList();
    }
}

}

<p>Finally, it only remains to modify Index.cshtml in order to add a search box, which uses a Keyword field for data cleaning while a request is being processed.</p>
<h6>Index.cshtml</h6>

@using Example.Domain @using Incoding.MetaLanguageContrib @using Incoding.MvcContrib @{ Layout = "~/Views/Shared/_Layout.cshtml"; }

@(Html.When(JqueryBind.Click) .Direct() .OnSuccess(dsl => dsl.WithId("PeopleTable").Core().Trigger.Incoding()) .AsHtmlAttributes() .ToButton("Find"))

@(Html.When(JqueryBind.InitIncoding) .AjaxGet(Url.Dispatcher().Query(new GetPeopleQuery { Keyword = Selector.Jquery.Id("Keyword") }).AsJson()) .OnSuccess(dsl => dsl.Self().Core().Insert.WithTemplateByUrl(Url.Dispatcher().AsView("~/Views/Home/HumanTmpl.cshtml")).Html()) .AsHtmlAttributes(new { id = "PeopleTable" }) .ToDiv())

@(Html.When(JqueryBind.Click) .AjaxGet(Url.Dispatcher().AsView("~/Views/Home/AddOrEditHuman.cshtml")) .OnSuccess(dsl => dsl.WithId("dialog").Behaviors(inDsl => { inDsl.Core().Insert.Html(); inDsl.JqueryUI().Dialog.Open(option => { option.Resizable = false; option.Title = "Add human"; }); })) .AsHtmlAttributes() .ToButton("Add new human"))

<br/>
<ul>
<li><a href="/IncodingSoftware/get-started/wiki/4.-CRUD">Previous</a></li>
<li><a href="/IncodingSoftware/get-started/wiki/6.-Unit-tests">Next</a></li>
</ul>
Clone this wiki locally