Feeds:
Bài viết
Phản hồi

Link 1: http://www.dofactory.com/ShortCutKeys/ShortCutKeys.aspx
Link 2: http://msdn.microsoft.com/en-us/library/td6a5x4s.aspx

Silverlight Game

http://www.spritehand.com/silverlight/2.0/BossLaunch/BossLaunch.htm

Looking for a fun software development project? Want to build your own YouTube? Ya me either, but how the heck do you convert videos to flash orSilverlight? It’s not that hard actually as long as you use FFmpeg and some c# code. FFmpeg is a free video and audio converter.  What I like about Silverlight is it will just take the WMV format, and stream it for you.  So if your video is already in WMV, your are done.  If not, you can convert i to WMV, including the HD formats.  In flash, you will have to write some extra code to get it converted.  Check out the HD Halo Trailer in Silverlight by the way.

The first thing you will want to do is download FFmpeg.  There will be a few different files in there.  Make sure you put FFMPEG.exe and FLVTOOL.exe in your application directory.  Next you will want set the correct permissions on your web server.

The code below is fairly basic, but using other FFmpeg commands you can easily create thumbnails from the video, and save them to a location on your web server.  This code for example would create some image thumbnails for you quit easily.

startInfo.Arguments = string.Format(“-i \”{0}\”
 -f image2 -ss 1 -vframes 1 
-s 80×60 -an \ \”{1}\””, srcURL, destURL);

If you are using Silverlight, you can just keep the file in wmv, or convert it to wmv, and just run the command above against the video file to capture the image screenshot.

Anyway here is the sample code for you to try out to convert the video file to flash.  Silverlight is cool because you don’t have to do that and it can just play the wmv in a streaming format automagically.

Sample Code

private void ConvertVideo(string srcURL, string destURL)
{
string ffmpegURL = “~/project/tools/ffmpeg.exe”;
DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName      (Server.MapPath(ffmpegURL)));
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = Server.MapPath(ffmpegURL);
startInfo.Arguments = string.Format(“-i \”{0}\” -acodec mp3 -ab 64k -ac 2       -ar 44100 -f flv -deinterlace -nr 500 -croptop 4 -cropbottom 8 -cropleft 8       -cropright 8 -s 640x480 -aspect 4:3 -r 25 -b 650k -me_range 25 -i_qfactor 0.71       -g 500 \”{1}\”", srcURL, destURL);
startInfo.WorkingDirectory = directoryInfo.FullName;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardError = true;

using (Process process = new Process())
{
process.StartInfo = startInfo;

try
{
process.Start();
StreamReader standardOutput = process.StandardOutput;
StreamWriter standardInput = process.StandardInput;
StreamReader standardError = process.StandardError;
process.WaitForExit();

lblError.Text = standardError.ReadToEnd();
lblOutput.Text = standardOutput.ReadToEnd();
}
catch (Exception ex)
{
Response.Write(ex.ToString());
}
}
}

Ehsan was telling me about the litte, simple and useful control.  All of us already seen that control in facebook chatting window. that textboox automatically bacome bigger according to the text written in the textbox.

Here is the demo of that JQueryhttp://www.aclevercookie.com/demos/autogrow_textarea.html

I have tested that in asp.net TextBox it works. Here is my code in asp.net for auto growing textbox.

You should download JQuery and JQuery Auto Grow to use it.

<script type=”text/javascript” src=”jquery-1.3.2.js”></script>
<script type=”text/javascript” src=”jquery.autogrow.js”></script>
<script type=”text/javascript”>
$(document).ready(function() {
$(’.expanding’).autogrow();
});

<style type=”text/css”>
.expanding {line-height: 18px;}
</style>

<asp:TextBox ID=”myTxt” runat=”server” CssClass=”expanding” AutoPostBack=”false” TextMode=”MultiLine”></asp:TextBox>

In part 1 I showed you how to create the basics for a 3D tagcloud in SilverlightIn this part I’ll show how to get the tags from your html for inserting it into a blog template, we’ll change the color of the tag based on the weight of the tag and let the hyperlink button actually function as a hyperlink (until now it does nothing when you click on it).

There are a few different ways you can pass or get information from the page the Silverlight control is hosted in:

  • Set the initParams parameter in the Silverlight object definition
  • Read the HtmlDocument in your Silverlight code
  • Use JavaScript to call methods in your Silverlight code

You can only use the first option if your hosting your Silverlight application on the same domain. When using the hosting service on silverlight.live.com, no interaction between your application and HTML page is allowed.

For the second option you can use the HtmlPage object in your code to traverse through the HTML DOM, basic methods such as GetElementById are available.

The last option is the one I’ll be usinin this post. To accomplish this you need to follow a few steps:

  • Decorate your page class with a “ScriptableType” attribute
  • Decorate the method you want to call with the “ScriptableMember” attribute and make sure it’s public
  • Register your object in the HtmlPage
  • Give your Silverlight object definition an id, so you can easily reference it from your JavaScript

The reason I’m going with this technique is the flexibility it provides when you want to reuse the tagcloud in other applications. Every blog engine has a different way to generate the HTML for a tagcloud, my blog just shows a list of tags.

 

To change the color based on the weight and set the url of the HyperlinkNutton we need to make some changes to the Tag3D object I showed you in the last post:

public class Tag3D 
    { 
        public HyperlinkButton btnLink { getset; } 
        private TextBlock textBlock { getset; } 
        public Point3D centerPoint { getset; } 
        private Color TagColor { getset; }  

        public Tag3D(double x, double y, double z, string text, string url, Color tagColor) 
        { 
            centerPoint = new Point3D(x, y, z); 
            textBlock = new TextBlock(); 
            textBlock.Text = text; 
            btnLink = new HyperlinkButton(); 
            btnLink.Content = textBlock; 
            btnLink.NavigateUri = new Uri(url); 
            this.TagColor = tagColor; 
        } 
         
        public void Redraw(double xOffset, double yOffset) 
        { 
            double zFactor = ((centerPoint.Z + 300) / 450.0);             
            btnLink.FontSize = 30.0 * zFactor; 
            double alpha = zFactor * 255;             
            //Debug.WriteLine(“Z: {0}; zFactor: {1}; alpha: {2}”, centerPoint.Z, zFactor, alpha); 
            btnLink.Foreground = newSolidColorBrush(Color.FromArgb(Convert.ToByte(alpha), TagColor.R, TagColor.G, TagColor.B));               

            Canvas.SetLeft(btnLink, centerPoint.X + xOffset – (btnLink.ActualWidth /2)); 
            Canvas.SetTop(btnLink, -centerPoint.Y + yOffset – (btnLink.ActualHeight/2)); 
            Canvas.SetZIndex(btnLink, Convert.ToInt32(centerPoint.Z)); 
        } 
    }

 
In the constructor we now pass in the url and a Color to show when the tag is the most important (I chose to use a scale of 1 to 10, with 10 being the most important).
The url is simply assigned to the NavigateUrl property of the HyperlinkButton. The Color is used when setting the new Foreground Brush. I also made some modificationsin the calculations of the font size and alpha of the Brush to make it look a bit more realistic.
 
To let the JavaScript in the page add the tags I’ve created a AddTag method and decorated it with the ScriptableMember attribute:
 
[ScriptableMember()] 
        public void AddTag(string tag, string url, int weight) 
        { 
            if (weight > 10
                weight = 10
             
            Color color = new Color(); 
            color.R = Convert.ToByte(Math.Round(209.0 * ( weight / 10.0))); 
            color.G = Convert.ToByte(Math.Round(18.0 * (weight / 10.0))); 
            color.B = Convert.ToByte(Math.Round(65.0 * (weight / 10.0)));  

            tagBlocks.Add(new Tag3D(0.00.00.0, tag, url, color)); 
        }

In this method we calculate the Color of the tag based on the weight. Then we add a new tag to the tagBlocks List<Tag3D>.

 

After calling this method a couple of times we need to place the tags and display them. I’ve changed the FillTags method shown in the previous post and renamed it to ProcessTags to make the name a bit more meaningful:

[ScriptableMember()] 
        public void ProcessTags() 
        { 
            double radius = RootCanvas.Width / 3
            int max = tagBlocks.Count; 
            double phi = 0
            double theta = 0;             
            for (int i = 1; i < max + 1; i++) 
            { 
                phi = Math.Acos(-1.0 + (2.0 * i – 1.0) / max); 
                theta = Math.Sqrt(max * Math.PI) * phi; 
                                 
                double x = radius * Math.Cos(theta) * Math.Sin(phi); 
                double y = radius * Math.Sin(theta) * Math.Sin(phi); 
                double z = radius * Math.Cos(phi);  

                Tag3D tag = tagBlocks[i –1]; 
                tag.centerPoint = new Point3D(x, y, z); 
                tag.Redraw(RootCanvas.Width / 2, RootCanvas.Height / 2); 
                RootCanvas.Children.Add(tag.btnLink);                 
            } 
        }

 

 

 

 

We need one more thing to make the methods callable from JavaScript. Register the object with the HtmlPage in the constructor:

HtmlPage.RegisterScriptableObject(“TagCloud”this);

 

 

No you can call the methods from JavaScript:

<script type="text/javascript">
            function addTags() {
                var control = document.getElementById("Xaml1");
                control.content.TagCloud.AddTag("Silverlight", "http://silverlight.net", 5);
                control.content.TagCloud.AddTag("Tagcloud", "http://blogs.tamtam.nl", 2);
                control.content.TagCloud.AddTag("Tam Tam", "http://www.tamtam.nl", 10);
                control.content.TagCloud.AddTag("Axelerate3D", "http://www.codeplex.com", 8);
                control.content.TagCloud.AddTag("WPF", "http://www.microsoft.com", 1);
                control.content.TagCloud.AddTag("SharePoint", "http://www.microsoft.com", 4);
                control.content.TagCloud.ProcessTags();
            }
        </script>
        <center>
            <input type="button" value="Add Tags" onclick="addTags()"/>
        </center>

I’m just attaching some code to the onclick of a button and hard-coding the tags. Normally you would handle the onload of the document (or better yet the $(document).ready in jQuery) and get your tags from the Html to pass them to the Silverlight object.

And that wraps it up for this tutorial.

 

Reference Link: http://www.roytanck.com/2008/03/15/wp-cumulus-released/

When I saw the wp-cumulus plugin by Roy Tanck, I thought it would be a great idea to implement the same sort of functionality in Silverlight. It’s hardly original but allows me to learn some parts of the Silverlight framework.

The components behind it are quite simple:

  • Get (or send) the tags from your HTML page to the Silverlight usercontrol
  • Render the tags so it looks 3D
  • Create a method to rotate the tags based on the position of your mouse

 

Choosing a 3D library

The current version of Silverlight doesn’t include 3D functionality like WPF does through the Media3D namespace. Fortunately some developers implemented the same functionality in libraries for Silverlight. The main options I found were Kit3D and Axelerate3D. I decided to use the last one because that one mimics the RotateTransform3D class in WPF 3D the best (it contains a TryTransform method).

Rendering the tags

I decided to tackle the second item first, because if I wasn’t able to manage this, the other items wouldn’t be very useful.

To create a tag in 3D you need some basic functionality:

  • A way to store it’s x, y and z coordinates
  • A hyperlinkbutton to redirect to a page that shows all the items with that tag
  • A textblock to display the tag

 

public class Tag3D 
    { 
        public Tag3D(double x, double y, double z, string text) 
        { 
            centerPoint = new Point3D(x, y, z); 
            textBlock = new TextBlock(); 
            textBlock.Text = text; 
            btnLink = new HyperlinkButton(); 
            btnLink.Content = textBlock;             
        } 
         
        public HyperlinkButton btnLink { getset; } 
        public TextBlock textBlock { getset; } 
        public Point3D centerPoint { getset; } 
     }

 

Then we need a way to make it look like it’s rendered in 3D. We do that by changing the fontsize and the opacity of the text. For that I created a method Redraw:

public void Redraw(double xOffset, double yOffset) 
        { 
            double posZ = centerPoint.Z + 200
            btnLink.FontSize = 10 * (posZ / 100); 
            double alpha = centerPoint.Z + 200
            if (alpha > 255
                alpha = 255
            if (alpha < 0
                alpha = 0
            btnLink.Foreground = newSolidColorBrush(Color.FromArgb(Convert.ToByte(alpha), 000));               

            Canvas.SetLeft(btnLink, centerPoint.X + xOffset – (btnLink.ActualWidth /2)); 
            Canvas.SetTop(btnLink, -centerPoint.Y + yOffset – (btnLink.ActualHeight/2)); 
            Canvas.SetZIndex(btnLink, Convert.ToInt32(centerPoint.Z)); 
        }

 

Placing the tags

To distribute the tags evenly over the sphere, we need some math. Luckily someone was way ahead of me and posted a useful blogentry on this subject (this technique is also used in the wp-cumulus plugin).

The following method creates and places the tags in the canvas:

private void FillTags() 
        { 
            tagBlocks = new List<Tag3D>(); 

            string[] tags = new string[] { “Silverlight”,  
                                            “WPF”,  
                                            “3D”,  
                                            “Rotation”,  
                                            “SharePoint”,  
                                            “.Net”,  
                                            “C#”,  
                                            “Transform”,  
                                            “Blog”,  
                                            “TagCloud”,  
                                            “Tam Tam”,  
                                            “Axelerate3D”,  
                                            “MOSS”,  
                                            “Math”}; 

            double radius = RootCanvas.Width / 3
            int max = tags.Length; 
            double phi = 0
            double theta = 0;             
            for (int i = 1; i < max + 1; i++) 
            { 
                phi = Math.Acos(-1.0 + (2.0 * i – 1.0) / max); 
                theta = Math.Sqrt(max * Math.PI) * phi; 
                                 
                double x = radius * Math.Cos(theta) * Math.Sin(phi); 
                double y = radius * Math.Sin(theta) * Math.Sin(phi); 
                double z = radius * Math.Cos(phi); 

                Tag3D tag = new Tag3D(x, y, z, tags[i –1]); 
                tag.Redraw(RootCanvas.Width / 2, RootCanvas.Height / 2); 
                RootCanvas.Children.Add(tag.btnLink); 
                tagBlocks.Add(tag); 
            } 
        }

 

At the moment the tags to render are hard-coded but we’ll sort that out in part 2.

 

Rotating the tags

To rotate the tags we will use the position of the mouse as a starting point. When the mousepointer is in the center the tagcloud will remain in the current position. Once the mouse is further away from the centerpoint we’ll increase the rotationspeed. The location of the mousepointer compared to the centerpoint will set the angle of the rotation.

 

First we will set the rotation when the tagcloud loads:

 void TagCloud_Loaded(object sender, RoutedEventArgs e) 
        { 
            FillTags(); 

            rotateTransform = new RotateTransform3D(); 
            rotateTransform.Rotation = new AxisAngleRotation3D(new Vector3D(1.0,0.00.0), 0); 

            CompositionTarget.Rendering += newEventHandler(CompositionTarget_Rendering); 
            LayoutRoot.MouseEnter += new MouseEventHandler(LayoutRoot_MouseEnter); 
            LayoutRoot.MouseLeave += new MouseEventHandler(LayoutRoot_MouseLeave); 

        }

 

 

Here we set the rotation angle to 0 and the rotationaxis to the x-axis. When the mouse moves, we’ll change those parameters, so the rotation will have an effect:

void LayoutRoot_MouseMove(object sender, MouseEventArgs e) 
        { 
            Point mouseLocation = e.GetPosition(RootCanvas); 
            double relativeX = mouseLocation.X – (RootCanvas.ActualWidth / 2); 
            double relativeY = mouseLocation.Y – (RootCanvas.ActualHeight / 2); 

            MouseX.Text = relativeX.ToString(); 
            MouseY.Text = relativeY.ToString(); 

            double speed = Math.Sqrt(Math.Pow(relativeX, 2) + Math.Pow(relativeY,2)) / 170

            RotationSpeed.Text = speed.ToString(); 

            rotateTransform.Rotation = new AxisAngleRotation3D(newVector3D(relativeY, relativeX, 0), speed); 
        }

 

Now that the rotationparameters are set we need to rotate the tags, or more precisely the centerpoint of the tag. To accomplish this we’ll make use of the Rendering event of the CompositionTarget object. This is called everytime the Silverlight plugin wants to render a new frame.

void CompositionTarget_Rendering(object sender, EventArgs e) 
        {  
            if (((AxisAngleRotation3D)rotateTransform.Rotation).Angle > 0.05)  
                RotateBlocks(); 
        } 

        private void RotateBlocks() 
        { 
            foreach (Tag3D textBlock in tagBlocks) 
            { 
                Point3D newPoint; 
                if (rotateTransform.TryTransform(textBlock.centerPoint, outnewPoint)) 
                { 
                    textBlock.centerPoint = newPoint; 
                    textBlock.Redraw(RootCanvas.ActualWidth / 2, RootCanvas.ActualHeight / 2); 
                } 
            } 
        }

 

To relieve the CPU a bit, we’ll only rotate the tags if the rotation angle is higher than a threshold value. The actual transformation is accomplished by invoking the TryTransform method and passing it the current centerpoint of each tag.

 

 

At the moment the Silverlight control looks like this:

 

 

In the next part I’ll show you a way to dynamically set the tags, base their fontsize on the actual weight of the tag and actually use the hyperlink button.

I’ll try to dive deep into the caching of NHibernate in this article. This post has been inspired by the talk given by Oren Eini (aka Ayende) at the Kaizen Conference in Austin TX.

Caching is a topic that is IMHO only superficially described so far especially regarding the second level cache. Most of the time one finds a lot of information about how to configure a specific cache provider for usage but the real usage (who and when) is not really described. I hope to be able to provide some of the missing pieces with this post.

The full source code used for this post can be found here.

First Level Cache

When using NHibernate the first level cache is automatically enabled as long as one uses the standard session object. We can avoid to use a cache at all when using the stateless session provided by NHibernate though. The stateless session is especially useful for reporting situations or for batch processing. When NHibernate is loading an entity by its unique id from the database then it is automatically put into the so called identity map. This identity map represents the first level cache. 
The life-time of the first level cache is coupled to the current session. As soon as the current session is closed the content of the respective first level cache is cleared. Once an entity is in the first level cache a subsequent operation that wants to load the very same entity inside the current session retrieves this entity from the cache and no roundtrip to the database is needed. 
One of the main reasons behind this entity map is to avoid the situation that two different instances in memory can represent the same database record (or entity). The NHibernate session object provides us two ways to retrieve an entity by its unique id from the database. There are subtle but important differences between them.

Let’s implement an Account class for our samples

public class Account
{
    public virtual int Id { get; private set; }
    public virtual string Name { get; private set; }
    public virtual decimal Balance { get; private set; }
 
    protected Account()
    {
    }
 
    public Account(string name, decimal balance)
    {
        Name = name;
        Balance = balance;
    }
 
    public virtual void Credit(decimal amount)
    {
        Balance += amount;
    }
}

the corresponding XML mapping file is

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="Caching"
                   assembly="Caching">
  <class name="Account">
    <id name="Id">
      <generator class="hilo"/>
    </id>
    <property name="Name"/>
    <property name="Balance"/>
  </class>
</hibernate-mapping>

Get an entity from database

With the session.Get(id) method we can retrieve an entity from database. If there is no record found in the database with the given id then null is returned. 
On the other hand if a record with the given unique id exists in the database then NHibernate loads this record and instantiates a fully populated entity in memory and immediately puts this entity into the entity map (or first level cache) 
Assuming that a specific record has already been loaded from database inside the current session then a subsequent Get(id) operation will return the cached entity to the caller. We can see this in the following output produced by the unit test. There is only ONE select statement produced by NHibernate

FistLevelCache

The above output was produced by this unit test

[Test]
public void trying_to_get_the_same_account_a_second_time_should_get_the_account_from_1st_level_cache()
{
    Console.WriteLine("------ now getting entity for the first time");
    var acc1 = Session.Get<Account>(account.Id);
    Console.WriteLine("------ now getting entity for the second time");
    var acc2 = Session.Get<Account>(account.Id);
 
    acc1.ShouldBeTheSameAs(acc2);
}

Load an entity from database

When using the session.Load(id) method NHibernate only instantiates a proxy for the given entity. As long as we only access the id of the entity the entity itself is not loaded from the database. Only when we try to access one of the other properties of the entity NHibernate loads the entity from the database. We can see this clearly in the following output produced by the unit test. I have added some comment to the output to make it easier to verify the result.

Here is a unit test

[Test]
public void trying_to_load_a_non_existing_entity()
{
    var acc1 = Session.Load<Account>(account.Id);
    acc1.ShouldNotBeNull();
    Console.WriteLine("------ now accessing the id of the entity");
    Console.WriteLine("The id is equal to {0}", acc1.Id);
    acc1.Id.ShouldEqual(account.Id);
    Console.WriteLine("------ now accessing a property (other than the ID) of the entity");
    Console.WriteLine("The name of the account is: {0}", acc1.Name);
    acc1.Name.ShouldEqual(account.Name);
}

and the output produced by the above code

FistLevelCache2

Using Load to optimize data access

The behavior mentioned above is especially useful when creating or updating complex entities which have relations to other entities. Assume that the account entity references a customer entity and I want to create a new account for a customer from which I only know its unique id. Then my code might look as follows

var newAccount = new Account("EUR Account 1", 1250m);
newAccount.Customer = Session.Load<Customer>(customerId);
Session.Save(newAccount);

Note that I use the Load method to get the (existing) customer entity. NHibernate will not physically load the customer since in the account table on the database there is only the id of the customer needed (as a foreign key). And as I mentioned above, as long as you only use the id of an entity retrieved with the Load method the corresponding entity is not physically loaded from the database.

Second level cache

The life time of the second level cache is tied to the session factory and not to an individual session. Once an entity is loaded by its unique id and the second level cache is active the entity is available for all other sessions (of the same session factory). Thus once the entity is in the second level cache NHibernate won’t load the entity from the database until it is removed from the cache. 
To enable the second level cache we have to adjust our configuration file. We have to define which cache provider we want to use. There exist various implementations of a second level cache. For our sample we use a Hashtable based cache which is included in the core NHibernate assembly. Please note that you should never use this cache provider for production code but only for testing. Please refer to the chapter “Second Level Cache implementations” below to decide which implementation fits best for your needs. You won’t have to change your code if you change the cache provider though.

We have to add the the following line to the configuration file

    <property name=”cache.provider_class”>NHibernate.Cache.HashtableCacheProvider</property>

this will instruct NHibernate to use the previously mentioned Hashtable based cache provider as a provider for the second level cache. 
Now let’s have a look at the following unit test.

[Test]
public void trying_to_load_an_existing_item_twice_in_different_sessions_should_use_2nd_level_cache()
{
    using(var session = SessionFactory.OpenSession())
    {
        var acc = session.Get<Account>(account.Id);
        acc.ShouldNotBeNull();
    }
 
    using(var session = SessionFactory.OpenSession())
    {
        var acc = session.Get<Account>(account.Id);
        acc.ShouldNotBeNull();
    }
}

In the above test we open a first session and load an existing entity from the database. Then we open a second session and try to load the very same entity from the database again. Without a second level cache we would expect that NHibernate loads the entity two times from the database since we are using 2 different sessions and thus the first level cache can not be used to avoid a roundtrip to the database. So let’s have a look at the result produced.

SecondLevelCache

Wait a moment – we clearly see two select statements instead of only one. What did we do wrong? This is not an error, no but it’s a feature. NHibernate does not enable the second level cache by default, since it would have too many undesired implications. One has to explicitly enable the second level cache. If we add the following statement to the configuration file

    <property name=”cache.use_second_level_cache”>true</property>

we activate the second level cache. But that is still not enough. We have to also enable our entity to be cached in the second level cache. 
This can be done by adding the following statement to the entity’s mapping file

    <cache usage=”read-write”/>

If we now run the unit test again we obtain the expected result. The entity is loaded only once from the database. The second time it is loaded from the second level cache. 
Now if we try to update an entity which is already in the second level cache then this entity should also be automatically updated in the second level cache. The following unit test should prove this behavior. 

[Test]
public void when_updating_the_entity_then_2nd_level_cache_should_also_be_updated()
{
    using(var session = SessionFactory.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        var acc = session.Get<Account>(account.Id);
        acc.Credit(200m);
        tx.Commit();
    }
 
    using(var session = SessionFactory.OpenSession())
    {
        var acc = session.Get<Account>(account.Id);
        acc.Balance.ShouldEqual(1200m);
    }
}

and indeed it does as we can see in the test output. Again the entity is loaded from the cache the second time it is requested although it was updated (no select statement after the update statement). The last line in the test code verifies that the entity was indeed updated. 
SecondLevelCache2 

Second Level Cache Providers

All second level cache providers are part of the NHibernate contribution. The following list gives as short description of each provider.

  • Velocity: uses Microsoft Velocity which is a highly scalable in-memory application cache for all kinds of data.
  • Prevalence: uses Bamboo.Prevalence as the cache provider. Bamboo.Prevalence is a .NET implementation of the object prevalence concept brought to life by Klaus Wuestefeld in Prevayler. Bamboo.Prevalence provides transparent object persistence to deterministic systems targeting the CLR. It offers persistent caching for smart client applications.
  • SysCache: Uses System.Web.Caching.Cache as the cache provider. This means that you can rely on ASP.NET caching feature to understand how it works.
  • SysCache2: Similar to NHibernate.Caches.SysCache, uses ASP.NET cache. This provider also supports SQL dependency-based expiration, meaning that it is possible to configure certain cache regions to automatically expire when the relevant data in the database changes.
  • MemCache: uses memcachedmemcached is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load. Basically a distributed hash table.
  • SharedCache: high-performance, distributed and replicated memory object caching system. See here and here for more info

Saving a transient entity

Lately the following question came up in the NHibernate user list. “When I create a transient object and then save it to my session and 
commit to my database, should it be added to my second level cache as well?” The answer is of course YES. Let’s write a unit test

[TestFixture]
public class when_saving_a_transient_account : FixtureBase
{
    private Account newAccount;
 
    protected override void Context()
    {
        base.Context();
        using (var session = SessionFactory.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            newAccount = new Account("CHF Account", 5500m);
            session.Save(newAccount);
            tx.Commit();
        }
    }
 
    [Test]
    public void account_should_be_in_second_level_cache()
    {
        using (var session = SessionFactory.OpenSession())
        {
            Console.WriteLine("--> Now loading account");
            var acc = session.Get<Account>(newAccount.Id);
            acc.ShouldNotBeNull();
            acc.Name.ShouldEqual(newAccount.Name);
        }
    }
}

In the method Context I create and save a new account entity. In the test method I open a new session and try to load the previously created entity from database. I now expect that NHibernate should take the entity out of the second level cache. And indeed it does. This is the resulting output

SecondLevelCache3

as we can see, no select statement is sent to the database when the entity is loaded from a new session after it has been created and saved beforehand.

Inside the second level cache

An important point is that the second-level cache does not cache instances of the object type being cached; instead it caches the individual values of the properties of that object. This provides two benefits. One, NHibernate doesn’t have to worry that your client code will manipulate the objects in a way that will disrupt the cache. Two, the relationships and associations do not become stale, and are easy to keep up-to-date because they are simply identifiers. The cache is not a tree of objects but rather a map of arrays.

If you are interested in some more details about the inner workings of the second level cache then the following text (taken from Ayende and only slightly edited by me) will be of interest to you:

NHibernate is design as an enterprise OR/M product, and as such, it has very good support for running in web farms scenarios. This support include running along side with distributed caches, including immediate farm wide updates.  NHibernate goes to great lengths to ensure cache consistency in these scenarios…

The way it works, NHibernate keeps three caches.

  • The entities cache – the entity data is disassembled and then put in the cache, ready to be assembled to entities again.
  • The queries cache – the identifiers of entities returned from queries, but no the data itself (since this is in the entities cache).
  • The update timestamp cache – the last time a table was written to.

The last cache is very important, since it ensures that the cache will not serve stale results.

Now, when we come to actually using the cache, we have the following semantics.

  • Each session is associated with a timestamp on creation.
  • Every time we put query results in the cache, the timestamp of the executing session is recorded.
  • The timestamp cache is updated whenever a table is written to, but in a tricky sort of way:
    • When we perform the actual writing, we write a value that is somewhere in the future to the cache. So all queries that hit the cache now will not find it, and then hit the DB to get the new data. Since we are in the middle of transaction, they would wait until we finish the transaction. If we are using low isolation level, and another thread / machine attempts to put the old results back in the cache, it wouldn’t hold, because the update timestamp is into the future.
    • When we perform the commit on the transaction, we update the timestamp cache with the current value.

Now, let us think about the meaning of this, shall we?

If a session has perform an update to a table, committed the transaction and then executed a cache query, it is not valid for the cache. That is because the timestamp written to the update cache is the transaction commit timestamp, while the query timestamp is thesession’s timestamp, which obviously comes earlier.

The update timestamp cache is not updated until you commit the transaction! This is to ensure that you will not read “uncommitted values” from the cache.

Please note that if you open a session with your own connection, it will not be able to put anything in the cache (all its cached queries will have an invalid timestamp!)

In general, those are not things that you need to concern yourself with, but I spent some time today just trying to get tests for the second level caching working, and it took me time to realize that in the tests I didn’t used transactions and I used the same session for querying as for performing the updates.

Collections and the second level cache

Let’s assume the following scenario: we have a blog which can have many posts. See the diagram below. 
Blog diagram 
The corresponding code to define the entities is as follows

public class Blog
{
    public virtual int Id { get; set; }
    public virtual string Author { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Post> Posts { get; set; }
 
    public Blog()
    {
        Posts = new List<Post>();
    }
 
}
 
public class Post
{
    public virtual int Id { get; private set; }
    public virtual string Title { get; set; }
    public virtual string Body { get; set; }
}

and we can define the mapping of the blog and the post entity like this

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="Caching"
                   assembly="Caching">
  <class name="Blog">
    <cache usage="read-write"/>
    <id name="Id">
      <generator class="hilo"/>
    </id>
    <property name="Author"/>
    <property name="Name"/>
    <bag name="Posts" cascade="all" lazy="true">
      <cache usage="read-write"/>
      <key column="BlogId"/>
      <one-to-many class="Post"/>
    </bag>
  </class>

  <class name="Post">
    <cache usage="read-write"/>
    <id name="Id">
      <generator class="hilo"/>
    </id>
    <property name="Title"/>
    <property name="Body"/>
  </class>
</hibernate-mapping>

Note that I have added a <cache> element to the mapping for the Blog entity. This is enough to cache all simple Blog property values (e.g. Id, Name and Author) but not the state of associated entities or collections. Collections require their own <cache> element. In our case I added a <cache> element to the Posts collection (which is mapped as a bag). This cache will be used when enumeration the collectionblog.Posts, for example. Please be aware that a collection cache only holds theidentifiers of the associated post instances. That is, if we have a blog with three posts having id’s 1,2 and 3 respectively then the second level cache will contain the values of simple properties of the blog and in addition an array with the ids {1,2,3}. If we require the post instances themselves to be cached, then we must enable caching of the Post class by adding a <cache> element to it’s mapping.

Let me resume: by adding a <cache> element to the Blog, the Posts collection and the Post itself I have declared that I want NHibernate to cache not only my blog entities but also the associated Post collection in full detail.

Attention: dragons ahead

A common error (It happened to me as well!) is to forget to commit or omit a transaction when adding or changing an entity/aggregate to the database. If we now access the entity/aggregate from another session then the 2nd level cache will not be prepared to provide us the cached instances and NHibernate makes an (unexpected round trip to the database). The reason why this is the case is described in the chapter “Inside the second level cache” above.

What does this mean? Let’s have a look at the code I use to setup the context for our unit tests regarding the Blog–>Posts problem.

blog = new Blog{ Author = "Gabriel", Name = "Keep on running"};
blog.Posts.Add(new Post{Title = "First post", Body = "Some text"});
blog.Posts.Add(new Post { Title = "Second post", Body = "Some other text" });
blog.Posts.Add(new Post { Title = "Third post", Body = "Third post text" });
using (var session = SessionFactory.OpenSession())
using(var tx = session.BeginTransaction())
{
    session.Save(blog);
    tx.Commit();        // important otherwise caching does NOT work!
}

In the above code I create a new blog having three assigned posts. The blog instance is then saved to the database inside a transaction. If I would omit the transaction or if I would forget to commit the transaction then the above samples would not work as expected and the 2nd level cache would not be used as desired.

Caching queries in the second level cache

We cannot only cache entities loaded by their respective unique id but also any query. For this we have to define the query as cacheable and set the desired cache mode. Let’s have a look at a typical sample

[Test]
public void trying_to_cache_a_query()
{
    using (var session = SessionFactory.OpenSession())
    {
        Console.WriteLine("---> using query first time");
        var query = session
            .CreateQuery("from Blog b where b.Author = :author")
            .SetString("author", "Gabriel")
            .SetCacheable(true);
        var list = query.List<Blog>();
    }
    using (var session = SessionFactory.OpenSession())
    {
        Console.WriteLine("---> using query second time");
        var query2 = session
            .CreateQuery("from Blog b where b.Author = :author")
            .SetString("author", "Gabriel")
            .SetCacheable(true);
        var list2 = query2.List<Blog>();
    }
}

In the above sample I use the same query from different sessions. Please not that I have set cacheable to true for the query. In this case the query will be cached in the second level cache the first time it is executed. Any subsequent calls using the very same query will not hit the database. It is important to note however that if I change the value of the parameter(s) in the query then the query is reloaded again from the database. So it is the query and the set of parameter values that define the key under which the query is stored in the 2nd level cache.

The result of the above test looks as follows

 CachQuery1 

Of course I can also use named queries and cache them. A named query is defined inside a mapping file, e.g.

<query cacheable="true" cache-mode="normal" name="query1">
  <![CDATA[from Blog b where b.Name like :name]]>
  <query-param name="name" type="String"/>
</query>

The above query is called “query1” and has a single parameter called “name”. The cache mode for this query is set to “normal”. I can use such a query as follows

[Test]
public void trying_named_query()
{
    using (var session = SessionFactory.OpenSession())
    {
        Console.WriteLine("---> using named query first time");
        var list = session.GetNamedQuery("query1")
            .SetString("name", "Keep%")
            .List<Blog>();
    }
    using (var session = SessionFactory.OpenSession())
    {
        Console.WriteLine("---> using named query second time");
        var list2 = session.GetNamedQuery("query1")
            .SetString("name", "Keep%")
            .List<Blog>();
    }
}

The session object has a method GetNamedQuery to retrieve the query. The output produced by the above test is then

CachQuery2
If the content of the table on which the cached query is based is changed then the query is evicted from the second level cache and the next time the query is executed the query must be reloaded.

Cache Regions

If we don’t use cache regions the second level cache can only be cleared as a whole. If you need to clear only part of the second level cache then use regions. Regions are distinguished by their name. One can put any number of different queries into a named cache region. The command to clear a cache region is as follows 
    SessionFactory.EvictQueries(“My Region”); 
where SessionFactory is the session factory instance currently used and “My Region” is the name of the cache region.

Source Code

The full source code used for this post can be found here.

Summary

NHibernate provides two types of caches. The first level cache and the second level cache. The first level cache is also called the identity map and is used not only to reduce the number of round trips to the database to improve the speed of an application but also to guarantee that there do not exist two distinct instances of an object having the very same id. NHibernate provides us two methods to load an entity by its unique id from the database. The Get method returns null if an entity with the given id does not exist or returns the fully loaded entity to the caller. The Load method on the other hand returns a proxy to the caller and only loads the entity from the database if another property than the identity is accessed. One can also call this adeferred load.

The second level cache is not used by default and should be used with caution. It can provide a huge scalability gain if used wisely but also reduce the performance of the overall system and introduce unnecessary complexity if used wrong.

The second level cache is related to the session factory, that is all session instances of a given session factory use the same 2nd level cache. That’s differs from the behavior of the 1st level cache which is related to an individual session instance. One can cache individual entities or whole aggregates in the 2nd level cache. But one can also cache (complex and/or time consuming) queries in the 2nd level cache. The 2nd level cache can be fragmented into regions for a more fine grained control.

Theo dõi

Get every new post delivered to your Inbox.