Applications

Implementing Entity Keywords

Implementing Entity Keywords

Use this feature in your application when you want to enhance searchability of your entities by allowing users to tag them with existing keywords or create their own.
Configuring your domain classes, view model, and adding the relationship
It is implicit that your entities and keywords are associated to each other in a many-to-many relationship. Here’s what your domain classes should look like:
public class Object
{
    public int ObjectId { get; set; } *
    public string Name { get; set; }
    // other entity properties
    public virtual ICollection<Keyword> Keyword { get; set; } **
}
 
public class Keyword
{
    public int KeywordId { get; set; } *
    public string Name { get; set; }
    public ICollection<Object> Object { get; set; } **
}
In your context class, you need to override the OnModelCreating method and add the following fluent API:
modelBuilder.Entity<Object>().HasMany(p => p.Keyword).WithMany(t => t.Object)
            .Map(mc =>
                {
                    mc.ToTable("ObjectsJoinKeywords");
                    mc.MapLeftKey("ObjectId"); *
                    mc.MapRightKey("KeywordId"); *
                });
* If the Id (PK) field of each class has the same name Entity Framework won’t be able to map correctly to the database, use the ClassName + Id convention
** Use ICollection<>, EF doesn’t like IEnumerable<>. Also keep these properties singularized to prevent ModelState errors.
 
Add the Keywords to the ViewBag and the field to the view
In the controller’s Create methods (the Http Get and Post versions), call the PopulateKeywords method before returning the Create view to add all available (and previously selected, if any) keywords to the ViewBag as a Collection<SelectListItem>:
private void PopulateKeywords(Object object)
{
    var keywords = appService.GetAllKeywords();
    var objectJoinKeywords = new HashSet<int>(object.Keyword.Select(k => k.KeywordId));
    var selectList = new Collection<SelectListItem>();
    foreach (var keyword in keywords)
    {
        if (objectJoinKeywords.Contains(keyword.KeywordId))
        {
            var selectedItem = new SelectListItem {Text = keyword.Name, Selected = true};
            selectList.Add(selectedItem);
        }
        else
        {
            var item = new SelectListItem { Text = keyword.Name };
            selectList.Add(item);
        }
    }
    ViewBag.Keywords = selectList;
}
In the view you’ll need a ListBox helper to display the available keywords:
<div>
    @Html.ListBox("Keywords", ViewBag.Keywords as MultiSelectList, new
        {
            @class = "chzn-select",
            id = "KeywordSearch",
            data_placeholder = "Add Keywords…",
            style = "width:250px;"
        })
</div>
 
Adding the NuGet Package and making the JavaScript
Right-click on your project and select Manage NuGet Packages…, search for chosen.jquery. Installing this package will add the full and minified .js files along with the .css stylesheet and sprite .png.
Now add a new .js file to your Scripts folder and add the following code:
$(document).ready(function () {
    $(".chzn-select").chosen();
    $(":text").each(function () {
        $(this).on(‘keyup’, function (e) {
            if (e.which == 13) {
                $(".chzn-select").append(‘<option selected>’ + $(this).val() + ‘</option>’);
                $(".chzn-select").trigger(‘liszt:updated’);
            }
        });
    });
})
You may stop after the .chosen(); statement for out-of-the-box functionality, this will keep your keyword store "managed" and will only populate the field with existing keywords. However if you want to dynamically add new keyword options, you can trigger an update when the Enter key is hit from the search box.
Lastly you’ll need to reference this script and the chosen.css stylesheet in the view. In the Scripts block, add:
@section Scripts {
    @Scripts.Render("~/Scripts/chosen.jquery.js")
    @Scripts.Render("~/Scripts/MyChosenScript.js")
    @Styles.Render("~/Content/chosen.css")
}

Leave a Reply