in

Chennai .Net User Group

A platform that enables you to Learn, Share & Grow (India's first .Net user group)

Shiju Varghese's Blog

June 2010 - Posts

  • Speaking about ASP.NET MVC 2 at DevCon 2010

    I will be speaking about ASP.NET MVC 2 at the DevCon 2010 organized by Kerala Microsoft User Group (K-Mug). The DevCon 2010 event will be conducted in Techopark, Trivandrum, India on 3rd and 4th July, 2010. My ASP.NET MVC 2 session will be introducing the ASP.NET MVC and will walk through several features of the ASP.NET MVC 2 framework along with Visual Studio 2010. The other seesions including Cloud computing and Windows Azure, .NET 4.0 & Visual Studio 2010, Robotics Programming, Web Security and Security Auditing, Application Compatibility in Windows 7, SQL Server 2008, Mixed Mode Windows development using C# and C++ and Windows 7 Phone. The event details available at http://k-mug.org/events/devcon2010/.

  • How to work RavenDB Id with ASP.NET MVC Routes

    By default RavenDB's Id would be sperated by "/". Let's say that we have a category object, the Ids would be like "categories/1". This will make problems when working with ASP.NET MVC's route rule. For a route category/edit/id, the uri would be category/edit/categories/1. You can solve this problem in two ways

    Solution 1 - Change Id Separator

    We can use different Id Separator for RavenDB Ids in order to working with ASP.NET MVC route rules. The following code specify that Ids would be seperated by "-" rather than the default "/"

     documentStore = new DocumentStore { Url = "http://localhost:8080/" };

     documentStore.Initialize();

     documentStore.Conventions.IdentityPartsSeparator = "-";


    The above IdentityPartsSeparator would be generate Ids like "categories-1"

    Solution 2 - Modify ASP.NET MVC Route

    Modify the ASP.NET MVC routes in the Global.asax.cs file, as shown in the following code

     

    routes.MapRoute(

        "WithParam",                                           // Route name

        "{controller}/{action}/{*id}"                         // URL with parameters

        );

     We just put "*" in front of the id variable that will be working with the default Id separator of RavenDB

  • NoSQL with RavenDB and ASP.NET MVC - Part 2

    In my previous post, we have discussed on how to work with RavenDB document database in an ASP.NET MVC application. We have setup RavenDB for our ASP.NET MVC application and did basic CRUD operations against a simple domain entity. In this post, let’s discuss on domain entity with deep object graph and how to query against RavenDB documents using Indexes. You can download the source code from http://ravenmvc.codeplex.com

    Let's create two domain entities for our demo ASP.NET MVC appplication

     

    public class Category

    {

     

        public string Id { get; set; }

        [Required(ErrorMessage = "Name Required")]

        [StringLength(25, ErrorMessage = "Must be less than 25 characters")]

        public string Name { get; set;}

        public string Description { get; set; }

        public List<Expense> Expenses { get; set; }

     

        public Category()

        {

            Expenses = new List<Expense>();

        }

    }

      

    public class Expense

    {

     

        public string Id { get; set; }

        public Category Category { get; set; }

        public string  Transaction { get; set; }

        public DateTime Date { get; set; }

        public double Amount { get; set; }

     

    }

     

    We have two domain entities - Category and Expense. A single category contains a list of expense transactions and every expense transaction should have a Category.

    Let's create  ASP.NET MVC view model  for Expense transaction

    public class ExpenseViewModel

    {

        public string Id { get; set; }

     

        public string CategoryId { get; set; }

     

        [Required(ErrorMessage = "Transaction Required")]       

        public string Transaction { get; set; }

     

        [Required(ErrorMessage = "Date Required")]       

        public DateTime Date { get; set; }

     

        [Required(ErrorMessage = "Amount Required")]

        public double Amount { get; set; }

     

        public IEnumerable<SelectListItem> Category { get; set; }

    }

    Let's create a contract type for Expense Repository

     

    public interface IExpenseRepository

    {

        Expense Load(string id);

        IEnumerable<Expense> GetExpenseTransactions(DateTime startDate,DateTime endDate);

        void Save(Expense expense,string categoryId);

        void Delete(string id); 

    }

    Let's create a concrete type for Expense Repository for handling CRUD operations.

    public class ExpenseRepository : IExpenseRepository

    {

     

    private IDocumentSession session;

    public ExpenseRepository()

    {

            session = MvcApplication.CurrentSession;

    }

    public Expense Load(string id)

    {

        return session.Load<Expense>(id);

    }

    public IEnumerable<Expense> GetExpenseTransactions(DateTime startDate, DateTime endDate)

    {      

     

        //Querying using the Index name "ExpenseTransactions"

        //filtering with dates

        var expenses = session.LuceneQuery<Expense>("ExpenseTransactions")

            .WaitForNonStaleResults()

            .Where(exp => exp.Date >= startDate && exp.Date <= endDate)

            .ToArray();

        return expenses;

    }

    public void Save(Expense expense,string categoryId)

    {

        var category = session.Load<Category>(categoryId);

        if (string.IsNullOrEmpty(expense.Id))

        {

            //new expense transaction

            expense.Category = category;

            session.Store(expense);

        }

        else

        {

            //modifying an existing expense transaction

            var expenseToEdit = Load(expense.Id);

            //Copy values to  expenseToEdit

            ModelCopier.CopyModel(expense, expenseToEdit);

            //set category object

            expenseToEdit.Category = category;

     

        }

        //save changes

        session.SaveChanges();

    }

    public void Delete(string id)

    {

        var expense = Load(id);

        session.Delete<Expense>(expense);

        session.SaveChanges();

    }

     

    }

     


    Insert/Update Expense Transaction

    The Save method is used for both insert a new expense record and modifying an existing expense transaction. For a new expense transaction, we store the expense object with associated category into document session object and load the existing expense object and assign values to it for editing a existing record.

     

    public void Save(Expense expense,string categoryId)

    {

        var category = session.Load<Category>(categoryId);

        if (string.IsNullOrEmpty(expense.Id))

        {

            //new expense transaction

            expense.Category = category;

            session.Store(expense);

        }

        else

        {

            //modifying an existing expense transaction

            var expenseToEdit = Load(expense.Id);

            //Copy values to  expenseToEdit

            ModelCopier.CopyModel(expense, expenseToEdit);

            //set category object

            expenseToEdit.Category = category;

     

        }

        //save changes

        session.SaveChanges();

    }



    Querying Expense transactions

     

    public IEnumerable<Expense> GetExpenseTransactions(DateTime startDate, DateTime endDate)

    {      

     

        //Querying using the Index name "ExpenseTransactions"

        //filtering with dates

        var expenses = session.LuceneQuery<Expense>("ExpenseTransactions")

            .WaitForNonStaleResults()

            .Where(exp => exp.Date >= startDate && exp.Date <= endDate)

            .ToArray();

        return expenses;

    }

     

    The GetExpenseTransactions method returns expense transactions using a LINQ query expression with a Date comparison filter. The Lucene Query is using a index named "ExpenseTransactions" for getting the result set. In RavenDB, Indexes are LINQ queries stored in the RavenDB server and would be  executed on the background and will perform query against the JSON documents. Indexes will be working with a lucene query expression or a set operation. Indexes are composed using a Map and Reduce function. Check out Ayende's blog post on Map/Reduce

    We can create index using RavenDB web admin tool as well as programmitically using its Client API. The below shows the screen shot of creating index using web admin tool.

     


    In the Map function, we used a Linq expression as shown in the following

    from exp in docs.Expenses
    select new { exp.Date };

    We can also create Indexes using Raven Cleint API as shown in the following code

    documentStore.DatabaseCommands.PutIndex("ExpenseTransactions",

        new IndexDefinition<Expense,Expense>()

    {

        Map = Expenses => from exp in Expenses

                        select new { exp.Date }

    });

     


    We have not used a Reduce function for the above index. A Reduce function is useful while performing aggregate functions based on the results from the Map function. Indexes can be use with set operations of RavenDB.

    SET Operations

    Unlike other document databases, RavenDB supports set based operations that lets you to perform updates, deletes and inserts to the bulk_docs endpoint of RavenDB. For doing this, you just pass a query to a Index as shown in the following command

    DELETE http://localhost:8080/bulk_docs/ExpenseTransactions?query=Date:20100531

    The above command using the Index named "ExpenseTransactions" for querying the documents with Date filter and  will delete all the documents that match the query criteria. The above command is equivalent of the following query

    DELETE FROM Expenses
    WHERE Date='2010-05-31'

    Controller & Actions

    We have created Expense Repository class for performing CRUD operations for the Expense transactions. Let's create a controller class for handling expense transactions.

     

    public class ExpenseController : Controller

    {

    private ICategoryRepository categoyRepository;

    private IExpenseRepository expenseRepository;

    public ExpenseController(ICategoryRepository categoyRepository, IExpenseRepository expenseRepository)

    {

        this.categoyRepository = categoyRepository;

        this.expenseRepository = expenseRepository;

    }

    //Get Expense transactions based on dates

    public ActionResult Index(DateTime? StartDate, DateTime? EndDate)

    {

        //If date is not passed, take current month's first and last dte

        DateTime dtNow;

        dtNow = DateTime.Today;

        if (!StartDate.HasValue)

        {

            StartDate = new DateTime(dtNow.Year, dtNow.Month, 1);

            EndDate = StartDate.Value.AddMonths(1).AddDays(-1);

        }

        //take last date of startdate's month, if endate is not passed

        if (StartDate.HasValue && !EndDate.HasValue)

        {

            EndDate = (new DateTime(StartDate.Value.Year, StartDate.Value.Month, 1)).AddMonths(1).AddDays(-1);

        }

     

        var expenses = expenseRepository.GetExpenseTransactions(StartDate.Value, EndDate.Value);

        if (Request.IsAjaxRequest())

        {

     

            return PartialView("ExpenseList", expenses);

        }

        ViewData.Add("StartDate", StartDate.Value.ToShortDateString());

        ViewData.Add("EndDate", EndDate.Value.ToShortDateString());        

        return View(expenses);           

    }

     

    // GET: /Expense/Edit

    public ActionResult Edit(string id)

    {

     

        var expenseModel = new ExpenseViewModel();

        var expense = expenseRepository.Load(id);

        ModelCopier.CopyModel(expense, expenseModel);

        var categories = categoyRepository.GetCategories();

        expenseModel.Category = categories.ToSelectListItems(expense.Category.Id.ToString());               

        return View("Save", expenseModel);         

    }

     

    //

    // GET: /Expense/Create

     

    public ActionResult Create()

    {

        var expenseModel = new ExpenseViewModel();          

        var categories = categoyRepository.GetCategories();

        expenseModel.Category = categories.ToSelectListItems("-1");

        expenseModel.Date = DateTime.Today;

        return View("Save", expenseModel);

    }

     

    //

    // POST: /Expense/Save

    // Insert/Update Expense Tansaction

    [HttpPost]

    public ActionResult Save(ExpenseViewModel expenseViewModel)

    {

        try

        {

            if (!ModelState.IsValid)

            {

     

                var categories = categoyRepository.GetCategories();

                    expenseViewModel.Category = categories.ToSelectListItems(expenseViewModel.CategoryId);                  

                return View("Save", expenseViewModel);

            }

     

            var expense=new Expense();

            ModelCopier.CopyModel(expenseViewModel, expense); 

            expenseRepository.Save(expense, expenseViewModel.CategoryId);              

            return RedirectToAction("Index");

        }

        catch

        {

            return View();

        }

    }

    //Delete a Expense Transaction

    public ActionResult Delete(string id)

    {

        expenseRepository.Delete(id);

        return RedirectToAction("Index");

     

     

    }    

    }

     

      Download the Source - You can download the source code from http://ravenmvc.codeplex.com

Copyright © 2002-2008 Chennai .Net User Group. All Rights Reserved. Microsoft and Microsoft logo's are trademarks of Microsoft Corporation