Wednesday, April 10, 2013

Handling 404 Error in ASP.NET MVC

In ASP.NET Web Forms, handling 404 errors are easy - which is basically a web.config setting. In ASP.NET MVC, it is a bit more complicated. Why is it more complicated? In comparison, everything is seemingly easier in MVC than WebForm.

It is more complicated mainly because of Routing. In WebForm, most 404 occurs because of non-existent file and each UR: is usually mapped to a particular file (aspx). With MVC, that is not the case. All requests are handled by the Routing table and based on that it will invoke appropriate controller and actions etc. Secondly, our basic default route usually is quite common ({controller}/{action}/{id}) - therefore most URL request will be caught by this route.

So, let's dive in on how can we do proper handling of 404 errors with ASP.NET MVC.

TURN ON CUSTOM ERROR IN WEB.CONFIG

    <customErrors mode="On" defaultRedirect="~/Error/Error">
      <error statusCode="404" redirect="~/Error/Http404" />
    </customErrors>

DECLARE  DETAIL ROUTES MAPPED IN ROUTE TABLE

So instead of just using the default route:
   routes.MapRoute(
      name: "Default",
      url: "{controller}/{action}/{id}",
      defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
   );
Declare all your intended route explicitly and create a "catch-all" to handle non-matching route - which basically a 404. In MVC, a 404 can happen when you try to access a URL where the there is no controller for. This code in the routing table handles that scenario.
   routes.MapRoute(
      name: "Account",
      url: "Account/{action}/{id}",
      defaults: new { controller = "Account", action = "Index", id = UrlParameter.Optional }
   );

   routes.MapRoute(
      name: "Home",
      url: "Home/{action}/{id}",
      defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
   );

   routes.MapRoute(
      name: "Admin",
      url: "Admin/{action}/{id}",
      defaults: new { controller = "Admin", action = "Index", id = UrlParameter.Optional }
   );

   routes.MapRoute(
      name: "404-PageNotFound",
      url: "{*url}",
      defaults: new { controller = "Home", action = "Http404" }
   );

OVERRIDE HANDLEUNKNOWNACTION IN BASECONTROLLER CLASS

Create a Controller base class that every controller in your application inherits from. In that base controller class, override HandleUnknownAction method. Now, another scenario that a 404 may happen is that when the controller exists, but there is no action for it. In this case, the routing table will not be able to trap it easily - but the controller class has a method that handle that.
    [AllowAnonymous]
    public class ErrorController : Controller
    {
        protected override void HandleUnknownAction(string actionName)
        {
            if (this.GetType() != typeof(ErrorController))
            {
                var errorRoute = new RouteData();
                errorRoute.Values.Add("controller", "Error");
                errorRoute.Values.Add("action", "Http404");
                errorRoute.Values.Add("url", HttpContext.Request.Url.OriginalString);
 
                View("Http404").ExecuteResult(this.ControllerContext);
            }
        }
 
        public ActionResult Http404()
        {
            return View();
        }
 
        public ActionResult Error()
        {
            return View();
        }
    }

CREATE CORRESPONDING VIEWS

View for generic error: Error.chtml
@model System.Web.Mvc.HandleErrorInfo
 
@{
    ViewBag.Title = "Error";
}
 
<hgroup class="title">
    <h1 class="error">Error.</h1>
    <br />
    <h2 class="error">An error occurred while processing your request.</h2>
    @if (Request.IsLocal)
    {
        <p>
            @Model.Exception.StackTrace
        </p>
    }
    else
    {
        <h3>@Model.Exception.Message</h3>
    }
</hgroup>
View for 404 error: Http404.chtml
@model System.Web.Mvc.HandleErrorInfo
 
@{
    ViewBag.Title = "404 Error: Page Not Found";
}
<hgroup class="title">
    <h1 class="error">We Couldn't Find Your Page! (404 Error)</h1><br />
    <h2 class="error">Unfortunately, the page you've requested cannot be displayed. </h2><br />
    <h2>It appears that you've lost your way either through an outdated link <br />or a typo on the page you were trying to reach.</h2>    
</hgroup>
-- read more and comment ...

Sunday, April 7, 2013

Properly Deleting Database for Database-Migration

I am working on a simple brand new project with ASP.NET MVC and decided to try EF to connect to my database. This gives me the opportunity to learn EF Code-First, database-migration, etc.

Everything seems to be pretty intuitive until when I am running "update-database" from the Package Manager Console. I created my Configuration class, turn-on automatic migration, and populated my database using Seed method, made sure all my context are correct.

[TL;DR]

When one need to delete/recreate a database, do not delete the mdf file from App_Data, but instead go to "SQL Server Object Explorer" and find your database under (localdb) and delete it from there.

FULL VERSION:


Then, since I want to recreate my database, I delete my database (mdf file) from my App_Data folder under Solution Explorer and then run "update-database". See picture on the left.

But then I am getting an error:
Cannot attach the file D:\Projects\MvcApplication1\App_Data\aspnet-MvcApplication1-20130407085115.mdf' as database 'MvcApplication1'.
I looked in the file explorer and the mdf file is surely gone. Try to close Visual Studio and reopen, same error.

Well, the database was initially created when I try to "Register" or create an account using the site (it's using SimpleMembershipProvider) - so maybe it will recreate it if I simply run the site and try to register again. But then I am getting the same error when running the website.

I went to the recycle bin, restore the mdf file and ran the project again - it worked. It did not have the new tables or new data, but no "cannot attach" error. Restoring this file also restore my default connection. If I try to delete the mdf file again, then my project won't run and my database-migration also won't run.

I almost resort to think that "Code-First" is a lie - that I simply have to add the new tables manually in the SQL table designer, etc. This is so confusing - should not be that hard, I think.

So with the mdf file deleted, I went to Server Explorer and checked that my connection to the database is gone - there is nothing under "Data Connections".

So maybe I need to delete the data connection instead of deleting the mdf file from App_Data? So I did a restore again from my Recycle Bin, made sure my project ran, and then I deleted my connection from the Server Explorer, rebuilt the project and ran it. It worked! But my delight is short-lived, since then I realized that deleting the connection does not necessarily mean deleting the database - which I quickly checked that my mdf file still in the App_Data folder. I simply deleted the connection to view the database via Visual Studio.

I tried more and more things - which increase the frustration level - because at this point I am stuck in my project and all I have been trying to do is to modify my database schema. If I did this using the old way using SQL Management Studio or SQL Express, or even Linq-to-SQL, I could have been making a huge progress in my project.

SOLUTION

Until I stumbled on "SQL Server Object Explorer" under "VIEW" in your VS Studio 2012 top menu/toolbar. I noticed that although my mdf file is deleted from my App_Data folder, but under "SQL Server Object Explorer", my database is still registered under (localdb)\v11.0\Databases.

Out of curiosity, I deleted my database from there and re-ran my project - it worked. It recreated my database (without the new tables and seed data). So I deleted it again from SQL Object Explorer, and ran "update-database" - it ran successfully this time. EUREKA!!

So lesson learned: when one need to delete/recreate a database, do not delete the mdf file from App_Data, but instead go to "SQL Server Object Explorer" and find your database under (localdb) and delete it from there.

-- read more and comment ...

Monday, April 1, 2013

Crumbtrail ActionFilter

Recently, I had to make a crumb-trail in the web application that I am working on (ASP.NET MVC). There are multiple ways of doing this and initially I elected to do this in my controller base class (which is inherited by all my controller classes). I created a method that do the job - but this means that this method has to be called on every single action (with GET method). If a fellow developer miss to call the method, then it would mean that the data in the crumb-trail is not built properly or accurately. If there is just an interceptor that I can hook into that will run automatically every time a controller action is being called ... *sigh

Wait - there is one, ActionFilter!!

So I created an action filter and via configuration register and apply it to all controllers. The logic in my code handles the exceptions to the apply all (such as Account controller, POST method, and non-authenticated users). Here is the code to the ActionFilter code:
    public class CrumbTrailKeyValuePair<TKey, TValue>
    {
        public CrumbTrailKeyValuePair() { }
        public CrumbTrailKeyValuePair(TKey key, TValue value)
        {
            Key = key;
            Value = value;
        }
        public TKey Key { get; set; }
        public TValue Value { get; set; }
    }

    public class CrumbTrailAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            if (filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                // skip recording Account controller actions
                if (filterContext.RouteData.Values["controller"] != null && 
                    filterContext.RouteData.Values["controller"].ToString() != "Account")
                {
                    // put in cookies
                    Queue<CrumbTrailKeyValuePair<string, string>> crumbTrailQueue = 
                       new Queue<CrumbTrailKeyValuePair<string, string>>();
                    HttpCookie crumbTrailCookie = 
                       filterContext.HttpContext.Request.Cookies["CrumbTrailLinks"] ?? new HttpCookie("CrumbTrailLinks");
 
                    // initialize serializer
                    var serializer = new JavaScriptSerializer();
 
                    // if crumbTrailCookie is not empty, retrieve value from cookie the rehydrate queue
                    if (crumbTrailCookie != null && !string.IsNullOrEmpty(crumbTrailCookie.Value))
                    {
                        // rehydrate crumbTrailQueue with cookie value         
                        crumbTrailQueue = 
                           new Queue<CrumbTrailKeyValuePair<string, string>>
                              (serializer.Deserialize<IEnumerable<CrumbTrailKeyValuePair<string, string>>>
                                 (HttpUtility.UrlDecode(crumbTrailCookie.Value)));
                    }
 
                    if (filterContext.HttpContext.Request.HttpMethod.ToUpper() == "GET")
                    {
                        // get page title
                        var pageTitle = string.IsNullOrEmpty(filterContext.Controller.ViewBag.Title) ? 
                           "PAGE" : filterContext.Controller.ViewBag.Title;
 
                        // get url
                        string url = filterContext.HttpContext.Request.RawUrl;
 
                        // if current page is not in both queue, then add it
                        if (!crumbTrailQueue.Any(x => x.Value == url && x.Key == pageTitle))
                        {
                            // remove oldest menu item, keep queue length to 6
                            if (crumbTrailQueue.Count >= 5)
                                crumbTrailQueue.Dequeue();
 
                            // insert new menu item into queue
                            crumbTrailQueue.Enqueue(new CrumbTrailKeyValuePair<string, string>(pageTitle, url));
                        }
 
                        crumbTrailCookie.Value = serializer.Serialize(crumbTrailQueue);
                        crumbTrailCookie.Expires = DateTime.Now.AddDays(365);
                        crumbTrailCookie.Path = "/";
                        filterContext.HttpContext.Response.Cookies.Add(crumbTrailCookie);
                    }
 
                    // put in viewbag
                    if (filterContext.Result.GetType().Name == "ViewResult")
                    {
                        (filterContext.Result as ViewResult).ViewBag.QuickAccessQueue = crumbTrailQueue;
                    }
                }
            }
        }
    }
Inside the view, just get the queue from the ViewBag and display accordingly. The queue is stored temporarily in a cookie, so it will be remembered even when the browser is closed and reopen and relogin.
-- read more and comment ...

Monday, March 25, 2013

Creating Validation Adapter for Validation Attribute

If your project is rather big, often you would like your validation to reside on the business layer instead of on the UI layer. So for example, using our previous code sample, it would make perfect sense if the custom attribute code resides in your business layer project (instead of on the UI project). BUT, to enable client validation (here for example), implementing IClientValidatable is required and IClientValidatable is within the System.Web.Mvc namespace. This means that your business layer needs to reference System.Web.Mvc. But why would you want to do that - referencing a UI related reference in your business layer project?

When we look at the built-in validator (such as RequiredAttribute), how did they do this? If we look at the documentation, it is within System.ComponentModel.DataAnnotations.ValidationAttribute namespace and deriving from System.Object - there is no dependency to System.Web.Mvc at all. We can use that validation attribute in WinForm, Silverlight, WebForm, WPF, etc.


There is no magic - the ASP.NET guys simply built a validation adapter for it in the System.Web.Mvc namespace. In this post, I am going to show you how to build one for our MagicNumber validator.

So let's clarify on our namespacing issue - in our business layer, let's call that MyApp.Library project (namespace: MyApp.Library), which includes MyClass.cs (namespace: MyApp.Library or MyApp.Library.MyClass to be complete) and our custom validation attribute MagicNumberAttribute.cs (namespace: MyApp.Library or MyApp.Library.MagicNumberAttribute to be complete).

Here is the code for MyApp class:
   public class MyClass {
      // ... more code
      [MagicNumber(ErrorMessage = "Sum is not correct")]
      public int MyData { get; set; }
      // ... more code
   }
Here is our code for the custom validation attribute in MagicNumberAttribute.cs:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute {
   // constructor to accept the comparison property name
   public MagicNumberAttribute(string thirtySevenProperty) : base() {
      if (thirtySevenProperty == null) {
         throw new ArgumentNullException("thirtySevenProperty");
      }
      ThirtySevenProperty= thirtySevenProperty;
   }

   // property to store the comparison field name
   public string ThirtySevenProperty{ get; private set; }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      // get property info of the "ThirtySevenProperty"
      PropertyInfo thirtySevenPropertyInfo = validationContext.ObjectType.GetProperty(ThirtySevenProperty);

      // check if that property exists / valid
      if (thirtySevenPropertyInfo == null) {
         return new ValidationResult(String.Format(CultureInfo.CurrentCulture, "UNKNOWN PROPERTY", ThirtySevenProperty));
      }

      // get the value of the property
      object thirtySevenPropertyValue = thirtySevenPropertyInfo.GetValue(validationContext.ObjectInstance, null);

      // do comparison and return validation result with error if not equal
      if (!Equals(value, thirtySevenPropertyValue)) {
         return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
      }

      // return null if everything is ok
      return null;
   }
}
We'll call our ASP.NET MVC project to be MyApp.UI.Mvc (namespace: MyApp.UI.Mvc). Here is our javascript from the previous post, we'll reuse it, put it in a js file:
   jQuery.validator.unobtrusive.adapters.addSingleVal("magicnumber", "other");

   jQuery.validator.addMethod("magicnumber", function (val, element, other) {
      var modelPrefix = element.name.substr(0, element.name.lastIndexOf(".") + 1)
      var otherVal = $("[name=" + modelPrefix + other + "]").val();
      if (val && otherVal) {
        return val == otherVal;
      }
      return true;
   );
So now, instead of implementing IClientValidatable, we will be creating a validation adapter. We will put this in a file in our MyApp.UI.Mvc project, let's name it "MagicNumberAttributeAdapter.cs". The code is simple:
public class MagicNumberAttributeAdapter : DataAnnotationsModelValidator
{
   public MagicNumberAttributeAdapter(ModelMetadata metadata, ControllerContext context, MagicNumberAttribute attribute)
       : base(metadata, context, attribute)
   {
   }

   public override IEnumerable GetClientValidationRules()
   {
      var rule = new ModelClientValidationRule
      {
          ErrorMessage = this.ErrorMessage,
          ValidationType = "magicnumber",
      };
      rule.ValidationParameters.Add("other", this.Attribute.OtherProperty);
      yield return rule;
   }
}
To make the connection between the adapter and the validation attribute itself, we need to let MVC know about it - so we need to register it inside the global.asax.cs file:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MagicNumberAttribute), typeof(MagicNumberAttributeAdapter));
Once all those are setup, our custom validator will fire on the client-side (as well as server-side), we have clear separation of concern - where the business layer does not reference the UI as dependency, and our custom validation attribute is also reusable. Awesome!

Additional reading:
-- read more and comment ...

Sunday, March 17, 2013

Custom Validation Attribute - Client Side Enabled With Custom Rule

In the previous post, I went over on how to enable client-side validation for our custom validation attribute - using a pre-build jQuery validator. Now what if you want to create your own custom script - because your validator needs to do something truly custom that the pre-build validator is not covering? No problem!

Modify your code to this:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute, IClientValidatable {
   // constructor to accept the comparison property name
   public MagicNumberAttribute(string thirtySevenProperty) : base() {
      if (thirtySevenProperty == null) {
         throw new ArgumentNullException("thirtySevenProperty");
      }
      ThirtySevenProperty= thirtySevenProperty;
   }

   // property to store the comparison field name
   public string ThirtySevenProperty{ get; private set; }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      // get property info of the "ThirtySevenProperty"
      PropertyInfo thirtySevenPropertyInfo = validationContext.ObjectType.GetProperty(ThirtySevenProperty);

      // check if that property exists / valid
      if (thirtySevenPropertyInfo == null) {
         return new ValidationResult(
            String.Format(CultureInfo.CurrentCulture, "UNKNOWN PROPERTY", ThirtySevenProperty));
      }

      // get the value of the property
      object thirtySevenPropertyValue = thirtySevenPropertyInfo.GetValue(validationContext.ObjectInstance, null);

      // do comparison and return validation result with error if not equal
      if (!Equals(value, thirtySevenPropertyValue)) {
         return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
      }

      // return null if everything is ok
      return null;
   }

   public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
      ModelMetadata metadata, ControllerContext context) {
      var rule = new ModelClientValidationRule
      {
          ErrorMessage = this.ErrorMessage,
          ValidationType = "magicnumber",
      };
      rule.ValidationParameters.Add("other", OtherProperty);
      yield return rule;
   }
}
As we see, in "GetClientValidationRules", I am returning a "ModelClientValidationRule" with "ValidationType" set to "magicnumber". This string is the javascript method name. The name itself is not important, but they need to be consistent between what you put here and the actual method name in the javascript method itself. I am also adding parameters into it - which then will be transformed into a name-value pair that can be retrieved in our javascript method.

Now create a javascript file to be included in your project (it must be referenced AFTER the basic jquery.js and jquery.validate.js and jquery.validaten.unobstrusive.js).
   jQuery.validator.unobtrusive.adapters.addSingleVal("magicnumber", "other");

   jQuery.validator.addMethod("magicnumber", function (val, element, other) {
      var modelPrefix = element.name.substr(0, element.name.lastIndexOf(".") + 1)
      var otherVal = $("[name=" + modelPrefix + other + "]").val();
      if (val && otherVal) {
        return val == otherVal;
      }
      return true;
   );
The first line is to connect our javascript method "magicnumber" into the validation event and the rest is our client-side script to do client side validation. Our "magicnumber" method takes in "other" parameter, which will hold the value for the name for our comparison property/field - we need that to get the value of the field. We also want to consider if our model is using prefix - beyond that it is just doing a dom selector and get the value and returning a comparison result.

Additional reading:
-- read more and comment ...

Tuesday, March 12, 2013

Custom Validation Attribute - Client Side Enabled!

In previous posts, I outlined how to make a custom validation attribute - including a more complex one that depends on another property. Both of those validators are working - but they are working on the server-side. So if you have an ASP.NET MVC web application, the validation will fire after it gets to the server side within your controller. Ideally, we want to do client-side validation as well - so that the form does not need to POST if the data is not valid. How do we do that?

We can make custom javascript, query the fields manually and check for conditions - but this code will be disconnected from the custom validator that we created. Doing custom javascript also means that it will be so specific that the likelihood of being reusable is very very small.

Also, as we have seen with the built-in validators that shipped with ASP.NET MVC, they seem to just work - no custom javascript needed per field or manual client-side validation. When a property or a field is decorated with the correct attribute and client-side validation is enabled in the web.config, then boom - it just works. How can we make our validator to behave in similar manner?


One way to do this is by implementing IClientValidatable in your custom validator. Implementing this interface means we must implement a method called "GetClientValidationRules". This method is what basically will become a connector for our validator to the client-side. The property decorated with our custom validation attribute will then outputting flags that will indicate that it should be evaluated during client-side validation. In this method as well we need to provide additional information to properly evaluate the validity of the property on the client-side - mostly a (javascript) method name to be called during validation and some parameters needed to evaluate property within that method. When we do this, this is the updated validator class will look like:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute, IClientValidatable {
   // constructor to accept the comparison property name
   public MagicNumberAttribute(string thirtySevenProperty) : base() {
      if (thirtySevenProperty == null) {
         throw new ArgumentNullException("thirtySevenProperty");
      }
      ThirtySevenProperty= thirtySevenProperty;
   }

   // property to store the comparison field name
   public string ThirtySevenProperty{ get; private set; }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      // get property info of the "ThirtySevenProperty"
      PropertyInfo thirtySevenPropertyInfo = validationContext.ObjectType.GetProperty(ThirtySevenProperty);

      // check if that property exists / valid
      if (thirtySevenPropertyInfo == null) {
         return new ValidationResult(String.Format(CultureInfo.CurrentCulture, "UNKNOWN PROPERTY", ThirtySevenProperty));
      }

      // get the value of the property
      object thirtySevenPropertyValue = thirtySevenPropertyInfo.GetValue(validationContext.ObjectInstance, null);

      // do comparison and return validation result with error if not equal
      if (!Equals(value, thirtySevenPropertyValue)) {
         return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
      }

      // return null if everything is ok
      return null;
   }

   public IEnumerable<ModelClientValidationRule$gt; GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
      yield return new ModelClientValidationEqualToRule(this.ErrorMessage, OtherProperty);        
   }
}
In the code above, I am reusing an existing validation rule "ModelClientValidationEqualToRule" which will connect with "equalto" jQuery client-validator. This is a pre-existing rule, so at this point, we can just run this and it should work - no more code needed.

In the next post, I will go over on how to return our own custom ModelClientValidationRule and how to create a corresponding javascript client-side validator.

Additional reading:
-- read more and comment ...

Friday, March 8, 2013

Creating Custom Validation Attribute With Dependency To Other Property

In the last post, I went through the steps to make a custom validation attribute - if you missed it, check it out here: Creating Custom Validation Attribute. Now let's say we want to make our validator to look up on the value "37" from another property in our class (instead of hard-coding it). How do we do that? With the help of reflection, we can do that quite easily.

Here is our modified class:
public class MyClass {
   // ... more code
   [MagicNumber("ThirtySeven", ErrorMessage = "Sum is not correct")]
   public int MyData { get; set; }
   // ... more code
   public int ThirtySeven {
      get { return 37; }
   }
   // ... more code
}

In our new custom validator, we override a different IsValid method (the original one is commented out):
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute {
   // constructor to accept the comparison property name
   public MagicNumberAttribute(string thirtySevenProperty) : base() {
      if (thirtySevenProperty == null) {
         throw new ArgumentNullException("thirtySevenProperty");
      }
      ThirtySevenProperty= thirtySevenProperty;
   }

   // property to store the comparison field name
   public string ThirtySevenProperty{ get; private set; }

   // public override bool IsValid(object value) {
   //    var num = int.Parse(value.ToString());
   //    return num == 37;
   // }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      // get property info of the "ThirtySevenProperty"
      PropertyInfo thirtySevenPropertyInfo = validationContext.ObjectType.GetProperty(ThirtySevenProperty);

      // check if that property exists / valid
      if (thirtySevenPropertyInfo == null) {
         return new ValidationResult(String.Format(CultureInfo.CurrentCulture, "UNKNOWN PROPERTY", ThirtySevenProperty));
      }

      // get the value of the property
      object thirtySevenPropertyValue = thirtySevenPropertyInfo.GetValue(validationContext.ObjectInstance, null);

      // do comparison and return validation result with error if not equal
      if (!Equals(value, thirtySevenPropertyValue)) {
         return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
      }

      // return null if everything is ok
      return null;
   }
}

That's it!
Additional reading:
-- read more and comment ...

Sunday, March 3, 2013

Creating Custom Validation Attribute

In this post, I will outline steps in making custom validation attribute. This is the first post of a series, which eventually will go all the way to making the validation to be able to evaluated in the client side. But, let's not get too far ahead - first we need to make our custom validator.

In this example, I have a simple validator that need to evaluate whether the value of the field "MyData" is equal to the "37".

Our class:
public class MyClass {
   // ... more code
   [MagicNumber(ErrorMessage = "Magic Number is not correct")]
   public int MyData { get; set; }
   // ... more code
}

Our custom validator:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute {
   public override bool IsValid(object value) {
      var num = int.Parse(value.ToString());
      return num == 37;
   }
}

Done. Simple.
Additional reading:
-- read more and comment ...

Tuesday, December 11, 2012

Prepaid Cellphone Services (update)

In July, I wrote about going PREPAID for my cellphone service - in which I mentioned T-Mobile Monthly 4G plan. IN the past several months, I have done quite an extensive research about available prepaid service, from just minutes and messages to all unlimited. This post is a highlight or summary of what I found and my recommendations.

Straight Talk

Straight Talk is probably providing the best value for prepaid phone service on the market right now in US - with $45 a month for unlimited voice, messages, and data*. This is probably my choice in January when I am done with my T-Mobile contract.

Straight Talk Wireless' parent company is TracFone Wireless - which is the largest prepaid service provide in US. TracFone mostly offers basic services such as voice only or voice and messages only. Straight Talk is different that it's main service is about unlimited* voice, messages, and data for $45 a month. The data part is not truly unlimited - it is 2-5GB with 3G speed with some restrictions such as data plan cannot be used to tether, cannot be used with BB service, no P2P, etc.

Straight Talk uses GSM bands from both T-Mobile and ATT. So what this means is that you can bring your own ATT-locked phone and asked for ATT-based SIM from Straight Talk or bringing your own T-Mobile-locked phone and ask for T-Mobile based SIM. If you have a T-Mobile based phone, you will get the T-Mobile 4G (HSDPA+) with Straight Talk. ATT will only give you 3G.

Using both bands also means larger selection of handsets. If you want to keep your Lumia 900 from ATT, you can. If you want to use the Galaxy S3 from T-Mobile, you can. If you want to use the new Nokia Lumia 920 from ATT, you can.  No need to unlock the device - just ask for specific SIM card based on the phone.

You can pay in advance for several months or even a year - which will give you some savings. For example, if you buy a year plan, it will cost you $495 (instead of 12 * $45).

There are instances where MMS won't work with Straight Talk. There is also a short configuration setting mads that you will need to do once the SIM is installed. Nothing hard or requires unlocking etc - see here.

T-Mobile

With T-Mobile, to get the similar package like Straight Talk, you will need to pay $60 per month. But T-Mobile provides more options - see my post about T-Mobile prepaid service here. The killer awesome deal from T-Mobile is the $30 per month (web only or from Walmart) which gives you 100 minutes, unlimited messages, and 5GB of data. This is actually perfect for me and would have gone for it except for the causes below.

T-Mobile coverages is quite lacking compared to ATT or Verizon. I go to several places in a year that my current T-Mobile phone do not get coverage at all - while my ATT friends get their 3G.

Secondly, T-Mobile handset selection is limited, especially compared to Straight Talk, which gives you the flexibility to choose handsets from BOTH ATT and T-Mobile.

Also, T-Mobile customer service has been awful for the past 2 years or so.

Virgin Mobile and Cricket Wireless

Virgin Mobile (running on Sprint network) probably provides the best pricing with $35 per month for 300 minutes, unlimited messages and data* (2.5 GB). This would have been the perfect plan for me. But then, to go unlimited everything, it would have cost you $55 - which is still more expensive than Straight Talk.

Cricket also has $45 a month for 1000 minutes with unlimited text and data (1GB). To get 2.5GB data, the cost is $65 per month. 

Virgin & Cricket also has very limited selection of phone (Sprint phones) - but it has the iPhone 4 and iPhone 4S options. Secondly, Sprint's 3G (or 4G) is the slowest.

Others

Boost Mobile provides unlimited voice, text, and data for $50 per month, but like Straight Talk, if you pay several months in advance then you will get a discount.

Verizon provides the best coverage for 3G, but it is also the most expensive. It will cost you $50 per month for unlimited text, voice, and web* on a basic-dumb-phone. If you have a smartphone, it will cost you $80 per month.

ATT GoPhone costs $65 per month with 1GB of 3G data.



-- read more and comment ...

Sunday, November 18, 2012

Microsoft Surface

Several months ago, my trusty DELL laptop that is provisioned from my employer died. Since I have been working on a project at a client that provisions me with a desktop, I have not been rushing to get a replacement (although I did ask for a replacement). But since the contract mandates that any work I do must be done on the client's facility, so I am all equipped with what I need to perform my work and a work laptop would be superfluous.

But there is also a need for a mobile device for me - different client assignments, VPN to the office, RDP to my development VMs, creating documents and presentations, etc. I have a smartphone that fulfill most of my email and social networking needs, but my smartphone is far from the quintessential device to create content and be productive (such as creating a Power Point presentation or writing a blog post like this). 

I am also more of a desktop guy. I prefer to have a beefy desktop to do most of my work. At home, I have desktop with quad core CPU, 8GB of RAM, 256GB SSD, plenty of data storage, decent video card to power my three monitors setup (all 24 inches, 1920x1200). You can see my home setup on the picture on the left - where I have my desktop powering the monitors on the left. The Surface was connected to the smaller monitor (21") on the left.

Trying to meet the need between the desktop and the smartphone, there are several ideas that come to mind: a huge smartphone (like Galaxy Note), a Chromebook, an ultra-book, or a tablet. But, none of them really fit with my needs or budget - until I heard about Microsoft Surface. I went to the closest store in Cleveland, OH to try and test the Microsoft Surface the day it came out on October 26th 2012. I liked it, bought it, and have been using it pretty much daily ever since.

So why Surface? Why not those other options? Macbook Air? Chromebook? iPad? Galaxy Tab? Nexus 7?

TL;DR

Microsoft Surface is fantastic. It fits my needs awesomely with the correct price point. It provides me the tools to be productive (like creating this lengthy blog post or creating a Power Point presentation) but yet still allows me to have the occasional  entertainment and media consumption such as games (WORDAMENT, Angry Birds), movies (XBOX Movies, Hulu), etc. It is not the be-all and end-all laptop or tablet - but it is certainly much more capable than iPad, Chromebook, or any Android tablets I have tried.

OFFICE SUITE

I owned a Galaxy Tab and used Nexus 7 quite extensively (before giving it to the office for Android development purpose).  While the Tab and the Nexus 7 are nice consumption devices - they are really lacking in productivity tools. Quick Office, Google Docs (and others) are quite nice in their own rights, but they are not Microsoft Office. My experience in using them left me longing for the Office experience.

The Surface comes with Office 2013 out of the box. It only has Word, Excel, Power Point, and OneNote. Now these applications are not striped down version of Word or Excel, etc - but they are the full version, just like the regular Office that I have on my desktop. Yes, it does not have Outlook or Access, but the essential four is enough for me. Just by that virtue alone - the Surface has become the best tablet for productivity.

My mobile device does not need to be beefy - but it has to be able to support Office - and Surface does that (and more). This is also one of the reason why Chromebook is not enough - it runs great for most of my need (email, browsing, searches, remote desktop, even Office Web App), but falls short for my power user need for Office.

TOUCH/TYPE COVER

The cover for Surface doubles as a keyboard as well - which is awesome.I have a cover for my Tab, which also functions as a stand. But then if I want to use a keyboard dock, I have to take the Tab out of the cover and dock it. Once done, I have to un-dock it and put it back into its cover. Plus, ever seen a Galaxy Tab keyboard/dock? It's ugly and thick.

The Touch Cover is thin (3mm), acts as a cover, water-repellant, can be folded back when used as a tablet (without removing it), strong magnetically attached, and looks awesome. It does take some getting used to during my first day or so using it - but then after a while, I can type probably 85%+ speed on it (assuming 100% is my full-keyboard speed).

The Type Cover is also thin, but thicker than the Touch Cover (6mm). It also can be folded back, magnetically attached, only comes in 1 color: black with gray back. With it I can probably type fairly close to full speed. Both covers are reversible and using very strong magnetic connection - so strong that I can hang my Surface upside down holding the cover without it falling off.

If you hate the small keyboard or the feeling of it - you can still plug in your existing (mechanical) keyboard via USB - and it will always work.

BATTERY LIFE

The battery life for the Surface is great - pretty similar to iPad, about 9 hours plus. So basically it can easily last the whole day. I brought mine to the office last week and used it pretty much from the morning (around 9am) until end of day (around 4:30pm) without connecting it to its charger and with probably 10% left at the end of the day. This was using the Surface connected to an external monitor, with a USB mouse, and used to connect to a VM via remote desktop all day. You can see the setup in the picture on the right.

This kind of battery life is pretty awesome because that means that I don't have to bring my charger every time. I can feel securely that it won't be running out of juice and it will last the whole day - and eventually connect it to the charger at night.

The charger itself is great and it charges very very quickly. I'd say within a couple of hours or so - from almost empty to fully charged. I was certainly benefited from this when I had to charge my Surface on a rush before a trip. I don't know how many times I wished my phone or Tab or laptop would charge faster during a layover or at a friend's house or on the go. 

EXTERNAL CONNECTIONS and KICKSTAND

The Surface also has a micro HDMI out port and a USB port. This means I can connect my monitor if needed (like when I am at the office), connect it to a projector for presentation, connect thousands of peripherals (such as my phone, mice, keyboard, printers, etc). The USB port is really awesome - I don't know how many times I wished there is a USB port for my Tab or Nexus 7 - so I can print or connect a keyboard or even a phone or camera to transfer files.

In my "docked" setup at home,  I am connecting both the USB and the micro HDMI. The USB connects to a USB hub which then connects my external keyboard, mouse, card-reader (built-in to the monitor), and printer (not seen, under the desk).

The Surface also has a micro-SD slot, so you can easily expand your space by just adding a micro-SD card - which is not an option for iPad or my Tab.

The kick-stand is absolutely-without-a-doubt-ultra-useful. In my Tab or my friends' iPad, there are no built-in stand. I thought the iPad cover that folds into a stand was brilliant, but the built-in kick-stand for the Surface is more awesome - especially when it is being used in the productive mode like a laptop.

MULTIPLE USER ACCOUNTS

Unlike a smartphone, where single account suffice, I need a mobile productivity tool that can be used with multiple user accounts. I want my emails to be separated from my wife's, my gamer score to be left alone by my son, and my setup not to be messed with. This feature allows me to customize my user experience according to me needs and my wife to her needs, and still allow a guest account for everybody else.

Android supports that with the new 4.2 JellyBean update - but that will never come to my Tab. For iPad - this feature is non-existent.

The fact that I am also a Windows user on my desktop, this also means that my setup roams (using Microsoft Account), my music selection & playlists roam, etc - which is nice indeed.

OTHERS and NEED IMPROVEMENT

There are other things that make the Surface nice, such as the XBOX integration with Smartglass, XBOX music, Netflix, Hulu+, etc. Those are nice - but I'd say that those things are bonuses and not the main reason that attracted me to the Surface.

The price point for the Surface is also right on. I think spending $1,000 for a Macbook Air (or more for Pro) for what I need is unnecessary and heedless spending. It is the same price point of an iPad/Galaxy Tab, but double the storage, and much much more productive-capable. 

There are several things that Microsoft can still improve. The first one is related to my experience with the Touch Cover - where the seam came lose near the connector within 2 days after purchase. MS Support took care of me (with the help of Mr. Sinofsky via Twitter), but it was still disappointing that a product needs to be replaced that soon.

The magnetic connection to the charger is also not that great. It works awesomely when connected properly, but it is a bit of a pain to get it connected and won't snap with assurance (unlike the Covers) without really paying attention. The indicator light helps, but I wish it would have been easier.  

The Windows Store needs more apps. It has most of the apps that I need, but I think the ecosystem will get better with more apps. More games, more productivity tools, more niche apps.

IS IT FOR YOU?

Well, isn't it the question? Depending on your needs and what you already invested, the answer can be "yes" or "no".

Let's do the "NO"s first:
  • If you already have a recent ultra-book (like Lenovo X1/X220/etc, ASUS Zenbook series, etc). I'd just upgrade your ultra-book to Windows 8 and be done with it. 
  • If you are looking to have one-powerful-machine-to-do-them-all. If this is your need (running Photoshop, doing software dev, running VMs, playing Starcraft 2, etc) then get a robust powerful laptop instead. The Surface will never be a full laptop replacement. Or wait for the Surface PRO.
  • If you are already heavily invested in Apple lines (Macbook, iPad, iPhone with all their accessories). Unless you really want to "switch".
"YES" is a good answer if:
  •  Your needs are similar to mine: no laptop and have other computer to run other things, need MS Office, casual gamer, with no or little investments in other tablets. I think you'll love the Surface like I do.
  • Want to update or replace your old netbook and your needs are quite basic, such as email and browsing, social (Facebook, Twitter), MS Office. There is no sense of wasting $1,400 for a Macbook Pro or $1,000 for a Macbook Air if that's all pretty much you need your computer to do.
"Maybe" is in order:
  • If you are a gamer and primarily looking for a casual gaming device. An iPad or Nintendo 3DS or PSP Vita is probably better for that - or even an Android tablet like the Nexus 7. This of course can change as the Windows Store are getting more and more apps/games.  
  • If you are a heavy XBOX 360 user. Smartglass is awesome and being able to watch a movie on your XBOX and then continue it on your Surface is cool. Or getting extra contents of the movie you are watching, the games you are playing, etc. But, Smartglass app for iOS, Android, and WindowsPhone should be able to do the same thing.
  • If you want to switch from Apple to Windows. 
  • The rest of all other reasons that you can think of. 

-- read more and comment ...

Tuesday, October 30, 2012

Nest Thermostat

I heard about the Nest thermostat since last year and followed its development online through their blog and tech news sites. At the time, my digital thermostat was running fine, but it is always a pain to set/adjust, change the schedule, and non-intuitive - so my wife and my friends would usually ask me to adjust the temperature setting (instead of doing it themselves). So since that point on I have been on the lookout for a "better" thermostat and preferably an "internet connected" one. When I heard about pre-ordering a Nest, I did it right away and bought it when it came out last year.

Nest was not cheap ($250), especially compared to the typical programmable thermostat you find at the hardware store (~$75). Most people will say that "it's just a thermostat - why get an expensive one?" - and they are right in a way that the benefit is not that apparent right away. But after using it about a week or so, I never looked back.

So what sold me in the first place? After a year or so using it, what are my thoughts about it? This blog post will answer both questions.

Top notch website 

Have you ever been to the Nest's website? I urge you to go there - http://www.nest.com. Not only it is very well designed and easy to navigate - but it also absolutely informative. After reading some articles and watching some videos (all which are well made), it really answers all my questions and more. The videos put me at ease about doing a self installation or whether Nest will work my current furnace/AC with their compatibility checker.

Nest's website also explain how will Nest help you save energy consumption. So instead of some nebulous magic, Nest spells it out in its blog and articles.

There is also this "Living with Nest" page that pretty much explains how Nest actually work with your living habit, from the first day, the first week, first month, and eventually where Nest will stay out of your way and learn to understands your habits and adjust to them.

Killer Nest Web App

Nest also has a web app. So once you connect your Nest to your WiFi, register it and create an account, you can actually manage your Nest from anywhere in the world with an internet connection. 

This is a killer feature. I can be at the office or my friend's place and lower down the temperature at my house or even turn it off. I can pre-heat my home on my way back from vacation or cool it down, etc.

The web app also allows you to manage your Nest settings such as Auto-Away & Away Temperatures, Schedule, Learning, Lock, etc. No more fiddling directly in the thermostat once you got Nest connected to the internet. Everything can be done via the web admin tool.

The Web App also shows you the energy usage for the last 10 days. If you are saving energy, Nest will give you a "leaf" - which is also showed in the energy usage screen (a bit of gamification for energy saving).  This energy calculation is also adjusting depending on the temperature in your area as well as any adjustments you make during that day. In the detail view, it shows the time of the day when the heating/AC unit is working and the sum of hours for the whole day. This is really awesome in helping me out in saving energy - thus saving money. This feature along has helped and motivated me and my family to adjust our habit to save energy.

If you have multiple Nests in your home (to manage different units for multiple rooms etc), the web app also will identify each Nest individually, so you can control each of them independently.

I have to say that their website and web-support is superb. Probably the best I have seen and experienced. Sometimes I could not help showing it of to my friends. You can read more about their Web App here

Mobile device support

Nest also support mobile devices: iPhone, iPad, Android Phones and Tablets. Beyond the Web App, this is something that I probably use the most: adjusting temperature from my bed, or setting Away while on the road, or simply just checking the energy usage. The mobile app has most of the features that the web app has. Use the web app when sitting in front of the computer and use the mobile app when using the phone/tablet - awesome.

It took Nest a while to support Android tablet, but thankfully, the web app can be accessed quite easily using FireFox or Chrome using the desktop mode. Now, Nest released an update that works for my Galaxy Tab. Now - I am a Windows Phone use, so if you are reading this blog, Nest - can you make a Windows Phone app? You can read more about the mobile app for Nest here.

Nest Monthly Energy Report

This is an unexpected feature/support that surprises me - in a very very good way. I did not know this for in the beginning but was pleasantly surprised when I found out about it. Basically, Nest sends you an email every month summarizing your Nest usage report, compared to previous month, your "leaf" earning, etc - as well as links to new blog posts, tips, and other Nest benefits.

Unlike typical marketing emails or spams, this email from Nest is actually useful and beneficial to help you in using your Nest better and saving more energy.

Getting this email is like getting a pad in the back about your energy saving and also motivate you to maximize your Nest instead of just simply leaving it as a programmable thermostat. It gives you a picture of your energy usage pattern and helps you to make adjustments to be more efficient.

Software updates and support

I'd say that Nest has better software updates support and support in general than my Android devices. In 1 year, my Nest has had 2 software/firmware updates - both were feature packed updates (as well as fixes etc), huge improvements for the web app, and continuous updates for mobile device apps. All in all, excellent software and firmware support.

Secondly, their website is filled with articles, videos, and how-tos on about anything you can think of about Nest - from installation, troubleshooting, compatibility, and apps. This combined with responsive email & twitter support, and phone support - I certainly feel taken care of and satisfied. My hats off to them.

Oh yes, it comes with 2 years warranty too.
-- read more and comment ...

Tuesday, September 11, 2012

KOSS PortaPro Review

When my full-size headphone was snapped in half several years ago, I was scrambling to find a new one. I got a newer similar model from a local store for around $20 - but then after several months, its cable broke and became basically a mono headphone. A friend of mine then gave me a KOSS portaPro. The portaPro is an on-ear headphone, which I have never used before - since all of my headphones were always full-size ones. So understandably, I was very skeptical about it. But since my banged-up mono broken headphone was my only other option so I started using it. It has been about 3 years now since I started using the portaPro - and I am still using it as my trusty headphone.

Awesome sound!

I still distinctly remember when I used it for the first time to listen to music (prior to that I was using it for listening to podcast), I was really blown away. The sound quality coming out from this seemingly dinky headphone is amazing. Yes, my old full-size headphones were cheap (less than $40) and obviously not BOSE or Beats or any of those fancy ones. But seriously, this portaPro was awesome in terms of sound quality. Clear and powerful bass, superb mid-range, balanced treble. I would say that the sound quality of this portaPro probably competes with the mid-range ($75-$150) full-size headphones (with no noise-cancelling).

Cheap!

Since the portaPro was a gift, I did not know how much it cost - so I looked it up online on Amazon - and it was cheap (or at least A LOT cheaper than those fancy headphones). It is only about $45 (or less) - which is only 1/4 of the price of Beats ($199), 1/7 from Bose ($299), or 1/2 of any typical Sony headphones (~$100). Then you say it's because it's an on-ear, of course it's cheap. Yes, that might be true - but, I have tried other on-ear headphones (Logitech, Sony, Panasonic, Phillips) and none of them rival the sound quality of the portaPro. Truly, I think the combination of the sound quality and the economic price hits the sweet spot for me.

Small size

The advantage of having the portaPro vs a full-size headphone is the small size. It fits easily into my laptop bag, my travel bag, or even my wife's purse. This is something I could not do with my full-size headphone - which means if I decide to carry it, I practically have to have it hanging on my neck all the time if not using it. The portaPro also folds - makes it even smaller.

Last longer than my older headphones

This portaPro probably takes a lot more abuse than my older headphones, since I carry it in more places, shoved into my bags repeatedly, taken into trips, etc. But still looks great, functions normally, and really no complains from me. It looks flimsy, but it seems to last forever.

After 3 years, I had to replace the foaming pads that cover the drivers. The old foaming are torn. I bought the foaming on Amazon and they install easily - just remove the old foaming, put new ones one - took about 3 minutes and done. No glue, no stitching, no hassle.

Now it feels like it's a new headphone again.

So if you are in the market for a headphone, I suggest you check out KOSS portaPro. I totally love mine and I am guessing you will be pleasantly surprise by it.
-- read more and comment ...

Monday, August 20, 2012

Installing Jelly Bean ROM to Galaxy Tab 10.1

Samsung is taking its time in making ICS available for Galaxy Tab 10.1. Since JellyBean has come out since June 2012, it will probably taking another year to get a JellyBean update from Samsung. Looking in the internet, there are JB (JellyBean) custom ROMs available for the Tab. Since running Honeycomb is really aggravating, lagging, and quite a pain - I decided to take the plunge, root, and install custom ROM. Another option is to just get a Nexus 7 device - which will cost $200 more than installing custom ROM.

So in this post, I will describe the step by step process in installing JB ROM into my Tab (from stock Honeycomb). Please note that there are risks involved when one is doing this - that one may "brick" the device, or the installation may fail and require further troubleshooting etc. So do it on your own risk - no guarantees from me. My experience was that the installation was smooth and without any glitch whatsoever. Please do not skip any steps - also make sure your battery is full or almost full. Part of the steps is wiping the data. So if you do not want to lose any data, make sure you back it up first. 


1. ROOT (if yours is already rooted, you can skip this step)
Follow the steps from xda-developers here for rooting your device. The link above even has a video step-by-step guide.

2. OPTIONAL
At this point, I purchased ROM Manager App from the Google Play and update Clockwork Recovery to the latest version. 

3. INSTALL JB
  1. Download 2 sets of files from xda-developers, look for the section like the image on the right:
    1. The custom ROM (select the one that applies to you)
    2. Google Apps
  2. Copy the JB package (both the ROM and the Google Apps zip files) to your tablet’s internal Tab memory
  3. Turn off Tab.
  4. Go to ClockworkMod Recovery by turn on the Tab while holding the Volume Up button. 
  5. When the screen turns on, release the Power button but hold the Volume Up button until a menu shows up on the screen. 
  6. Press Volume Down to select the recovery mode icon and then, press the Volume Up button to enter recovery.
  7. Create a backup of your current ROM. Select "backup and restore". Select "backup" again. This will initiate the backup process. Once it's done, select "go back" to go back to main menu.
  8. Select "wipe data/factory reset" (and confirm)
  9. Select "wipe cache partition" (and confirm)
  10. Select "advanced" -- "wipe dalvik cache" (and confirm)
  11. Select "mounts and storage" -- "format / system" (and confirm). Once done, go back to main menu
  12. Select "install zip from sd card" -- "choose zip from sd card" and select the JB zip file and then confirm selection. The ROM installation will start. 
  13. Select "install zip from sd card" -- "choose zip from sd card" and select the Google Apps zip file and then confirm selection. The Apps installation will start
  14. Once done, select "go back" to the main menu and then select "reboot system now". DONE!
-- read more and comment ...

Wednesday, August 1, 2012

How to Remote Desktop to Win7 Home

In Windows 7, you can only Remote Desktop to Windows 7 Pro, Enterprise, or Ultimate. So, for most of home users who have Windows 7 Home Premium, there is no RDP into it. Just to be clear, all Windows 7 versions can initiate a connection, but only Pro, Ent, and Ultimate can host (read here for more).

So, one of my desktop that I use for Media Center is running Home Premium - and from time to time I do need to login to it to run updates, install drivers, troubleshoot, etc. Yes, I can do it from the TV, but that means putting keyboard & mouse - which I don't want to. There is a "hack" - which basically installing the host executable in the PC - but somehow when I tried, it did not work - although the forum chatter seems to indicate that this should be relatively easy. So anyway, I decided to just put a keyboard & mouse for the time being. Until about 2 months ago, when I discover Chrome Remote Desktop (BETA)!

What is Chrome Remote Desktop? It is a Chrome extension (a plugin for Chrome browser) that enables a computer (PC or Mac) with Chrome that also has the Chrome Remote Desktop extension installed to RDP to each other. So in my case, I just installed Chrome browser and then the extension in my Media Center PC. Run the extension, it asked for my permission, give it a password, and it's ready. Then I also installed the same extension on my main PC in my office. Run the extension and - VOILA - I can RDP to my Media Center PC (running Win7 Home Premium)!

Here is the step-by-step:
If you do not have Chrome installed, go download & install it here. Go to Chrome Web Store and search for "remote desktop" and you should see result like this:


Click "ADD TO CHROME" and the download & installation of the extension should start. When it's done, you will see the Chrome Remote Desktop app icon shows up in your Chrome Apps.

Running the app for the first time, it will prompt you for allowing it access your computer. Click "Continue" here and then "Allow access" on the next screen.


Once you allow access, now it's ready to be setup for use. Click the "Get started" button.

To enable your computer to be connected to, click the "Enable remote connections" button. You will be asked to enter a PIN, which you will use every time you need to connect to the computer.


That is it - now it's ready! If you install and enable remote connections in more than 1 computer, they will get added the list automatically. The list is tied to your Google account (GMail/Google+). 

Now do the same steps above in a different computer, now you have 2 PCs that are able to RDP to each other via web. Here is the kicker: since this is via port 80, or http, this means one does not need to tweak firewall rules, install special VPN software, get dynamic DNS address, etc. As long as the computer is turned on, the Chrome extension will manage all the plumbing underneath. All you need is a broadband connection, Chrome browser & extension, and your setup/PIN.
-- read more and comment ...

Sunday, July 29, 2012

I am going PREPAID cellular plan ...

Currently, with T-Mobile, I am paying $49.99 family plan, which gives me 2 lines, 500 minutes, free T-Mobile to T-Mobile calls, free nights and weekends. I pay extra for my 5GB data plan ($25) and my wife is using the 200MB ($12). Plus another $4.99 for each messaging plan. So overall, I am paying around $100 per month. My family plan is an old plan (grandfathered), so I also get the full discount for new devices in I choose to extend the 2 year contract.

Now, my plan is not bad at all, $100 for 2 persons - it is actually considered to be quite awesome - since most my friends who have newer plans on T-Mobile (or other carriers) usually pay between $80 or more per person. So basically almost half of what most of my friends with smartphones are paying.

Now, let's consider the overall ownership cost over 2 years - since that is the length of the standard cellphone contract. If I pay $279 for each device (assuming Samsung Galaxy S3), add the monthly payment, over 2 years, I am paying about $2,950+. Of course, that is cheaper than my friends who is paying $110 per month for their family plan. But, I plan to reduce the overall cost some more. How? By going PREPAID!



To each their own, but after assessing my needs (getting some stats from my bills for the last 6+ months), I think getting the Monthly 4G plan for $30 will be sufficient for me (and my wife). This plan will get me 5GB data, unlimited text and 100 minutes voice. So for both of us, that will be $60 per month - without contract. There is a drawback to this - that if I buy a new device, I have to pay full price without any discount. So that means $600 for Samsung Galaxy S3. So in short, no contract, I am paying cheaper per month, but paying 3+ times IF buying a new device. But what is the overall 2 years cost of ownership? Let's assume we both buy GS3 and paying $30 Monthly 4G plan - which totaling $2,640 - a saving of $300 over 2 years. It looks a small saving on paper, but in reality, it is actually a pretty good deal. Let's list down the benefit:
  • No contract. Only pay as needed. So the potential saving here is actually larger. If I think for the next month that I will be in a WiFi bubble, I may switch to $10 pay as you go ans save some more. If I am traveling, I can switch back to the Monthly 4G etc. 
  • In a contract deal, if I am not getting a new device once my contract is up - this means that I am giving free money to the carrier, since the cost of the phone is factored in the monthly bill. With no contract prepaid, I don't have that burden. This also means that if I elect to switch carrier or get a new cellphone/device, I don't have to wait until the contract is done.
  • My wife data plan will get an upgrade, from 200MB per month to 5GB. Now, I am assuming that she needs that 5GB - if she does not (because of WiFi), she can switch to a cheaper pay-by-the-day based on usage.
  • From 300 texts per month to unlimited texts. This is also assuming we need that. I mostly do not need it, but there is no plan with voice and data only. 
  • If spending $100 or more in 1 year, get a Gold status, which means 15% more minutes and your minutes can be used for a full year (instead of the regular 90 days).
  • No overages!
So overall, I am fairly convinced (for now) that the prepaid/Monthly 4G will save me some money but still fulfill my need for mobile communication.

T-Mobile also has Pay-As-You-Go plan (for voice prepaid) and Pay-By-The-Day plan (for use only when needed). Depending on your needs, these plans can potentially be cheaper than the Monthly 4G plan that I am planning to use.

 

 
Microsoft Windows engineer John Lam also has a similar story in his blog
-- read more and comment ...