Override EF 5 database mapping

public class UserRepo
{
    private UserContext _context;
    public UserRepo(UserContext context)
    {
        _context = context;
    }
 
    public User Save(User user)
    {
        if (user.Id <= 0)
        {
            _context.Users.Add(user);
        }
        else
        {
            _context.Users.Attach(user);
        }
        return user;
    }
}
 
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}
 
public class UserMapping : EntityTypeConfiguration<User>
{
    public UserMapping()
    {
        HasKey(p => p.Id);
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("Id");
        Property(p => p.Name).HasMaxLength(100);
 
        ToTable("User");
    }
}
 
public class UserContext : DbContext
{
    public DbSet<User> Users { get; set; }
 
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new UserMapping());
    }
 
    public UserRepo UserRepo
    {
        get
        {
            return new UserRepo(this);
        }
    }
}

SQL script to view sizes of all tables

SELECT
    t.NAME AS TableName,
    p.rows AS RowCounts,
    SUM(a.total_pages) * 8 AS TotalSpaceKB, 
    SUM(a.used_pages) * 8 AS UsedSpaceKB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
FROM
    sys.tables t
INNER JOIN     
    sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
    sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
    sys.allocation_units a ON p.partition_id = a.container_id
WHERE
    t.NAME NOT LIKE 'dt%'
    AND t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
GROUP BY
    t.Name, p.Rows
ORDER BY
    t.Name

How OAuthSecurity to obtain emails for different oauth clients, but Microsoft Client doesn’t return email, it didn’t include scope “wl.emails”

I have been playing with MVC 4, SimpleMembership, WebSecurity and OAuthWebSecurity for a while now. I can see the idea of OAuthWebsecurity is a wrapper around DotNetOpenAuth. It registers the clients in AuthConfig.cs file. That really works and helps me reduce heaps lines of code.

But soon enough,

1. I find only Google client returns an email as a username. I mean email is still quite important for the newsletters or system emails etc.

2. For Twitter, they don’t provide email via OAuth or any API, which is a shame. But I don’t complain. (Maybe they have changed without my awareness). So we don’t do anything with it.

3. For Facebook, with DotNetOpenAuth and OAuth2, it actually includes the scope “email” and returns email in the “ExtraData” dictionary.

So in the ExternalLoginCallback() method, you can find this line:

            AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));

If you query result.ExtraData[“username”], that contains the user’s email in it.

4. For Microsoft, it is a nightmare, I find DotNetOpenAuth didn’t even include the scope “wl.emails” in their request at all. I am disappointed, but it is not the end of the world.

I am trying to create a Custom Authentication Client, to retrieve Microsoft emails.

First, create a class MicrosoftScopedClient and implement IAuthenticationClient interface. You must implement two methods of that interface.

    public class MicrosoftScopedClient : IAuthenticationClient
    {

        public void RequestAuthentication(HttpContextBase context, Uri returnUrl)
        {
  
        }

        public AuthenticationResult VerifyAuthentication(HttpContextBase context)
        {
        }
    }

The next step is to build the authentication url in method “RequestAuthentication()”,

public void RequestAuthentication(HttpContextBase context, Uri returnUrl)
        {
            string url = baseUrl + "?client_id=" + clientId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + HttpUtility.UrlEncode(scope) + "&response_type=code";
            context.Response.Redirect(url);
        }

Then I build VerifyAuthentication() method to receive the authentication code and send requests to obtain the access_token, and then use the access_token to request for the profiles.

public AuthenticationResult VerifyAuthentication(HttpContextBase context)
        {
            string code = context.Request.QueryString["code"];

            string rawUrl = context.Request.Url.ToString();
            //From this we need to remove code portion
            rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");

            IDictionary userData = GetUserData(code, rawUrl);

            if (userData == null)
                return new AuthenticationResult(false, ProviderName, null, null, null);

            string id = userData["id"];
            string username = userData["email"];
            userData.Remove("id");
            userData.Remove("email");

            AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
            return result;
        }

After I have done the work of building the MicrosoftScopedClient, I need to register it in the AuthConfig.cs, now we can feel free to pass any scopes there =)

            OAuthWebSecurity.RegisterClient(new MicrosoftScopedClient(ConfigurationManager.AppSettings["Microsoft.ClientId"].ToString(),
                ConfigurationManager.AppSettings["Microsoft.Secret"].ToString(),
                "wl.basic wl.emails"
                )
                , "Microsoft", null);

Below is a full copy of the MicrosoftScopedClient,

using DotNetOpenAuth.AspNet;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.Messaging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace MicrosoftClient.Filters
{
    public class MicrosoftScopedClient : IAuthenticationClient
    {
        private string clientId;
        private string clientSecret;
        private string scope;

        private const string baseUrl = "https://login.live.com/oauth20_authorize.srf";
        private const string tokenUrl = "https://login.live.com/oauth20_token.srf";

        public MicrosoftScopedClient(string clientId, string clientSecret, string scope)
        {
            this.clientId = clientId;
            this.clientSecret = clientSecret;
            this.scope = scope;
        }

        public string ProviderName
        {
            get { return "Microsoft"; }
        }

        public void RequestAuthentication(HttpContextBase context, Uri returnUrl)
        {
            string url = baseUrl + "?client_id=" + clientId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + HttpUtility.UrlEncode(scope) + "&response_type=code";
            context.Response.Redirect(url);
        }

        public AuthenticationResult VerifyAuthentication(HttpContextBase context)
        {
            string code = context.Request.QueryString["code"];

            string rawUrl = context.Request.Url.ToString();
            //From this we need to remove code portion
            rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");

            IDictionary userData = GetUserData(code, rawUrl);

            if (userData == null)
                return new AuthenticationResult(false, ProviderName, null, null, null);

            string id = userData["id"];
            string username = userData["email"];
            userData.Remove("id");
            userData.Remove("email");

            AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
            return result;
        }

        private IDictionary GetUserData(string accessCode, string redirectURI)
        {
            string token = QueryAccessToken(redirectURI, accessCode);
            if (token == null || token == "")
            {
                return null;
            } 
            var userData = GetUserData(token);
            return userData;
        }

        private IDictionary GetUserData(string accessToken)
        {
            ExtendedMicrosoftClientUserData graph;
            var request =
                WebRequest.Create(
                    "https://apis.live.net/v5.0/me?access_token=" + EscapeUriDataStringRfc3986(accessToken));
            using (var response = request.GetResponse())
            {
                using (var responseStream = response.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(responseStream))
                    {
                        string data = sr.ReadToEnd();
                        graph = JsonConvert.DeserializeObject(data);
                    }
                }
            }

            var userData = new Dictionary();
            userData.Add("id", graph.Id);
            userData.Add("username", graph.Name);
            userData.Add("name", graph.Name);
            userData.Add("link", graph.Link == null ? null : graph.Link.AbsoluteUri);
            userData.Add("gender", graph.Gender);
            userData.Add("firstname", graph.FirstName);
            userData.Add("lastname", graph.LastName);
            userData.Add("email", graph.Emails.Preferred);
            return userData;
        }

        private string QueryAccessToken(string returnUrl, string authorizationCode)
        {
            var entity =
                CreateQueryString(
                    new Dictionary {
						{ "client_id", this.clientId },
						{ "redirect_uri", returnUrl },
						{ "client_secret", this.clientSecret},
						{ "code", authorizationCode },
						{ "grant_type", "authorization_code" },
					});

            WebRequest tokenRequest = WebRequest.Create(tokenUrl);
            tokenRequest.ContentType = "application/x-www-form-urlencoded";
            tokenRequest.ContentLength = entity.Length;
            tokenRequest.Method = "POST";

            using (Stream requestStream = tokenRequest.GetRequestStream())
            {
                var writer = new StreamWriter(requestStream);
                writer.Write(entity);
                writer.Flush();
            }

            HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();
            if (tokenResponse.StatusCode == HttpStatusCode.OK)
            {
                using (Stream responseStream = tokenResponse.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(responseStream))
                    {
                        string data = sr.ReadToEnd();
                        var tokenData = JsonConvert.DeserializeObject(data);
                        if (tokenData != null)
                        {
                            return tokenData.AccessToken;
                        }
                    }
                }
            }

            return null;
        }

        private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
        private static string EscapeUriDataStringRfc3986(string value)
        {
            StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));

            // Upgrade the escaping to RFC 3986, if necessary.
            for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
            {
                escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
            }

            // Return the fully-RFC3986-escaped string.
            return escaped.ToString();
        }

        private static string CreateQueryString(IEnumerable<KeyValuePair> args)
        {
            if (!args.Any())
            {
                return string.Empty;
            }
            StringBuilder sb = new StringBuilder(args.Count() * 10);

            foreach (var p in args)
            {
                sb.Append(EscapeUriDataStringRfc3986(p.Key));
                sb.Append('=');
                sb.Append(EscapeUriDataStringRfc3986(p.Value));
                sb.Append('&');
            }
            sb.Length--; // remove trailing &

            return sb.ToString();
        }

        protected class ExtendedMicrosoftClientUserData
        {
            public string FirstName { get; set; }
            public string Gender { get; set; }
            public string Id { get; set; }
            public string LastName { get; set; }
            public Uri Link { get; set; }
            public string Name { get; set; }
            public Emails Emails { get; set; }
        }

        protected class Emails
        {
            public string Preferred { get; set; }
            public string Account { get; set; }
            public string Personal { get; set; }
            public string Business { get; set; }
        }
    }
}

Conditional required validation or field mandatory depends on another field MVC 4

I have experienced this situation that when I need to make a field mandatory if the user has entered a value in another field (or a particular value for that field).

Here is my example, I have two radio buttons says “Do you have the purchase receipt?” with options “yes” or “no”. If the user has selected “yes”, I need them to specify the date of the purchase as well.

image

Now the headache is, I can’t make “Purchase Date” required field. Because if user selects “no”, they don’t need to enter the “Purchase Date”. After I did some research and lookup from the internet. I find this solution on StackOverflow. It has a few bugs and I fixed them and shared over here in my blog.

I created a RequiredIfAttribute,

public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
    protected RequiredAttribute _innerAttribute;

    public string DependentProperty { get; set; }
    public object TargetValue { get; set; }

    public bool AllowEmptyStrings
    {
        get
        {
            return _innerAttribute.AllowEmptyStrings;
        }
        set
        {
            _innerAttribute.AllowEmptyStrings = value;
        }
    }

    public RequiredIfAttribute(string dependentProperty, object targetValue)
    {
        _innerAttribute = new RequiredAttribute();
        DependentProperty = dependentProperty;
        TargetValue = targetValue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // get a reference to the property this validation depends upon
        var containerType = validationContext.ObjectInstance.GetType();
        var field = containerType.GetProperty(DependentProperty);

        if (field != null)
        {
            // get the value of the dependent property
            var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
            // trim spaces of dependent value
            if (dependentValue != null && dependentValue is string)
            {
                dependentValue = (dependentValue as string).Trim();

                if (!AllowEmptyStrings && (dependentValue as string).Length == 0)
                {
                    dependentValue = null;
                }
            }

            // compare the value against the target value
            if ((dependentValue == null && TargetValue == null) ||
                (dependentValue != null && (TargetValue.Equals("*") || dependentValue.Equals(TargetValue))))
            {
                // match => means we should try validating this field
                if (!_innerAttribute.IsValid(value))
                    // validation failed - return an error
                    return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), new[] { validationContext.MemberName });
            }
        }

        return ValidationResult.Success;
    }

    public virtual IEnumerable&lt;ModelClientValidationRule&gt; GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "requiredif",
        };

        string depProp = BuildDependentPropertyId(metadata, context as ViewContext);

        // find the value on the control we depend on;
        // if it's a bool, format it javascript style 
        // (the default is True or False!)
        string targetValue = (TargetValue ?? "").ToString();
        if (TargetValue is bool)
            targetValue = targetValue.ToLower();

        rule.ValidationParameters.Add("dependentproperty", depProp);
        rule.ValidationParameters.Add("targetvalue", targetValue);

        yield return rule;
    }

    private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
    {
        // build the ID of the property
        string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(DependentProperty);
        // unfortunately this will have the name of the current field appended to the beginning,
        // because the TemplateInfo's context has had this fieldname appended to it. Instead, we
        // want to get the context as though it was one level higher (i.e. outside the current property,
        // which is the containing object, and hence the same level as the dependent property.
        var thisField = metadata.PropertyName + "_";
        if (depProp.StartsWith(thisField))
            // strip it off again
            depProp = depProp.Substring(thisField.Length);
        return depProp;
    }
}

2. Create js validation and js unobtrusive validation, (I put them in document.ready() callback)

$.validator.addMethod('requiredif',
    function (value, element, parameters) {
        var id = '#' + parameters['dependentproperty'];

        // get the target value (as a string, 
        // as that's what actual value will be)
        var targetvalue = parameters['targetvalue'];
        targetvalue = (targetvalue == null ? '' : targetvalue).toString();

        // get the actual value of the target control
        // note - this probably needs to cater for more 
        // control types, e.g. radios
        var control = $(id);
        var controltype = control.attr('type');
        var actualvalue =
            (controltype === 'checkbox' ||  controltype === 'radio')  ?
            control.attr('checked').toString() :
            control.val();

        // if the condition is true, reuse the existing 
        // required field validator functionality
        if ($.trim(targetvalue) === $.trim(actualvalue) || ($.trim(targetvalue) === '*' && $.trim(actualvalue) !== ''))
            return $.validator.methods.required.call(
              this, value, element, parameters);

        return true;
    });

$.validator.unobtrusive.adapters.add(
    'requiredif',
    ['dependentproperty', 'targetvalue'],
    function (options) {
        options.rules['requiredif'] = {
            dependentproperty: options.params['dependentproperty'],
            targetvalue: options.params['targetvalue']
        };
        options.messages['requiredif'] = options.message;
    });

3. For the Model,

        [Required]
        public bool HasReceipt { get; set; }

         [RequiredIf("HasReceipt", true, ErrorMessage = "You must enter purchase date")]
        [Display(Name="Purchase Date")]
        public DateTime? PurchaseDate { get; set; }

4. When reference this validation js, I notice that it only works before the unobtrusive javascript,


    <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
    <script src="~/Scripts/jquery.validate.min.js"></script>
    <script src="~/Scripts/jquery.validate.requiredif.js"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

5. Now you have your conditional required validations.

There are many scenarios like this,

e.g.

If you have entered an Address field, you must enter Suburb, City.

If you have selected yes for a credit card, you must enter the credit card digits.

If you have subscribed a service, you must enter a valid email.

And many more.

With YouTube new interface, how to copy YouTube playlist from another user?

With the old YouTube interface, when you play a playlist, there is an option at the bottom “Save as a new playlist”. After YouTube has updated to a completely new interface, this feature is gone.

For myself I am feeling frustrated, so here comes a weekend project youtubeplaylist.net. It provides you several ways of copying playlists with YouTube.

image

1. You can search a playlist.

Enter a keyword, it returns the same result from search in YouTube. Or if you know your YouTube playlist URL, you can copy to the search box.

image

2. You can copy a playlists with selected videos.

After you have selected the playlist, you come to this page and see all the videos from this playlist. Now you can select all videos or only the videos you wanted to copy. (You can preview the video by click the magnifier icon on left corner of the video tile.)  When you are ready, click continue to proceed.

e.g. a NBA playlist

image

3a. Before you get to the “Target Playlist” page, we will ask you for the permission of accessing your YouTube content, please select “Allow” to proceed.

image

3b. Select a target playlist (where you want to copy the videos), it can be a brand new playlist in your YouTube. Or the videos can be added to your existing playlists. It also works with your “Favourite’s”, “Watch Later”  and “Likes” lists. (The pink tiles are the special lists, and grey tiles are the normal playlists you have in YouTube )

 image

4. Make the selection above, and you are done. Just wait for the service to copy all the videos to the targeted playlist and you can share them or just go to YouTube and enjoy them.

image

5. You can also manage your playlists with youtubeplaylist.net, register and login, on the top right corner, you can see your logged in email and click it to see a list of options. Click “Manage Playlist”.

image

You can bulk delete your YouTube playlists. (Please be caution, if you delete a playlist, all the videos under that playlist will be deleted.)

image

So please enjoy youtubeplaylist.net, and if you love it please share with your friends and support us. We will do better to make our lives easier.

Find us on Facebook,  http://www.facebook.com/youtubeplaylist.net

Sending email from gmail smtp server

You can do smtp server setting in either web.config or backend code.

Web.config

<mailSettings>
      <smtp>
            <network host="smtp.gmail.com" userName="your@gmail.com" password="yourpassword" port="587" enableSsl="true"/>
      </smtp>
    </mailSettings>

C#,

           SmtpClient smtp = new SmtpClient();
            var fromAddress = new MailAddress"your@gmail.com", "Your Name");
            const string fromPassword = "yourpassword";            
smtp = new SmtpClient
            {
                Host = "smtp.gmail.com",
                Port = 587,
                EnableSsl = true,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
            };

It is very handy if you don’t have a smtp server setup.

Ninject with custom attribute and filter

I am implementing a custom credit check filter for a controller action. I use Ninject as my Ioc container. First, create a action filter and implement IActionFilter interface, implement the OnActionExecuted() and OnActionExecuting() methods. Within the credit checking functions, it needs a CreditService to check against the repository and database. So I have created the parameterized constructor using Ninject to inject the CreditService object.

 public class CreditCheckFilter : IActionFilter
    {
        private ICreditService _creditService { get; set; }

        public CreditCheckFilter(ICreditService myService)
        {
            _creditService = myService;
        }

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {

        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var userName = filterContext.HttpContext.User.Identity.Name;
            var bal = _creditService.GetTokenBalance(WebSecurity.GetUserId(userName));
            if (bal <= 0)
            {
                RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
                redirectTargetDictionary.Add("action", "GetTokens");
                redirectTargetDictionary.Add("controller", "Account");
                filterContext.Result = new RedirectToRouteResult(redirectTargetDictionary);
               
            }
        }
    }

Now Let’s create a CreditCheckAttribute that implements FilterAttribute, this is the attribute we put against the controller action,

    public class CreditCheckAttribute : FilterAttribute { }

Now, we need to use Ninject BindFilter() method to bind our CreditCheckFilter, when a controller action has the attribute CreditCheckAttribute. In the ninject kernel, do this,

            kernel.BindFilter(FilterScope.Action, 0).WhenActionMethodHas();

The last step is to put CreditCheckAttribute() against the controller action,

        [CreditCheckAttribute]
        public ActionResult Playlist(TestViewModel model)
        {
            return View(model);
        }