Feeds:
Bài viết
Bình luận

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.

.NET Framework v2.0 SP2, which will be included with .NET Framework v3.5 SP1, is scheduled to release this summer.  This release contains a long awaited ASP.NET Cache feature, which will notify you when an entry becomes invalid and leave the stale entry in the cache while you generate a replacement in the background.  Therefore, requests are not blocked attempting to access the cache entry while it is being updated, and the update is done by a single thread.  Here’s the specification:

 

 

A new Cache.Insert overload:

 

void Cache.Insert( String key,

                   Object expensiveObject,

                   CacheDependency dependencies,

                   DateTime absoluteExpiration,

                   TimeSpan slidingExpiration,

                   CacheItemUpdateCallback onUpdateCallback

                 );

 

This method inserts an object into the cache, with optional dependencies and expiration policy, and a callback that is invoked before the entry is removed.  Objects inserted into the cache with this method should be expensive to generate.  You should not use this method for objects that are inexpensive to generate, because there is overhead in the implementation.  When the callback is invoked, the application developer should generate a new object, with new dependencies and expiration policy, and return it via the callback’s out parameters.  The stale cache entry will be accessible while the callback is executing and the replacement object is generated.

 

Cache entries inserted by this method are not subject to eviction by the cache memory manager.  These objects only become invalid when they expire or a dependency changes, and therefore these objects MUST have some form of invalidation (i.e., it is not possible to insert the entry with a null dependency, NoAbsoluteExpiration, and NoSlidingExpriation).

 

 

A new update callback:

 

void CacheItemUpdateCallback( String key,

                              CacheItemUpdateReason reason,
                              out Object expensiveObject,

                              out CacheDependency dependencies,

                              out DateTime absoluteExpiration,

                              out TimeSpan slidingExpiration

                            );

 

When the cache entry becomes invalid, ASP.NET invokes this callback, allowing the application developer to generate a replacement cache entry.  The developer is passed only two pieces of information:  the key for the cache entry, and an enum value indicating why the entry is invalid.  The rest of the arguments to the callback are output-only parameters, allowing references to the newly generated object, dependencies, and expiration policy to be passed back and subsequently inserted into the cache by ASP.NET.

 

Do not modify the existing cache entry.  The stale cache entry is still alive and potentially being used by other threads.  It is not included in the callback arguments in order to minimize the likelihood of a developer accidentally updating portions of the cache entry.  Although this pitfall exists today in the cache API (e.g. a cache entry could be modified in a non-thread-safe manner by an ASP.NET developer), we think leaving the cached value out of the callback arguments will reduce the likelihood of a developer stumbling into this.

 

To remove the cache entry, simply set the argument expensiveObject to null.  Otherwise, set it to the newly generated replacement object.  The developer MUST also specify expiration and/or dependency information using the other out parameters.  As stated above, the cache entry is not subject to eviction by the cache memory manager, and therefore must have some form of invalidation.  It is an error to pass back a non-null value for expensiveObject while setting dependencies to null,absoluteExpiration to NoAbsolteExpiration, and slidingExpriation to NoSlidingExpiration.  The cache entry will be removed if this occurs.

 

If the developer’s callback throws an exception, ASP.NET will suppress the exception and remove the cache entry.  The exception needs to be suppressed because cache item updates occur in the background where there is no call path for routing the exception back to user code.  The cache entry is removed because without a new cache value, ASP.NET has no way of knowing what the correct behavior is for updating the cached item.

 

The update callback will not be called if the cached item was explicitly removed via a call to Cache.Remove, or a call to Cache.Insert (Insert removes a pre-existing entry).  These operations indicate the developer’s intent to remove the item, so the update callback will not be triggered.

 

 

A new update reason enumeration:

 

public enum CacheItemUpdateReason {

    // The item was removed from the cache because either

    // the absolute or sliding expiration interval expired.

 

    Expired = 1,

 

    // The item was removed from the cache because an

    // associated cache dependency was changed.

 

    DependencyChanged = 2

}

 

Unlike the CacheItemRemovedReason enumeration, the update reason enumeration does not include values for Removed (see explanation above) or Underused (as mentioned above, the cache entry is not subject to eviction by the cache memory manager).

 

http://my.opera.com/Tatchan/blog/show.dml/261281

http://www.lamchame.com/forum/showthread.php?t=7723

http://mp3.zing.vn/mp3/search/do.html?t=4&q=Con+Mãi+Tuổi+Lên+Ba

Không phải đến khi trẻ lên 4-5 tuổi cha mẹ mới bắt đầu dạy chúng tập đọc, đơn giản là vì đã quá trễ”, tiến sĩ Robert C. Titzer, một chuyên gia về giáo dục trẻ em ở Mỹ, lập luận. Phương pháp dạy trẻ đọc sớm của ông đang được xem là một đột phá. Phương pháp này được giảng dạy qua bộ đĩa giáo dục “Bé yêu biết đọc”.

Your baby can read - Dạy bé yêu biết đọc - Bộ đĩa Bé yêu biết đọc

Bộ đĩa Bé yêu biết đọc phiên bản tiếng Việt bao gồm 2 DVD.

Trong những năm đầu đời, não bộ trẻ phát triển rất nhanh và có những tiềm năng rất lớn. Trong giai đoạn này, khi các giác quan thính giác, thị giác, xúc giác được kích thích đúng phương pháp, trẻ sẽ phát triển tốt nhất tiềm năng sẵn có.

Với mục đích cập nhật những thông tin về giáo dục mới đến với phụ huynh, Học viện IQ, được sự phối hợp của Viện Nghiên cứu Giáo dục và nhãn hàng Gainplus Eye Q (Abbott) tài trợ giới thiệu một phương pháp học tập đột phá trong chương trình “Khơi dậy tốt nhất tiềm năng trí tuệ của trẻ”

Những bằng chứng khoa học cho thấy rằng tập đọc ngay từ giai đoạn thơ ấu sẽ dễ dàng hơn nhiều so với bất kỳ giai đoạn nào khác. Điều kỳ diệu của tập đọc là nó liên quan trực tiếp đến những thành công trong tương lai của trẻ. GS – TS Robert C. Titzer, giáo sư hàng đầu của Mỹ trong nghiên cứu về đề tài học đọc của trẻ nhỏ, lần đầu tiên trên thế giới đã sáng tạo ra bộ băng hình tập đọc dành cho trẻ từ 0 – 3 tuổi. Bộ băng hình chứa đựng những thông tin về việc bé của bạn có thể bắt đầu tập đọc khi chỉ mới 6 tháng tuổi. Việc học đọc này sử dụng phương pháp kích thích đa giác quan giúp bé tiếp cận với ngôn ngữ, từ đó phát triển trí não.

Trong những năm đầu đời, não bộ trẻ phát triển rất nhanh và có những tiềm năng rất lớn. Trong giai đoạn này, khi các giác quan thính giác, thị giác, xúc giác được kích thích đúng phương pháp, trẻ sẽ phát triển tốt nhất tiềm năng sẵn có. Tiềm năng của não bộ trẻ là cơ sở của các chiến lược giáo dục sớm, trong đó có việc học ngôn ngữ sớm. . ..

Theo GS – TS Robert C. Titzer, khi con bạn tỏ ra hứng thú ở một chủ đề đặc biệt nào đó, hãy giúp bé học đề tài đó bằng nhiều giác quan mà bạn có thể. Nếu con bạn thích học về hoa – hãy để bé xem, ngửi, chạm vào, thậm chí là lắng nghe những âm thanh dịu dàng mà bông hoa phát ra mỗi khi chạm vào tai bé. Phương pháp học này thường gây hứng thú nhiều hơn đối với trẻ, tạo hiệu quả nhiều hơn vì các kênh thần kinh được hình thành giữa các vùng khác nhau của não sẽ nhận được những thông tin đa cảm giác. Hãy cố gắng để kết hợp chặt chẽ phương pháp học đa giác quan cho con bạn bất cứ khi nào bạn có thể, thậm chí khi con bạn đang học những điều căn bản. Chẳng hạn như việc xem băng hình có sử dụng phương pháp học bằng đa giác quan này sẽ tạo cho trẻ cơ hội được xem từ, nghe từ, biểu diễn những động tác cơ thể liên quan đến các từ, xem và nghe những bức tranh trình bày nghĩa của những từ này…

Để tối ưu hóa sự phát triển toàn diện của trẻ, dinh dưỡng đóng vai trò rất quan trọng trong việc hoàn thiện trí tuệ và thể chất trong những năm đầu đời, là nền tảng để bé yêu luôn vượt lên phía trước.

Học viện IQ hiểu rằng, giáo dục đúng phương pháp cùng với dinh dưỡng khoa học sẽ là chìa khóa mang đến sự thành công của trẻ trong tương lai. Thông qua các hoạt động trong chuỗi sự kiện “Khơi dậy tốt nhất tiềm năng trí tuệ của trẻ” được tổ chức tại 7 thành phố lớn trên cả nước trong tháng 8 và tháng 9, Học viện IQ sẽ cập nhật cho cha mẹ phương pháp giáo dục đột phá giúp bé yêu phát triển tốt nhất tiềm năng trí tuệ của mình.

Nổi bật nhất trong chương trình của sự kiện này là buổi nói chuyện của GS – TS Robert Titzer, giáo sư hàng đầu của Mỹ trong nghiên cứu về đề tài học đọc của trẻ nhỏ, về phương pháp học đọc bằng đa giác quan dành cho trẻ từ 0 – 3 tuổi. Phụ huynh sẽ có cơ hội trực tiếp lắng nghe và tham vấn với GS – TS Robert Titzer về phương pháp học đọc này. Bộ đĩa Bé yêu biết đọc – Your baby can read Chào đón các bạn đến với một cách toàn bộ học mới. Dựa vào nghiên cứu của nhà khoa họcRobert C.Titzer, Ph.D., Your Baby Can Read! (Đứa Trẻ Của Các Bạn Có Thể Đọc! hay dạy con biết đọc hay Bé yêu biết đọc) là loại video đầu tiên trên thế giới được thiết kế để giúp đỡ những đứa trẻ, những đứa trẻ chập chững biết đi và trẻ con trước tuổi dô học để học đọc! Giành được sự chú ý của tin tức và phương tiện truyền thông khác, cũng như hàng nghìn những đứa trẻ ở Mỹ, nhà khoa học Robert C. Titzer sáng tạo ra một cách sử dụng nhiều cảm giác khác nhau để học đọc, và dạy những dứa trẻ em để đoán nhận từ sử dụng vài cảm giác một lúc.

Vào năm 1998 một cuộc hội thảo quốc gia của những chuyên gia về ngôn ngữ và nhà giáo dục chuyên về sự học của trẻ em trình diễn hai khuyến cáo chính để tiến bộ việc đọc của trẻ em trong Nước Mỹ:

1) Dạy đứa trẻ để đọc sớm.

2) Đừng tập trung vào ngôn ngữ hoặc âm học riêng rẽ, nhưng phải kết hợp hai phần.

Những video của chúng tôi đang làm điều này từ năm 1997. Đứa trẻ của các bạn sẽ nhìn thấy từ, nghe thấy từ, nhìn thấy những ảnh đại diện cho ý nghĩa của từ, và sẽ thường thực hiện hoạt động vật lý nào đó liên quan đến từ. Theo những lý thuyết trên sự phát triển trí óc, cách sử dụng nhiều cảm giác khác nhau để học đọc của nhà khoa học Robert C. Titzer có thể giúp đỡ thành lập khớp thần kinh mới trong sự thị giác, sự thính giác, và những vùng cảm giác thân thể khác của não.

Hiểu biết những kết nối phức tạp này có thể cung cấp một nền tảng tốt hơn cho giáo dục tương lai và cũng khuyến khích đến con cái với những phong cách học về sự thị giác, thính giác, và những vùng cảm giác thân thể khác nhau. Những video của chúng tôi rất khác với những video trẻ khác! Đa số những viđêô đứa trẻ được thiết kế như trò giải trí của trẻ em với những hình ảnh và những âm thanh vui thú. DVDs của chúng tôi tương tác, sử dụng nhiều cảm giác khác nhau và sẽ giúp đỡ dạy ngôn ngữ cho trẻ em trong thời gian quan trọng nhất để học ngôn ngữ. Những viđêô của chúng tôi được thiết kế bởi một nhà nghiên cứu trẻ em để đặc biệt dạy ngôn ngữ một cách vui đùa, cách tương tác sử dụng đứa trẻ, những thú vật, những bài hát, và những bài thơ.

Mặc dù những tên của nhiều viđêô trẻ em khác, những sản phẩm đó được thiết kế với mục đích để chiếm thời gian và là trò giải trí của đứa trẻ, không phải để giúp đỡ họ học bất cứ cái gì của giá trị. Nhiều cha mẹ đã nói với chúng tôi rằng con cái của họ học nhiều hơn sau khi xem viđêô của chúng tôi một lần so với xem viđêô trẻ em khác trong thời gian dài vài tháng. Phần tốt nhất là những viđêô của chúng tôi cũng vui thú và là trò giải trí, và có nhiều giá trị trong giáo dục trẻ em. Sớm hơn là tốt hơn . .

Bằng chứng khoa học mới góp ý rằng việc học ngôn ngữ thật sự dễ dàng hơn trong thời gian tuổi thơ ấu so với thời gian khác. Tất cả mọi sự giáo dục của ngôn ngữ được học sớm trong cuộc sống sẽ được tự nhiên và dễ dàng hơn. Những dứa trẻ tự nhiên học và hiểu nói từ ngữ bằng việc nghevà thấy ngôn ngữ. Những đứa trẻ được học sớm ngôn ngữ thu hai hoac la ngôn ngữ ước hiệu sẽ tự nhiên đạt tới được một mức bậc cao so với người lớn tuổi hơn. Những đứa trẻ em mà được cơ hội để nhìn thấy ngôn ngữ cũng có thể học hiểu ngôn ngữ viết tự nhiên và dễ dàng hơn. Ngôn ngữ mà chúng ta đợi cho lúc lớn tuổi sẽ tự động trở thành rất khó khăn để học. DVDs của chúng tôi được tạo ra bởi một ông nhà nghiên cứu mà đã thành công áp dụng kỹ thuật này với con cái của ông ấy. Những nghiên cứu tương quan trẻ em cho biết la trẻ em mà được dạy đọc trước khi năm tuổi đọc tốt hơn nhũng trẻ em khác mà có cùng điểm IQ và trung hợp tình trạng kinh tế xã hội mà đã được dạy trễ hơn. Trẻ em mà dạy để đọc lúc bảy hay tám tuổi còn chậm hơn trẻ em dạy sớm (sau khi kiểm tra cho điểm IQ và tình trạng kinh tế xã hội). Sau tám tuổi, trẻ em mà dạy để đọc tại những tuổi trẻ hơn vẫn đọc nhanh hơn những đứa trẻ em cùng điểm IQ và tình trạng kinh tế xã hội mà được dạy đọc trễ hơn. Bộ đĩa Bé yêu biết đọc

Bộ đĩa này đã được giới thiệu rộng rãi trên hơn 2.000 chương trình truyền hình nổi tiếng thế giới như CNN, Good Morning America, The Discovery Channel…

Link reference: http://choicungbe.com/