sharing about .NET and technology RSS 2.0
 Wednesday, April 05, 2006

I registered a new domain for my weblog. The new domain is www.delarou.net. Please update your rss feeds to http://www.delarou.net/weblog/SyndicationService.asmx/GetRss.

Wednesday, April 05, 2006 8:24:16 PM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Wednesday, March 29, 2006

When reviewing code, you see a lot of 'bad' use of exception handling. As Pieter Gheysens mentioned, you see a lot of code that looks like:

try
{
   // code statements
}
catch(Exception exc)
{
   throw exc;
}

No extra logic defined inside the catch-block, like logging, and when it is meant to re-throw an exception, the 'throw' statement must be used, instead of 'throw ex'. The statement 'throw ex' will erase the original stacktrace. Best practice for exception handling is

try
{
   // code that could throw an exception
}
catch(Exception exc)
{
   // log exception
   throw;
}

In case of a traditional layered architecture, UI, Business Logic (BL) & Data Access Logic (DAL), you will catch a DAL exception inside the BL layer, and translate it to a 'meaningfull' business exception. Each layer has a specific purpose and domain, and so are the exceptions!

It's also a good idea to subscribe to the Application.ThreadException (in case of a form application) and do the logging in there. Here log4net is used as logging tool.

ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
   log.Error("Application error", e.Exception);
   // show custom error dialog or throw
}

Wednesday, March 29, 2006 12:50:13 AM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Tuesday, March 28, 2006

Recently I have a new LCD monitor, the Samsung 204B. It's a 20.1 inch LCD monitor, with a resolution of 1600 x 1200 and a response time of 5 ms.

I am freak when it comes to monitors. I like to have a high resolution and the quality of the screen must be perfect. My current monitor is a Sony F520 (CRT), which is very expensive,  but it has a resolution of 2048 x 1536@86Hz and a pitch of 0.22mm. Therefore I was afraid about the quality of the Samsung 204B compared with my CRT monitor.

In one word, the quality is superb of the Samsung. The colors are vivid and the text is very clear (no ghosting or blur). No dead pixels found, has an ergonomic 4-way adjustable stand and I like the design very much. One more detail, it's about 50 pounds (23 kg) lighter than my current CRT monitor :)

Tuesday, March 28, 2006 10:16:11 PM (Romance Standard Time, UTC+01:00)  #    Comments [1] -

 Thursday, March 09, 2006

Modern GPUs are increasing in programmability and these chips can do more than just graphical computations. They can now be used as a coprocessor, and they can be integrated for a set of tasks. GPGPU (General-Purpose compuation on GPUs) is such an initiative that contains a catalog where the GPU can be used for general-purpose computation.

The big challenge, is to translate the everyday applications to two-dimensional graphic functions, like texture mapping. In other words: Pretend that everything is a game (source). 

As an example in this article and results, a quicksort algorithm of 18 million records in Visual C++ took 21 seconds, while the GPU took 2 seconds! What are the results for a Quad SLI setup? ;-)

Microsoft research is apparently working on a system that simplifies the programming of GPU to general-purpose tasks, it's called Accelerator (simplified programming of graphics processing units for general-purpose uses via data-parallelism).

Thursday, March 09, 2006 12:41:25 AM (Romance Standard Time, UTC+01:00)  #    Comments [0] -
 |  | 
 Friday, March 03, 2006

Wikipedia is the biggest and most famous online encyclopedia available. They have now more than 1 million articles, and it's still growing! This page gives you an idea about the architecture and the specification of the servers. The master database, called Samuel, contains all articles and has about a capacity of 400GB. Here you can monitor the wiki servers.

Friday, March 03, 2006 1:47:37 AM (Romance Standard Time, UTC+01:00)  #    Comments [0] -
 | 
 Wednesday, March 01, 2006

Continuous integration is the process that continuously build, analyze and test your sources. In many cases the process is triggered when changes are notified in the version control system, like VSS, CVS, etc. Martin Fowler has a good article about continuous integration.

In the .NET world, the most famous tool is CruiseControl.NET in combination with NAnt & NUnit. Getting an e-mail or popup from CruiseControl.NET is nice when a build is broken, but notifying the build status through traffic lights is much cooler.

Michael Swanson integrated CC.NET with the Ambient Orb. I think that the Ambient Orb is not an option for Europe, but you can integrate by using the X10 home automation technology. A good article about integrating X10 with .NET can be found on Coding4Fun and is called Controlling Lights with .NET.

Here are some (other) implementations:

 
 

I am going for a walk this evening, and I think that tomorrow a traffic light will be missing :-D

Wednesday, March 01, 2006 11:58:46 PM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Thursday, February 23, 2006

CAB is an application block for building smart clients in .NET 2.0 and contains proven practices for building complex UI forms with so called SmartParts and Workspaces. The DeckWorkspace enables you to show and hide controls and SmartParts in an overlapped manner with no visible frame.

If you switch between controls, you will notice that the controls are painted while there are initializing. During the loading you will see that there is some overlapping and it doesn't give you a professional user experience.

Therefore I extended the DeckWorkspace, so that the the controls are only redrawed when they are initialized. This can be done through the WM_SETREDRAW message. Note that this technique can also be used for other workspaces.

public class DeckWorkspaceEx: DeckWorkspace

   int freezePainting = 0;
   const int WM_SETREDRAW = 0xB;

   [DllImport("User32")]
   static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

   protected void FreezePaintingOn()
   {
      if (IsHandleCreated && this.Visible)
      {
         if (0 == freezePainting++)
         {
            SendMessage(Handle, WM_SETREDRAW, 0, 0);
         }
      }
   }

   protected void FreezePaintingOff()
   {
      if (freezePainting != 0)
      {
         if (0 == --freezePainting)
         {
            SendMessage(Handle, WM_SETREDRAW, 1, 0);
            Invalidate(true);
         }
      }
   }

   protected override void OnActivate(Control smartPart)
   {
      FreezePaintingOn();
      try 
      {
         base.OnActivate(smartPart);
      }
      finally 
      {
         FreezePaintingOff();
      }
   }

   protected override void OnClose(Control smartPart)
   {   
      FreezePaintingOn();
      try 
      {
         base.OnClose(smartPart);
      }
      finally 
      {
         FreezePaintingOff();
      }
   }

   protected override void OnHide(Control smartPart)
   {
      FreezePaintingOn();
      try 
      {
         base.OnHide(smartPart);
      }
      finally 
      {
         FreezePaintingOff();
      }
   }   

   protected override void OnShow(Control smartPart, SmartPartInfo smartPartInfo)
   {
      FreezePaintingOn();
      try 
      {
         base.OnShow(smartPart, smartPartInfo);
      }
      finally 
      {
         FreezePaintingOff();
      }
   }
}

Thursday, February 23, 2006 1:14:56 AM (Romance Standard Time, UTC+01:00)  #    Comments [4] -

 Sunday, February 19, 2006

One of the most popular markup languages is HTML. A markup language contains text and meta data about the text, such as layouts, styles, etc. Now Wiki sites are becoming more and more popular, and most of them use a customized markup language for HTML. One of the popular wiki sites is Wikipedia and is based on MediaWiki which uses WikiText as a markup language.

Here you find the syntax of WikiText. For example an asterix (*) is used for generating lists, the equal sign (=) can be used for sections, and there are many other constructions possible. For example templates are also possible, then contructions such as this can be made:

Template Result
{{chess position|=

 8 |rd|nd|bd|qd|kd|bd|nd|rd|=
 7 |  |pd|pd|pd|pd|pd|pd|pd|=
 6 |pd|  |  |  |  |  |  |  |=
 5 |  |  |  |  |  |  |  |  |=
 4 |  |  |  |  |  |  |  |  |=
 3 |  |  |  |  |  |  |  |  |=
 2 |pl|pl|pl|pl|pl|pl|pl|pl|=
 1 |rl|nl|bl|ql|kl|bl|nl|rl|=
    a  b  c  d  e  f  g  h

|30}}
 

There is also an extension called Wikitex for WikiText which is based on LaTeX. Where is the time that I wrote my thesis with pdfLaTeX using the MiKTeX distribution and WinEdt as my editor, very powerfull tool! However, with WikiTex you can even describe your music notes:

Template:

<music>
	\relative c' { 
		e16-.->a(b gis)a-.->c(d b)c-.->e(f dis)e-.->a(b a)
		gis(b e)e,(gis b)b,(e gis)gis,(b e)e,(gis? b e)
	}
</music>

Result:

One of the things I really like and I will certainly incorporate with MediaWiki is UMLGraph. With this library you can also describe your uml and a picture is generated for you. Examples can be found here.

Template Result
# UML User Guide: Figure 18-2

.PS

copy "sequence.pic";

boxwid = 1.1;
movewid = 0.5;

# Define the objects
object(C,"c:Client");
pobject(T);
object(P,"p:ODBCProxy");

# Message sequences
step();
active(C);
cmessage(C,T,":Transaction");
oconstraint("{Transient}");
step();
message(C,T,"setActions(a,d,o)");
active(T);
message(T,P,"setValues(d,3.4)");
active(P);
step();
inactive(P);
message(T,P,"setValues(a,\"CO\")");
active(P);
rmessage(T,C,"committed");
inactive(T);
inactive(P);
async(); dmessage(C,T);
step();
inactive(C);
step();

complete(C);
complete(P);

.PE
 

In my opinion, it becomes more important to have markup languages for the web (generates html, images, javascript, etc.). The big advantage is the flexibility of adding and updating the content very easily, without the need of extra tools and/or uploading images. Certainly when you have a wiki based site, where everybody can change the content.

On every site, whether through MediaWiki, Sharepoint, DotNetNuke or something else, knowledge has to be shared, and each site has its purpose. For example, a developer site has the need for syntax highlighting, uml diagrams, etc, whereas a mathematical site has the need for writing formulas, plots, etc. 

I think there is a need for a new library (application block) in the .NET community similar to WikiText. A markup language for the web that can be customized, extended and that can be incorporated in each application. Wouldn't it be great that you could write your blog through WikiText.NET? :) I think this can be a good subject for the contest.

 

Sunday, February 19, 2006 4:52:57 PM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Monday, February 13, 2006

Building an application and/or library usually starts with an OO design. Typically from that design you will define interfaces and implement base classes, which can be used as a starting point for building your application.

Defining the signature of a base class is very important. More specific, the visibility (public, protected & private) and purpose (virtual or not) of the methods are very important. Take for example an order-entry form based application that contains several screens for adding and updating data. Every screen will need to implement a kind of save method for persisting the changes. The very basic structure of your design can be:

public interface IDataView
{
   void Save()
}

public class DataViewBase: IDataView
{
   public virtual void Save()
   {
   }
}

Here the intention is that for example every screen of your application need to inherit from DataViewBase and implement the Save method. The problem with this design, is that the interface is marked as virtual (see Virtuality) and is responsible for two jobs, namely that it is part of an interface and for specifying the implementation.

A better way is to apply the Template Method design pattern, also called the  'Hollywood principle', that is, 'Don't call us, we'll call you'. (This pattern is also used a lot throughout the .NET SDK library.)

public class DataViewBase: IDataView
{
   public void Save()
   {
      DoSave();
   }

   protected virtual void DoSave()
   {
   }
}

This way you have complete control of the interface and you can easily add some logging before and after the save for example, extra checks, etc. If for example you need to implement whether a form-screen is dirty or not, you can easily plug it in as follow:

public class DataViewBase: IDataView
{
   public void Save()
   {
      // do some checks
      // do some logging

      if (IsDirty())
         DoSave();
   }

   protected virtual void DoSave()
   {
   }

   protected virtual bool IsDirty()
   {
      return true;
   }
}

If for example you need to extend the application such that when the screen is dirty you get a dialog box asking if you want to save or not. You can simply change the Save method without affecting the derived classes

public class DataViewBase: IDataView
{
   public void Save()
   {
      // do some checks
      // do some logging

      if (IsDirty())
         if (MessageBox.Show("Do you want to save?", "Information",
                            
MessageBoxButtons.YesNo,
                            
MessageBoxIcon.Question) == DialogResult.Yes)
            DoSave(); 
   }

   protected virtual void DoSave()
   {
   }

   protected virtual bool IsDirty()
   {
      return true;
   }
}

Due to the fact that the interface and the custom implementation is separated, we can easily extend and have control of the interface. In the other case we would have a lot of code duplication and in most cases we would violate the Liskov Substitution principle.

Monday, February 13, 2006 2:00:37 AM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Sunday, February 12, 2006
Here I describe how you can add syntax highlighting (C#, SQL, Javascript, etc.) in MediaWiki. In MediaWiki you can add syntax highlighting through GeSHiHighlight but I found it interesting for combining the world of PHP and .NET.
Sunday, February 12, 2006 11:04:22 PM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Wednesday, February 01, 2006

Microsoft announces on the msdn site that service pack 1 for Visual Studio 2005 will appear around Q3 of 2006.

Wednesday, February 01, 2006 10:46:43 PM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

Internet Explorer 7 Beta 2 has been released to the public, and can be downloaded here. I installed IE7 on a clean WinXP SP2 without any problems through VPC. My blog site and IStaySharp.NET rendered correctly with IE7.

The coolest feature I found, is the integrated rss reader...

Wednesday, February 01, 2006 2:04:57 AM (Romance Standard Time, UTC+01:00)  #    Comments [2] -
 | 
 Sunday, January 29, 2006

I followed the Office 12 Ascend training in Paris that was teached by Patrick. The training was very successful and Patrick showed a lot of interesting stuff and demo's about Office and Sharepoint. So little time, and so much to play with, but thankfully there are the O12 labs which are very useful and detailed.

On Patrick's blog you find a group photo with the team of Paris. Here are some pictures I took during the ascend training.

Sunday, January 29, 2006 1:43:25 PM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Friday, January 27, 2006

MediaWiki is a Wiki software licensed under GPL and written in PHP. Wiki allows users to easily add and edit content in a collaborative way. Wikipedia for example is one of the most popular Wiki's based on MediaWiki.

Updated: 29 January 2006

Below you find the necessary steps for installing MediaWiki.

  1. Download & install PHP 5.1.2 Installer. The installer creates a folder named c:\php
  2. Download PHP 5.1.2 zip package and extract all files in the folder c:\php
  3. Edit the php.ini file that resides in C:\Windows
    1. Change the extension directory: extension_dir = "c:/php/ext/
    2. uncomment mysql extension: extension = php_mysql.dll
  4. Download & install MySQL 4.1.16 (Windows (X86)).
  5. Create a virtual directory under IIS called phpmyadmin (e.g. c:/inetpub/wwwroot/phpmyadmin)
    1. Add the entry index.php in the documents tab.



  6. Download the latest phpMyAdmin and extract all files under the virtual directory of phpmyadmin.
  7. Edit the file config.default.php that resides in the phpmyadmin folder.
    1. Change the root password of MySQL.
  8. Browse to http://localhost/phpmyadmin

  9. Create a virtual directory under IIS called mediawiki (e.g. c:/inetpub/wwwroot/mediawiki)
    1. Add the entry index.php in the documents tab.
  10. Download the latest MediaWiki and extract all files under the virtual directory of mediawiki
  11. Browse to http://localhost/mediawiki & Configure.
    1. Add the line: '$wgEnableUploads = true' in the LocalSettings.php to enable uploads

Troubleshooting:

After installation you will notice that some pages give a warning, namely:

   Undefined index: REQUEST_URI in C:\Inetpub\wwwroot\mediawiki\includes\WebRequest

This is a known issue and can be fixed.

Friday, January 27, 2006 12:54:50 AM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Wednesday, January 25, 2006

Nowadays, is every kind of enterprise application more complex because there is the need to have much richer UI experience, integration with other systems, security, etc. Therefore it's important to have a good architecture and design of your application, so that you can better maintain and extend your application.

In every enterprise application you have a dependency to data resources. These resources can be to a database, web service, registry, file system, etc. It's a good practice to encapsulate all these dependent resources into services, such that the consumer (caller of the service) doesn't need to know the implementation details and the origin of the data.

That pattern is used a lot and is called a Service Gateway or Service Agent. There is a discussion on the difference between a Service Gateway that can be seen as consumer of a service and a Service Agent as a provider, but in the community both terms are used and mixed. The dependency between the consumer and the service is a contract which is typically a set of interfaces and business entities. In most cases, the service interface acts as a facade pattern, it encapsulates all subsystems underneath.

A typical example of a service agent is:

public interface ICustomerAgent
{
   Customer GetCustomer(int customerId);
   Order[] GetOrders(int customerId);
}

Note that the interface doesn't expose any details about the underlying system (e.g. database, xml, etc.) and communication protocol (e.g. web service, remoting, etc.).

Porgramming the UI of an application is typically the most difficult part and a challenge to achieve encapsulation and loose coupling. Thankfully there is for .NET 2.0 the Composite UI and the Smart Client Baseline Architecture Toolkit which guides you to an architecture for developing smart clients. Offcourse there is still the bible that every developer should read/know, it's the GoF book  that describes a set of design patterns.

One of the drawbacks of a designer like in Visual Studio.NET, is that it is so easy to drag and drop (in dutch I call it 'drag en drol') components, change some attributes and going through wizards for making your application. For prototyping and demo's this is good enough, but for real enterprise applications it's not. If you want you can write an application that has only one class (= form), named Form1.cs that includes all UI logic, business logic, data access, security, etc but that's not what we want.

Building the UI is basically populating UI controls with data that resides in business entities. This means that for example an array of customers that must be shown in a list, means that every customer entity must be translated to a ListViewItem (if we don't use databinding). In most of the cases this logic resides in the form/usercontrol itself, but in fact it can be seen as a separated responsability for mapping a customer entity to a ListViewItem and vice versa (like in SC-BAT). This means that we can have something like:

public static class CustomerMapper
{
   public static ListViewItem ToListViewItem(Customer customer) { ... }

   public static Customer FromListViewItem(ListViewItem listViewItem) { ... }
}

And in your form/usercontrol you write:

foreach (Customer customer in customers)
   customerListView.Items.Add(CustomerMapper.ToListViewItem(customer));

In every project there are some OO concepts and patterns I always try to achieve maintainability and extensibility. Making good OO designs is in my opinion a matter of experience, creativity and making abstractions. My conclusion...

The art of OO programming is to define the set of responsabilities (= objects),
to encapsulate and to interact them in a loosely coupled way.

Wednesday, January 25, 2006 1:50:37 AM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Monday, January 23, 2006

The patterns & practices team introduces a new project, called Baseline Architecture Toolkit (BAT). It's a collection of carefully selected, designed, and integrated guidance assets which include:

  • Reference Implementations
  • Application Blocks (such as CAB and EntLib)
  • Hands-On-Labs
  • How-Tos
  • Guidance Automation Packages automating common tasks
  • Documentation bringing all the parts together and describing the rationale behind the key smart client design recommendations.

The toolkit provides an effective way for architects, dev leads and developers to create high-quality baselines for their smart client applications, addressing non-trivial design and development challenges. The toolkit will provide reusable assets, guidance and examples for solving those challenges in the most common scenarios. Also, the guidance will be open and customizable, so you will be able to adjust it to your specific needs.

There is a Smart Client Baseline Architecture Toolkit released on GotDotNet and it will be updated every 3-4 weeks and is based on the CAB.

On Channel 9 you can find 2 casts about Smart Client BAT:

Monday, January 23, 2006 1:57:41 PM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

Here are some interesting blogs that are dedicated to Virtual PC

There are some fundamental features in Virtual PC that you really have to look at. For example differencing disks, see HOWTO: Use Virtual PC's Differencing Disks to your Advantage and you can also compress your VHD files, see Compact Your VHD Files.

Tip

On several keyboard layouts and for example on a AZERTY belgian keyboard you need the [Alt Gr] key for the backslash, square brackets, etc. Inside Virtual PC the [Alt Gr] key doesn't work, therefore you can use an alternative for the [Alt Gr] key, which is [Ctrl] + [Alt].

Update:

There is a hotfix now for the ALTGR problem.

Monday, January 23, 2006 1:58:06 AM (Romance Standard Time, UTC+01:00)  #    Comments [1] -

 Friday, January 20, 2006

The Windows SDK - Janary 2006 CTP is available for download and can be used for developing WinFX applications (WWF, WPF & WCF). This page give you the several components that are needed for developing WinFX applications, these are:

When I tried to install the SDK, I received the message that the user has cancelled the installation. In the config file I had the following statements:

0:03:18 vrijdag 20 januari 2006: [SDKSetup:Error] Config_Products_Configure: Configuration of Product Microsoft .NET Compact Framework 2.0 (failed): User cancelled installation.
0:03:18 vrijdag 20 januari 2006: [SDKSetup:Info] Config_Products_Configure: End configuration of product: Microsoft .NET Compact Framework 2.0
0:03:18 vrijdag 20 januari 2006: [SDKSetup:Info] Config_Products_Install: End installation of product: Microsoft .NET Compact Framework 2.0
0:03:18 vrijdag 20 januari 2006: [SDKSetup:Error] Config_Products_Install: Windows SDK Setup (failed): Installation of the "Microsoft .NET Compact Framework 2.0" product has reported the following error: User cancelled installation.

The solution was to simply remove the Microsoft .NET Compact Framework 2.0 from the Add/Remove Programs in the control panel. After that the installation was successfull.

Friday, January 20, 2006 12:56:00 AM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

 Sunday, January 15, 2006

For a long time I was thinking about purchasing a portable GPS device and I also wanted to replace my old GSM. One of the most popular devices is TomTom Go, but I felt it would be better to integrate both into a smartphone. TomTom also has a product called TomTom Mobile 5 that allows GPS navigation on your smartphone.

Therefore I bought the Qtek 8310 and TomTom Mobile 5 at PDAshop.be, I picked up the Navigation pack. The installation was easy, the only thing I had to do was to upgrade the TomTom software to TomTom Mobile 5.2 which can be found here, so that the software supports the Qtek 8310 which is the same as the i-mate sp5 (this must be chosen in the product upgrade). The TomTom package includes a mini-SD card which contains the TomTom software and maps that can be inserted into the smartphone.

I have to admit that everything works perfectly, the software is identically to the TomTom Go devices, there are no speed issues and the screen resolution is more than sufficient. Until now I didn't encounter any problems.

The smartphone itself has many many features, one of them is:

  • OS: Microsoft Windows Mobile
  • Quad-band GSM/GPRS / EDGE module
  • GPRS functionality
  • 1.3 Mega Pixels CMOS
  • Mini-USB plug (Slave USB, Power In)
  • Infrared IrDA SIR
  • miniSD Card slot
  • Bluetooth
  • WiFi
  • ActiveSync
  • MSN Messenger, IE, etc.
  • ...

And most importantly you can write .NET applications for your smartphone. If you download the Windows Mobile 5.0 SDK for Smartphone you get extra templates inside Visual Studio 2005 for writing smartphone applications.

Update:

Sunday, January 15, 2006 3:28:05 PM (Romance Standard Time, UTC+01:00)  #    Comments [2] -

 Monday, January 09, 2006

Some years ago on a project I needed a usercontrol for code coloring SQL statements. At that time I reused a dll library that is used inside the query analyzer of SQL server. I wrote a little article about that topic, I think it gives some useful information about COM interop in .NET.

Monday, January 09, 2006 8:34:44 PM (Romance Standard Time, UTC+01:00)  #    Comments [0] -

Navigation
Archive
<April 2006>
SunMonTueWedThuFriSat
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008
Christoph De Baene
Sign In
Statistics
Total Posts: 151
This Year: 22
This Month: 1
This Week: 0
Comments: 147