Setup a private nuget server

Deploy nuget server project

Nuget Server package on nuget.org:
https://www.nuget.org/packages/NuGet.Server/2.11.3/

The project source:
https://github.com/NuGet/NuGet.Server

deploy the project to IIS, and configure the web.config

1. set the apiKey for pushing packages to the server
2. set packagesPath to store all the packages, the default will be ~/Packages (you need to give write permissions for the app pool user)

Pushing packages

1. using nuget CLI

https://docs.microsoft.com/en-us/nuget/tools/nuget-exe-cli-reference

nuget.exe push -Source {NuGet package source URL} -ApiKey key {your_package}.nupkg

2. using dotnet core CLI

https://docs.microsoft.com/en-us/dotnet/articles/core/tools/dotnet-nuget-push


dotnet pack --configuration release
dotnet nuget push foo.nupkg -k 4003d786-cc37-4004-bfdf-c4f3e8ef9b3a -s http://customsource/

Enable authentication for accessing nuget server

1. enable windows authentication on the server site on IIS
2. create a windows user
3. adding the repository source with username and password (using nuget CLI), it will be saved into the global nuget.config file. (normally in Nuget has a global nuget.config, saved in \Users\%AppUSer%\AppData\Roaming\NuGet)
nuget.exe sources add -name {feed name} -source {feed URL} -username {username} -password {PAT} -StorePasswordInClearText

If you don’t have the username & password, it will return a 401 unauthorized error. In visual studio, it will prompt a dialog asking for credentials.

Restore nuget packages using nuget config per solution

If you work on a new machine, and checkout source code of a project, you will need to configure the nuget source, username, password etc. To enable developers restore the packages and build the project without any hassles, we can create a nuget.config per solution.


<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSourceCredentials>
<AWS_x0020_Nuget>
<add key="Username" value="spnugetuser" />
<add key="ClearTextPassword" value="SearchParty2017" />
</AWS_x0020_Nuget>
</packageSourceCredentials>
<packageSources>
<add key="AWS Nuget" value="http://nuget.searchparty.com/nuget" />
</packageSources>

</configuration>

Then you can call

nuget restore

or

dotnet restore

Advertisements

SelectListItem Helper to create selectlistitems from enum

first, we create a extension method to get the descriptions of enum values.

public static string ToDescription&lt;T&gt;(this T enumValue)
            where T : struct, IConvertible, IComparable, IFormattable // Criteria for Enums
        {
            var fieldInfo = enumValue.GetType().GetField(enumValue.ToString());
            var attributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast&lt;DescriptionAttribute&gt;().ToList();
            return attributes.Any() ? attributes.First().Description : enumValue.ToLabel();
        }

second, create the selectlistitems from the enum.

public static List&lt;SelectListItem&gt; GetItemsForEnum&lt;TEnum&gt;(int? selectedValue = null, string defaultText = &quot;&quot;)
            where TEnum : struct, IConvertible, IComparable, IFormattable // Criteria for Enums
        {
            var results = new List&lt;SelectListItem&gt;();
            var values = Enum.GetValues(typeof (TEnum));
 
            if (!string.IsNullOrEmpty(defaultText ))
                results.Add(new SelectListItem { Text = defaultText , Value = &quot;&quot; });
 
            foreach (var value in values)
            {
                var name = ((TEnum)value).ToDescription();
                var selected = (selectedValue != null) &amp;&amp; selectedValue.Equals((int)value);
                results.Add(new SelectListItem { Text = name, Value = ((int)value).ToString(), Selected = selected });
            }
            return results;
        }

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 &lt;= 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&lt;User&gt;
{
    public UserMapping()
    {
        HasKey(p =&gt; p.Id);
        Property(p =&gt; p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName(&quot;Id&quot;);
        Property(p =&gt; p.Name).HasMaxLength(100);
 
        ToTable(&quot;User&quot;);
    }
}
 
public class UserContext : DbContext
{
    public DbSet&lt;User&gt; Users { get; set; }
 
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new UserMapping());
    }
 
    public UserRepo UserRepo
    {
        get
        {
            return new UserRepo(this);
        }
    }
}

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.

JQuery ajax request not working in IE9

I have some ajax get request on the web page and it works perfectly fine in Firefox and Chrome. But it is not working with IE at all. After some research, I found out I need to specify the dataType and allow cross domain.

Before I have,

            $.ajax({
                type: "GET",
                url: 'http://example.com/abc',
                error: function (xhr, statusText) {
						//log error                    
                },
                success: function (data) {                    
						//process data
                }
            });
        }

After I have specify the dataType, contentType and crossDomain, it works straight away.

            $.ajax({
                dataType: "jsonp",
                contentType: "text/json; charset=utf-8",
                crossDomain: true,
                type: "GET",
                url: 'http://example.com/abc',
                error: function (xhr, statusText) {
						//log error                    
                },
                success: function (data) {                    
						//process data
                }
            });
        }

This little tip saved my day!

Create a custom HtmlHelper.TextBoxFor() to display label text as “placeholder” integrated with twitter bootstrap

We have TextBoxFor() and LabelFor() in MVC, they are binded with my model. If you are using twitter bootstrap and want to display the model property label as a placeholder in textboxes, what should you do.

Here is what I have done.

My model:

public class User
{
    [Display(Name="User name")]
    public string Username {get;set;}
}

Normally in the view we put,

@model User

<ol>
    <li>
        @Html.LabelFor(p=&gt;p.Username)
        @Html.TextBoxFor(p=&gt;p.Username)
    </li>
</ol>

image

If you want to display it as placeholder in twitter bootstrap,

image

Let’s create a TextBoxPlaceHolderFor() extension method of the htmlhelpers, remember to reference the namespace,

using System.Web.Mvc.Html;

So you can re-use the HtmlHelper.TextBoxFor() in your TextBoxPlaceHolderFor() method.

Here is the code with three overloads,

public static class TextBoxForExtensions
    {
        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression, object htmlAttributes)
        {
            var dict = new RouteValueDictionary(htmlAttributes);
            return html.TextBoxPlaceHolderFor(expression, dict);
        }
        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression)
        {
            var htmlAttributes = new Dictionary();
            return html.TextBoxPlaceHolderFor(expression, htmlAttributes);
        }

        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression, IDictionary htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (!String.IsNullOrEmpty(labelText))
            {
                if (htmlAttributes == null )
                {
                    htmlAttributes = new Dictionary();
                }
                htmlAttributes.Add("placeholder", labelText);
            }
            return html.TextBoxFor(expression, htmlAttributes);
        }

    }

When you call the methods you can do,

@Html.TextBoxPlaceHolderFor(m => m.UserName)

//or 

@Html.TextBoxPlaceHolderFor(m => m.UserName, new { @class="form-field" })


You are done. Now get your form neat and clean using the TextBoxPlaceHolderFor().

Cool features of ASP.NET MVC 4

I have been played around with MVC 4 for a while, now it is finally live.

I found there are quite a few great features included in MVC 4. Here I am going to chat about my favourite ones.

1. Extension to razor scripts

The default view engine supports, HTML attributes starting with ~/ will now be automatically replaced with the application root, developers no longer need to use the Url.Content function.

Another big time saver is conditional attributes. If the @styleClass variable in class="@styleClass" is null, the entirely attribute is automatically removed for you.

2. CSS & JS bundles

a. Instead of listing each css/js file needed by the client, I can use bundle to point to a specific folder.

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                 "~/Scripts/jquery-{version}.js"));

b. You can control the bundling and minification in web.config by turning compilation debug = true or false. Or you can override it from RegisterBundles() method

public static void RegisterBundles(BundleCollection bundles)
{
    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                 "~/Scripts/jquery-{version}.js"));

    // Code removed for clarity.
    BundleTable.EnableOptimizations = true;
}

c. bundle is cached on the browser for one year. It sets the http header one year from when the bundle is created.

d. It supports LESS style sheet, by adding “dotless” NuGet package to your project.

implement the IBundleTransform interface,

using System.Web.Optimization;

public class LessTransform : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse response)
    {
        response.Content = dotless.Core.Less.Parse(response.Content);
        response.ContentType = "text/css";
    }
}

and create the bundle of LESS files and CssMinify transform in RegisterBundles() method in BundleConfig.cs file.

var lessBundle = new Bundle("~/My/Less").IncludeDirectory("~/My", "*.less");
lessBundle.Transforms.Add(new LessTransform());
lessBundle.Transforms.Add(new CssMinify());
bundles.Add(lessBundle);

Add it to the view,

 @Styles.Render("~/My/Less");

3. WebAPI

  • Allows the building of HTTP APIs that automatically can serialize to the format requested such as xml/json
  • Support oData so that if the returned object support iQueryable parameters can be added to the return to subsets (paging, top n etc)
  • Allows hosting of WebAPI either in an area web, console, service, windows 8
  • Allows results to easily return custom HTTP response codes and location header for querying data

4. Mobile view engine,

  • Supports adaptive rendering using media queries
  • Allows creation of different mobile specific views
  • Supports mobile templating engines such as jQuery mobile
  • 5. Real time communication

  • Uses SignalR.
  • .NET way of doing what everyone is using node.js for.
  • Allows the server to call JavaScript, e.g pushes a call to the browser thus executing some JavaScript