Browsing:

Tag: C#

Db4o and ASP.NET MVC 2 part 1

Today Scott Guthrie anounced the final release of ASP.NET MVC 2. So it's a good time to give this a spin and check out the enhancements, like the new model validation and asynchronous controllers.

The best way to learn is to actually build an application. That's what I would like to do here. I am going to build a blog application. For persistence I will use db4o, an object oriented database. Reasons for using Db40 is convenience. With Db4o, you don't need to run SQL Server, which makes your code very portable and extremely easy to adjust to changing businessneeds.

The way I code this application is probably not from the books. It's just how I think I should code. If you can think of any improvements please do not hesitate and tell us.

IkZeg
I'm going to call this application 'IkZeg'. It's a blog application, so we need a class for stories and for writers. We also like to enable comments. The classdiagram would be something like this:
ClassDiagram2

Now install ASP.NET MVC 2 and download Db40 for .NET 3.5

Start Visual Studio (2008, I won't use 2010 until it's RTM-ed) and create a new ASP.NET MVC 2 Web Application: IkZeg.

start

I'm not adding a Test project because I just want to get going as fast as possible.

Then, rightclick the folder Models and add a new class and name it: Story.

public partial class Story
    {
        public Story()
        {
            storyId = System.Guid.NewGuid();
            commentsCount = 0;
            storyDate = DateTime.Now;
        }
        public Guid storyId { get; set; }
        public string storyTitle { get; set; }
        public string storyText { get; set; }
        public DateTime storyDate { get; set; }
        public Writer writer { get; set; }
        public int commentsCount { get; set; }
        public List

Also add a Writer and a Comment class:

 public class Writer
    {
       public Writer()
       {
           registerDate = DateTime.Now;
       }
        public string username { get; set; }
        public string password { get; set; }
        public string email { get; set; }
        public DateTime registerDate { get; set; }
        //the birthday is nullable:
        public DateTime? birthday { get; set; }
    }

public class Comment
    {
        public Comment()
        {
            postedDate = DateTime.Now;
        }
        public string username { get; set; }
        public string commentOn { get; set; }
        public DateTime postedDate { get; set; }
        public string commentText { get; set; }
    }

Now that we've created the model it's time to dive into persistence with Db4o.

Data Access with Db4o

An object-oriented database stores objects directly in the database without having to map it to a relational structure. This is a major timesaver and it has lots of other advantages (and disadvantages) but that discussion is a bit out of scope. Db4o is just awesome to use.

Add references to dll's from Db4o: Db40objects.Db4o, Db40objects.Db4o.CS and Db40objects.Db4o.Linq:

ref

To implement persistence I followed Rob Conery because yes, he's my hero. I learn an awful lot from him and from the Tekpub vids.

Data Access

To get started with Db4o is really simple. After adding the references you can start doing CRUD operations immediately, like so:

using(IObjectContainer db = Db4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(),@"c:\temp\thebigblahdatabase.dat"))
{
    Story story = new Story();
    story.storyTitle="Wat doen we met Pasen?!"
    story.storyText="Codekloppen, obviously!"
    db.store(story);
    db.Close();
}

But that would mean we would be repeating ourselves continuously. With every CRUD operation, we would have to write the same piece of code and worry about closing the database. It would be nice to abstract these operations away. Also, maybe we would like to migrate to MySQL or Oracle later. So let's take another approach.

Unit of Work

We'll set up a Unit of Work. This takes care of CRUD methods. The value of using a Unit of Work pattern is to free the rest of your code from these concerns so that you can concentrate on business logic.

DataAccess

using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Db4objects.Db4o.CS;

namespace IkZeg.Models
{
    public interface ISession:IDisposable 
    {
        void CommitChanges();
        Db4objects.Db4o.IObjectContainer Container { get; }
        void Delete(System.Linq.Expressions.Expression> expression);
        void Delete(T item);
        void DeleteAll();
        void Dispose();
        T Single(System.Linq.Expressions.Expression> expression);
        System.Linq.IQueryable All();
        void Save(T item);
    }
}

There's a lot of funky generics in there. That's good, because we need to implement this interface for all our ikZeg classes (Story, Writer and Comment).

Now let's create a class that implements the ISession interface we just created:

using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Db4objects.Db4o.CS;

namespace IkZeg
{
    public class Db4oSession : IDisposable,ISession
    {
        private IObjectContainer db;
        public IObjectContainer Container {
            get {
                return db;
            }
        }

        public Db4oSession(IObjectServer server) {
            db=server.OpenClient();
        }

       /// 
        /// Returns all T records in the repository
        /// 
        public IQueryable All() {
            return (from T items in db
                    select items).AsQueryable();
        }
/// 
        /// Finds a subset of items using a passed-in expression lambda
        /// 
        public IQueryable Where(System.Linq.Expressions.Expression> expression)
        {
            return All().Where(expression);
        }
 
        
        /// 
        /// Finds an item using a passed-in expression lambda
        /// 
        public T Single(System.Linq.Expressions.Expression> expression) {
            return All().SingleOrDefault(expression);
        }
 
        /// 
        /// Saves an item to the database
        /// 
        /// 
        public void Save(T item) {
            db.Store(item);
        }
 
        /// 
        /// Deletes an item from the database
        /// 
        /// 
        public void Delete(T item) {
            db.Delete(item);
        }
 
        /// 
        /// Deletes subset of objects
        /// 
        public void Delete(System.Linq.Expressions.Expression> expression) {
            var items = All().Where(expression).ToList();
            items.ForEach(x => db.Delete(x));
        }
 
        /// 
        /// Deletes all T objects
        /// 
        public void DeleteAll() {
            var items = All().ToList();
            items.ForEach(x => db.Delete(x));
        }
 
 
        /// 
        /// Commits changes to disk
        /// 
        public void CommitChanges() {
            //commit the changes
            db.Commit();
 
        }


        public void Dispose() {
            //explicitly close
            db.Close();
            //dispose 'em
            db.Dispose();
        }
    }
}

       

Now create a class that inherits from Db4oSession and it all starts making sense:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Db4objects.Db4o.CS;

namespace IkZeg.Models
{
    public class IkZegSession : Db4oSession
    {
        internal IkZegSession(IObjectServer server) : base(server) { }

        public IQueryable stories
        {
            get
            {
                return All();
            }
        }


        public IQueryable storiesByWriter(string writer)
        {
            return Where(s => s.writer.ToString() == writer);
        }


        public Story getStory(string id)
        {
            return Single(s => s.storyId.ToString() == id);
        }

        public int getComments(string storyGuid)
        {
            return Where(c => c.commentOn == storyGuid).Count();
        }

       }
}

And here is the class that holds the Db40 file- and connectioninfo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Db4objects.Db4o;
using Db4objects.Db4o.CS;
using Db4objects.Db4o.Linq;

namespace IkZeg.Models
{
    public class SessionFactory
    {
        static ISession _current;
       
        static IObjectServer _server;

        public static ISession CreateSession()
        {
            if (_server == null)
            {
                _server = Db4oFactory.OpenServer( @"c:\temp\debigblahdatabase", 0);
            }

            return new IkZegSession(_server);
        }

        public static IkZegSession Current
        {
            get
            {
                if (_current == null)
                    _current = CreateSession();
                return (IkZegSession)_current;
            }
        }

    }
}

Let's use this

So, let's create a story and show it on the website. To do that, I'll need a controller and a view. Let's start with the controller.

Right click the folder Controllers and click Add Controller.
Name the Controller StoryController and add action methods. Like this:

contr2

Add using statements for Db4o and the Models folder:

using IkZeg.Models;
using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Db4objects.Db4o.CS;

Just below the StoryController declare an IkZegSession:

  public class StoryController : Controller
    {
        IkZegSession db = SessionFactory.Current;

Scroll down to line 63 or so to the Create method and make it look like this:

   [HttpPost]
        public ActionResult Create(Story story)
        {
            if (ModelState.IsValid)
            {
                    story.writer = new Writer { username = Request.Form["writer.username"] };
                    story.storyDate = DateTime.Now;
                    db.Save(story);
                    return RedirectToAction("Index");
                }

                        else
            {
                return View();
            }
        }

Now add the view. Right click the Create Method (along line 65) and click Add View.

addView

Create a strongly-typed view. The data class is IkZeg.Models.Story. That's the story class we made earlier.

view2

Almost done now. Let's take a look a this View though. In the folder Views, you have now a Story folder, with a file named Create.aspx. Open the file and remove the HTML fieldset (because I think it's ugly). Also remove the Guid labels and textboxes and the Postdate labels and textboxes. We don't need them as they are generated in the Create method in the controller.

Hit debug (or F5). The webbrowser starts with the standard webpage. Browse to: http://localhost:51305(or whatever portnumber)/Story/Create. And there we are. It's a bit ugly but we will work at the layout later.
cr

After clicking Create you'll get a Yellow Screen of Death, which is okay, as we did not implement the Index method yet. I will cover that (and validation) in part 2.


And now for the NOSQL movement

As I wrote in my last post from (shame on me!) last year, I don't like to focus on databases that much. I'd rather spend my time with domain objects instead of thinking about inner joins, cross outer joins, views and stored procedures. Why? Because I always had to do things twice: designing a domain model and then designing the database for persistence. Or, even work with an existing database! And those two often clashed (the famous object-relational impedance mismatch).

Object Relational Mapping (ORM) like SubSonic and nHibernate are great in this respect but the mapping can be somewhat complex (which might be not true anymore for the most recent version of nHibernate which I haven't checked out yet). Then Microsoft introduced Linq 2 SQL which is great to quickly generate classes from a database (not the other way round unfortunately). But rumour has it Microsoft is turning Linq 2 SQL into legacy.

In "The end of SQL and relational databases?" the author states "With the growing trend of ORMs (Object Relational Mapping), some software developers are saying that SQL is becoming less relevant." Now whether that statement is true or false, it sure is very interesting to investigate some Object Databases, the alternative method for data persistance.

Object Databases aren't a new phenonemon, as you can see here there are quite a few of them. I decided to give MongoDB a shot, with a little help from this article.

Download MongoDB (Windows 32 bits for me) and extract it to c:\data\db (this is the default). You can start the database with mongod.exe
Capture

Next, download these 2 dll's to talk to MongoDB from C#.
Fire up Visual Studio and create a new C# console application. Add a reference to those two dll's like this:
Capture

And finally some code:

static void Main(string[] args)
      {
         //create a new Mongo instance
         var mongo = new Mongo();
         //connect to it
         mongo.Connect();

         //connect to DB (or create new if it not exists)
         var db = mongo.getDB("Frots");

         //connect to the collection (like a table)
         var frotten = db.GetCollection("frotten");

         //create a document (like a record)
         var newFrot = new Document();
         newFrot["titel"] = "nosql";
         newFrot["tekst"] = "Frotten zonder RDBMS is goed";
         frotten.Insert(newFrot);

         //find a document
         var zoekFrot = new Document();
         zoekFrot["titel"] = "nosql";
         var gezochteFrot = frotten.FindOne(zoekFrot);
          Console.WriteLine(gezochteFrot["tekst"]);
        }

This results in the following:

Capture3

While this may not look spectacular, it could be if I would have created a fancier app. But the simplicity is overwhelming. I didn't need to create tables or schemas or setup primary keys. It just worked like that.

So, I think these NoSQL databases could be a hit for .NET.

I am not saying RMDB's are bad though. I just think Object Databases are a great and simple way to store your objects.

And a very elegant way, at that.


SubSonic will be your friend

Subsonic claims to have a solution when you have better things to do than worry about Data Access. I, for one, am not very fond of the datalayer in my apps. I rather spend my time on the code instead of stored procedures and SQL queries. So, let's go ahead and see if SubSonic lives up to its promise.

Let's open the FrotMachine project from a few posts earlier.
Dowload SubSonic 3.0.0 and add a reference to the SubSonic.Core.dll in the Binaries folder:

reference

Next, create a database. I usually fire up SQL Studio Management Express and create a database by typing:

CREATE DATABASE FrotMachine
GO

Oh well, that's almost too simple to mention.

Anyway, after that I go back to Visual Studio and add a new App.Config item and make sure it looks like this:

appconfig

Let's create an instance of a Frot right now and see if a table and a record in the database are being made. But wait, we need to add the using statement for Subsonic first:

using SubSonic.Repository;

Now we can add to our Main method:

var f = new Frot();
            f.FrotID = 1;
            f.FrotName = "GUI Goodness";
            f.FrotDescription = "Create a nice GUI with whatever tool";

//this is the SubSonic part:
            var repo = new SimpleRepository("FrotMachine", SimpleRepositoryOptions.RunMigrations);
            repo.Add(f);

Let's check out the database now:

table

Isn't that nice.. it worked. SubSonic created a table and a record in my database. Quite good, isn't it? It sure is a lot easier than NHibernate. And twice as cool at that.

This may be a trivial example. I'm sure I've got my point accross though.


Back to Basics: how to build a FrotMachine

Ever wanted to build a Frotmachine? I guess you would.

But, whatever is a Frotmachine?

A Frotmachine is a place where you can store your Frots.
A Frot is something you did to solve a problem quickly.

Well, let's just start and create a prototype.
Let's say that the Frot has a description and a category. So, there would be 2 classes in our program. Frot and Category.

Start Visual Studio (2010 or 2008, you'll need the .NET Framework 3.5) and create a new Project. Choose Console Application.

Then create a new class and make it look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FrotMachine
{
    public class Category
    {
        public int CategoryID { get; set; }
        public string CategoryName { get; set; }
    }
    
    public class Frot
    {
        public int FrotID { get; set; }
        public string FrotName { get; set; }
        public Category FrotCategory { get; set; }
        public string FrotDescription { get; set; }
    }
}

That's very nice, but nothings happens, yet. We should do something with this in the Main method in Program.cs.

So put the following code in the Main method from Program.cs:

Frot f = new Frot
  {
     FrotID = 1,
     FrotDescription = "Runas command",
     FrotCategory = new Category { CategoryName = "Windows" }
  };

   Console.Write("laatst heerlijk gefrot ");
   Console.WriteLine("in de categorie {0} met de {1}",
   f.FrotCategory.CategoryName, f.FrotDescription);

Now, build the program and then press Ctrl+F5 to run it.

This is the result:

laatst heerlijk gefrot in de categorie Windows met de Runas command
Press any key to continue . . .

Next time we'll add the Frot to a database for retrieval.