Category Archives: asp.net in c#

Angular 2 Tutorial – movies App

Last time I wrote a blog created the MovieAPI and now I am using that API as back end services to create an angular 2 app.

the final source code could find at my github repository.  here is the link download

For the MovieAPI open it, build it will install the packages automatically, then run the service first.

After the service is done, go to the angular2 folder, open command window

–run npm install,

— npm start

It will look like this:

home

create.PNG

added

Step 1 : Setting up your angular 2 application, I already have a blog  click here to found it. Or also could download from the source file from https://github.com/angular/quickstart/blob/master/README.md but it add a lot of unnecessary setting, probably confuse for beginning learner.

It was better do few times manually setting it up to get understand all of the necessary for start anguar 2 application.

Step 2:  In angualr 2 everything is component, for me normally like to create the service first. I just create a file named movies.service.ts under folder movies


import {Injectable} from 'angular2/core';
import {IMovie} from './movie';
import {Http, RequestOptions , Response, Headers} from 'angular2/http';
import {Observable} from 'rxjs/Observable';

@Injectable() 
export class MovieService {   
    private _movieUrl = 'http://localhost:53871/api/movies';

    constructor(private _http: Http) { }

    getMovies(): Observable<IMovie[]> {
        return this._http.get(this._movieUrl)
            .map((response: Response) => <IMovie[]>response.json()) //arrow function, tranceform response to json object and is array of IMovie[]
            .do(data => console.log("All: " + JSON.stringify(data))) //for debug
            .catch(this.handleError);
    }


    createMovie(movie: IMovie): Observable {
        let body = JSON.stringify(movie); 
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this._http.post(this._movieUrl, body, options)               
                .map(this.extractData)
                .catch(this.handleError);
    }

    private extractData(res: Response) {
        let body = res.json();
        return body.data || {};
    }

    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

In there MovieService I have a IMovie here is my movie.ts I put it in the same folder with MovieService .


//interface won't compile to js file, it just for typescript do type checking
 export interface IMovie { 
    movieId: number;
    title: string;
    genre: string;
    classification: string;
    releaseDate: number;
    rating: number;
    cast: string[];

}


export class Movie implements IMovie {
    movieId: number;
    title: string;
    genre: string;
    classification: string;
    releaseDate: number;
    rating: number;
    cast: string[];
}

Step 3: Inject the services.
Go to the app.component.ts this is our container component, for anguar 2 only need inject once, then all of the nest container could using it ( by import it ).

Inject a service has two steps:

1) import the file into the app.component,

2) Inject it as providers

here is my app.component.ts

angular2_injection

Step 4: Create the movies-list.component.ts


//for using component, simply import it into this file
import {Component, OnInit} from 'angular2/core';
import { ROUTER_DIRECTIVES } from 'angular2/router';

import {IMovie} from './movie';
import {MovieService} from './movies.service';
import {MovieFilterPipe} from './movie-filter.pipe';
import { StarComponent } from '../shared/star.component';

@Component({
    selector: 'sz-movies',
    templateUrl: 'app/movies/movies-list.component.html',
    styleUrls: ['app/movies/movies-list.component.css'],  
    pipes: [MovieFilterPipe],
    directives: [StarComponent, ROUTER_DIRECTIVES]
   
})
export class MovieListComponent implements OnInit {
    pageTitle: string = 'Movie List';   
   
    listFilter: string;
    errorMessage: string;
    movies: IMovie[];


    constructor(private _movieService: MovieService) {

    }
    
    ngOnInit(): void {
        this._movieService.getMovies()
            .subscribe(
            movies => this.movies = movies,
            error => this.errorMessage = error);
    }

}

here is the movies-list.component.htmlmovies_html

 

for this component I have a nested StarComponent.

Setp 5: create nested StarComponent, here is the star.component.ts under the share folder


import { Component, OnInit, Input} from 'angular2/core';
@Component({
    selector: 'sz-star',
    templateUrl: 'app/shared/star.component.html',
    styleUrls: ['app/shared/star.component.css']
})
export class StarComponent implements OnInit {
    @Input() rating: number;
    starWidth: number;
    ngOnInit(): void {
        this.starWidth = this.rating * 86 / 5;
    }
}

star.component.htmlstar.PNG

css:

star_css

In angular 2: data flow is unidirectional, for passing data from view to the class

first: you passing data into the directive could bind it to the property

passing_data_down.PNG

Second: need import the Input from angular2/core, and bind it to the property

inData

Now, one nested movies-list.component and nested star.componet down.

Now using the same steps create the MovieCreateComponent


import {Component, OnInit} from 'angular2/core';

import {IMovie, Movie} from '../movie';
import {Router} from 'angular2/router';
import {MovieService} from '../movies.service';

@Component({
    templateUrl: 'app/movies/create/movie-create.component.html'
})

export class MovieCreateComponent implements OnInit {
    title: string = 'Create New Movie';
    newMovie: IMovie;

    constructor(
        private _moviesService: MovieService,
        private _router: Router
    ) { }

    ngOnInit() {
        this.newMovie = new Movie;
    }

    createMovie(event) {
        let _this = this;

        this._moviesService.createMovie(this.newMovie)
            .subscribe(function () {
                _this._router.navigate(['Movies']);
            });
    }

    cancelCreate(event) {
        this._router.navigate(['Movies']);
    }

}angular2_form.PNG
Router:
route.PNG

In the index.html page, put the baseurl:
baseUrl.png

Done, have fun!

STEP BY STEP INSTRUCTION FOR BUILDING RESTFUL API WITH ASP.NET WEB API – PART II

Register the repository

when install the ninject packages, it will create a file NinjectWebCommon.cs  under the App_Start foler, open it, go the method: RegisterServices, register your service.


  private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind().To().InSingletonScope();
            kernel.Bind().To().InSingletonScope();
        }     

what is this do, you don’t need new a object, instead of the ninject container will create a instance in your controller or repositaory class. And the ninject also will handle the life cycle of the object.

Create  MovieDataViewModal.cs file under the ViewModal foler, we using it for validation and create and put Movie record.


  using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace MovieDemo.ViewModal
{
    public class MovieDataViewModal : IValidatableObject
    {
        public int MovieId { get; set; }
        [Required]
        [Remote("DoesTitleExist", "Movies")]
        public string Title { get; set; }
        [Required]
        public string Genre { get; set; }
        [Required]
        public string Classification { get; set; }
        [Range(0, 5)]
        public int Rating { get; set; }
        public int ReleaseDate { get; set; }
        public string[] Cast { get; set; }

        public IEnumerable Validate(ValidationContext validationContext)
        {
            var result = new List();

            if (Title == Genre)
            {
                result.Add(new ValidationResult("Title can not be the same as your Genre", new string[] { "Title" }));
            }
            return result;
        }
    }
}

In the Global.asax add the config for using Automap


Mapper.Initialize(config =>
            {
                config.CreateMap<MovieData, MovieDataViewModal>().ReverseMap();
            });

Create the controller

In the solution explorer, create MoviesController.cs undert the Controllers foler


using System;
using System.Web.Http;
using MoviesLibrary;
using System.Web.Http.OData;
using MovieDemo.Services;
using AutoMapper;
using MovieDemo.ViewModal;

namespace MovieDemo.Controllers
{
    public class MoviesController : ApiController
    {
        private readonly IDataRepository _repo;

        public MoviesController(IDataRepository dataRepository)
        {
            _repo = dataRepository;
        }

       
        [EnableQuery(PageSize = 5)]// this is for suport url query working with oData
        public IHttpActionResult Get()
        {
            try
            {
                var movies = _repo.GetAllMovies(); 
                return Ok(movies);
            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }

        }
    

        //GET: api/Movies/5
        public IHttpActionResult Get(int id)
        {
            try
            {
                if (id > 0)
                {
                    var movie = _repo.GetMovieById(id);
                    var result = Mapper.Map(movie);
                    if (result != null) return Ok(result);

                    return NotFound();
                }
                return NotFound();
            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }
        }

        // POST: api/Movies 
        public IHttpActionResult Post([FromBody]MovieDataViewModal newMovie) 
        {
            try
            {

                if (newMovie == null)
                    return BadRequest("Movie can not be null");

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }

                var movie = Mapper.Map(newMovie);

                if (_repo.CreateMovie(movie)) return Ok(newMovie);

                return Conflict();

            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }
        }

        // PUT : api/Movies/5
        public IHttpActionResult Put(int id, [FromBody]MovieDataViewModal movieModified)
        {
            try
            {

                if (movieModified == null)
                    return BadRequest("Movie can not be null");

                if (movieModified.MovieId <= 0 && movieModified.MovieId != id)
                    return Conflict();

                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }

                var movieData = _repo.GetMovieById(id);
                if (movieData != null)
                {
                    var movie = Mapper.Map(movieModified);
                    _repo.UpdateMovie(movie); 
                    return Ok();
                }

                return NotFound();

            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }

        }

        public IHttpActionResult DoesTitleExist(string title)
        {
            if (_repo.DoesTitleAlreadyExist(title))
            {
                //Invalid
                return Ok();
            }
            //Valid
            return BadRequest("Title already existing");
        }
    }
}

then run the solution, open postman make query:

here is the example: http://localhost:53871/api/movies?$skip=2&$top=1
api06.PNG

for allmovies: api05.PNG

more detail  about odata query:
http://www.odata.org/documentation/odata-version-2-0/uri-conventions/

Step by step instruction for Building RESTful API with ASP.NET Web API – part I

vsUsing visio studio create a RESTful web API is very simple. In this tutorial I will follow the really live example and best practical to create a really useful web service.

source code download here:

https://github.com/sandywebdesigner/MovieWebApiDemo

It will included

  • using ninject register your service
  • using the most popularly repository pattern
  • oDATA to support url query
  • basic modal validation
  • AutoMapper
  • cache pattern

Create a new project

Start visual studio. From the File menu, select New > Project.

On the Templates, choose Web, then select ASP.NET Application, name it as MovieDemoapi01

In the pop out window, choose web api

api02

Install some  nuget packages I will using with this tutorial:

Ninject:

go to nuget search for ninject.MVC3 and install it,but this tutorial is for build web API, so also need install ninject webApi, see below is I installed for Ioc later using

api03

AutoMapper, search install the latest one.

OData

api04

For the data, I have one dll is called MovieLibrary, you could download it from my github. Included it into the project. here is the members I am going to use.


namespace MoviesLibrary
{
    public class MovieDataSource
    {
        public MovieDataSource();

        public int Create(MovieData movie);
        public List GetAllData();
        public MovieData GetDataById(int id);
        public void Update(MovieData movie);
    }
}

Now is the coding part start:
1)memory is cheaper,we put the retrive data into cache for fast query.
create a CacheHelper.cs ( I create a folder named helper holder this)


using System;
using System.Web;

namespace MovieDemo.Helper
{
    public static class CacheHelper
    {
        public static void SaveTocache(string cacheKey, object savedItem, DateTime absoluteExpiration)
        {
            if (IsIncache(cacheKey))
            {
                HttpContext.Current.Cache.Remove(cacheKey);
            }

            HttpContext.Current.Cache.Add(cacheKey, savedItem, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(24, 00, 0), System.Web.Caching.CacheItemPriority.Default, null);
        }

        public static T GetFromCache(string cacheKey) where T : class
        {
            return HttpContext.Current.Cache[cacheKey] as T;
        }

        public static void RemoveFromCache(string cacheKey)
        {
            HttpContext.Current.Cache.Remove(cacheKey);
        }

        public static bool IsIncache(string cacheKey)
        {
            return HttpContext.Current.Cache[cacheKey] != null;
        }
    }
}

2)create a folder named Services, create your repository, I create IDataRepository.cs and DataRepository.cs inside this folder.

for IDataRepository.cs:


using MoviesLibrary;
using System.Collections.Generic;
using System.Linq;

namespace MovieDemo.Services
{
    public interface IDataRepository
    {
        
        IQueryable GetAllMovies();
        MovieData GetMovieById(int id);
        bool CreateMovie(MovieData newMovie);
        bool UpdateMovie(MovieData movie);
        bool DoesTitleAlreadyExist(string title);
    }
}

3) the DataRepository.cs is the implementable of the IDataRepository, I put it along with the interface, so also under the Services folder.


using System;
using MoviesLibrary;
using System.Linq;
using MovieDemo.Common;
using MovieDemo.Helper;
using Ninject;

namespace MovieDemo.Services
{
    public class DataRepository : IDataRepository
    {   //here is the dependency inject. this need do extra in the ninject to     //regiester
        public DataRepository(MovieDataSource dataSrc)
        {
            this.dataSrc = dataSrc;
        }
       
        [Inject] private  MovieDataSource dataSrc { get; set; }
        public bool CreateMovie(MovieData newMovie)
        {
            try
            {
                if (dataSrc.Create(newMovie) > 0)
                {
                    CacheHelper.RemoveFromCache(MovieConstants.ALL_MOVIES);
                    return true;
                }
                return false;


            }
            catch(Exception ex)
            {
                return false;
            }
        }

       

        public IQueryable GetAllMovies()
        {

            var allMovies = CacheHelper.GetFromCache<IQueryable>(MovieConstants.ALL_MOVIES);

            if (allMovies == null)
            {

                allMovies = dataSrc.GetAllData().AsQueryable();
                CacheHelper.SaveTocache(MovieConstants.ALL_MOVIES, allMovies, DateTime.Now.AddHours(24));
            }

            return allMovies;
        }

        public MovieData GetMovieById(int id)
        {
            try
            {
                var allMovies = GetAllMovies();
                var movie = allMovies.FirstOrDefault(r => r.MovieId == id);
                if(movie == null)
                {
                    movie = dataSrc.GetDataById(id);
                    if (movie != null)
                        return movie;
                    return null;
                }
                return movie;
            }
            catch(Exception ex)
            {
                return null;
            }

        }

        public bool UpdateMovie(MovieData movie)
        {
            try
            {
                dataSrc.Update(movie);
                CacheHelper.RemoveFromCache(MovieConstants.ALL_MOVIES);
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
            
        }

        public bool DoesTitleAlreadyExist(string title)
        {
           return GetAllMovies().Any(r => r.Title == title);
        }
    }
}

In above implement I using dependence inject, next post I will explain how to register this DataRepository, modal validation and create the MoviesController

— to be continue

How to write a method render out a List that including all of your class members’ name

To write a Method Render a list to include all class names is very useful (perhaps not for my current project – but that is another story!). To achieve this we need to render all the member variables out from an existing class and put it into List(). This is pretty simple, here is my method:

        public static List GetMenuNodes()
        {            
            var memberOfObject = typeof(yourClassOrinstanceOfClass).GetFields();
            List propertyList = new List();
            for (var i = 0; i < memberOfObject.Length; i++)
            {               
                propertyList.Add(memberOfObject.GetValue(i).ToString());
            }
            
            return propertyList;
        }

Create custom section in Web.Config file

In previous my project that I alway put my infomation to the appSetting inset of my web.config file. But working for currently company that I learned how to create a custom section in the web.config file and could read it from the entire website and it is much better and it is very easy to achieve that. And it also help you organize your web.config.

For example I have those settings

<appSettings>

<add key=”business” value=”youremaile@hotmail.com” />
<add key=”IsSandbox” value=”true” />
<add key=”currency_code” value=”AUD” />
<add key=”return” value=”http://xxxxxx/Url&#8221; />

Now I like create a Section just named PayPalAPI

First, create the section named “PayPalAPI” just below your configuration.

<configuration>
<configSections>
<section name=”PayPalAPI” type=”YourProjectName.ConfigFile.PayPalAPIConfiguration,YourProjectName.ConfigFile” requirePermission=”false” allowLocation=”true” allowDefinition=”Everywhere” />

Second, move all the property inside of your appsettings to like this put it before </configuration> closed.

like this

<PayPalAPI IsSandbox=”true” Currency_code=”AUD” ReturnUrl=”http://xxxxxx/Url&#8221; />
</configuration>

Final step, just create a PayPalAPIConfiguration inherit from ConfigurationSection

Here is the sample:

public  class PayPalAPIConfiguration : ConfigurationSection
    {
        public static PayPalAPIConfiguration GetConfig()
        {
            var config = (EmailAccountConfiguration)ConfigurationManager.GetSection("PayPalAPI");
            return (config == null) ? new PayPalAPIConfiguration () : config;
        }
        //below is read for the property
        [ConfigurationProperty("isSandbox", DefaultValue = "", IsRequired = false)]
        public string IsSandbox
        {
            get
            {
                return this["isSandbox"] as string;
            }

            set
            {
                this["isSandbox"] = value;
            }
        }


        [ConfigurationProperty("currency_code", DefaultValue = "AUD", IsRequired = true)]
        public Boolean Currency_code
        {
            get
            {
                return (Boolean)this["currency_code"];
            }

            set
            {
                this["currency_code"] = value;
            }
        }
[ConfigurationProperty("returnUrl", DefaultValue = "http://xxxxxx/Url", IsRequired = true)]
        public Boolean ReturnUrl
        {
            get
            {
                return (Boolean)this["returnUrl"];
            }

            set
            {
                this["returnUrl"] = value;
            }
        }

    }

How to use it, since it allow use every where so inside your project just call this:

PayPalAPIConfiguration .GetConfig().IsSandbox;

Create custom HtmlHelper to Render different type of Twitter Card

In this day, all the social media site is very important, I have task need implement the Twitter Cards into all the existing projects.

Render partial View is fine, but since just need same meta data, so I just create a custom HtmlHelpler, I put my works here, anyone think that helpful, could plug into your site.

public static class TwitterCardHelper
    {
        //this is the base method
        public static MvcHtmlString TwitterCard(this HtmlHelper html, string cardType, string title, string description, string image = null, object listAttributes = null,object productAttributes = null)
        {           
            
            var format = @"<meta name=""twitter:{0}"" content=""{1}"" />";
            var sb = new StringBuilder();
            var config = ProjectConfigurationSection.GetConfig();
            sb.AppendFormat(format, "card", html.Encode(cardType));
            sb.AppendFormat(format, "site", config.ProjectEnvironment == ProjectEnvironment.Production ? config.ProjectDisplayName : String.Format("{0} ({1})", config.ProjectDisplayName, config.ProjectEnvironment.GetDescription()));
            sb.AppendFormat(format, "title", html.Encode(title));
            sb.AppendFormat(format, "description", html.Encode(description));
            if (cardType == "summary" || cardType == "summary_large_image")
            { 
                sb.AppendFormat(format, "image", UriExtensions.Parse(image).AbsoluteUri);
            }

            if(listAttributes !=null)
            {
                 sb.Append(GetHtmlAttributes(listAttributes));
            } 

            return MvcHtmlString.Create(sb.ToString());
        }

        //below is several overloads method for your to choose different type of TwitterCard 
        public static MvcHtmlString TwitterSummaryCard(this HtmlHelper html, string title, string description, string image = null)
        {
            return TwitterCard(html, "summary", title, description, image);
        }

        public static MvcHtmlString TwitterSummaryCardWithLargeImage(this HtmlHelper html, string title, string description, string image = null)
        {

            return TwitterCard(html, "summary_large_image", title, description, image);
        }


        //listAttributes sample:
        //    new{
        //         iphoneName ="iphoneName",
        //         iphoneId="iphoneId",
        //         iphoneUrl="iphoneUrl",
        //         ipadName = "ipadName",
        //             ipadId="ipadId",
        //         ipadUrl="ipadUrl",
        //         googleplayName="googleplayName",
        //         googleplayId="googleplayId",
        //         googleplayUrl="googleplayUrl"
        //      }
        public static MvcHtmlString TwitterAppCard(this HtmlHelper html,string title,string description,object listAttribute)
        {
            return TwitterCard(html, "app", title, description, null, listAttribute);
        }

        
      //below two member is to render anonymous type of object to string
        private static string GetHtmlAttributes(object listAttributes)
        {
           
             var format = @"<meta name=""twitter:app:{0}:{1}"" content=""{2}"" />";
            var sb = new StringBuilder();

            if (listAttributes != null)
            {
                var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(listAttributes);
                foreach (var item in attributes)
                {
                   
                    if (item.Key == "iphoneName") sb.AppendFormat(format, "name", "iphone", item.Value);
                    if (item.Key == "iphoneId") sb.AppendFormat(format, "id", "iphone", item.Value);
                    if (item.Key == "iphoneUrl") sb.AppendFormat(format, "url", "iphone", item.Value);

                    if (item.Key == "ipadName") sb.AppendFormat(format, "name", "ipad", item.Value);
                    if (item.Key == "ipadId") sb.AppendFormat(format, "id", "ipad", item.Value);
                    if (item.Key == "ipadUrl") sb.AppendFormat(format, "url", "ipad", item.Value);

                    if (item.Key == "googleplayName") sb.AppendFormat(format, "name", "googleplay", item.Value);
                    if (item.Key == "googleplayId") sb.AppendFormat(format, "id", "googleplay", item.Value);
                    if (item.Key == "googleplayUrl") sb.AppendFormat(format, "url", "googleplay", item.Value);
                    
                }

            }

            return sb.ToString();
        }

Customize a validation attribute working both client side and server side in asp.net MVC

In asp.net MVC the model is using for holding the data and for validation. There are a lot build in validation for you to use, how? just simply by put “using System.ComponentModel.DataAnnotations;” on top of the your model file then you could like this

using System.ComponentModel.DataAnnotations;

namespace youprojectName.Model
{
    public class Account
    {

       [StringLength(26)]//this is the validate
        public string BusinessName { get; set; }

        ...
       }
}

Those build in validation is very useful and they are both client side and server side validation.

But in real projects we need more validation to suit for our business logic. So in that scenario there is a way if your boss happen just the validation is server side,then simply let your model inherit from :IValidatableObject

then you could override the IEnumerable Validate(ValidationContext validationContext) Member.

Sample like this

       
        //those validation only for server side
        public IEnumerable Validate(ValidationContext validationContext)
        {
            if (Name.ToLower().StartsWith("xxxx"))
            {
                yield return new ValidationResult("Sorry, xxxx, you do not have permisstion to do this");
            }
             .....
        }

But this is not the way supports to write your validation, in MVC validation is a attribute just put on top of your class property.
So how to achieve that?
We could create a custom remote attribute and override IsValid() method.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;

namespace CustomizeValidation.Validation
{
    public class RemoteClientServerAttribute : RemoteAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // Get the controller using reflection
            Type controller = Assembly.GetExecutingAssembly().GetTypes()
                .FirstOrDefault(type => type.Name.ToLower() == string.Format("{0}Controller",
                    this.RouteData["controller"].ToString()).ToLower());
            if (controller != null)
            {
                // Get the action method that has validation logic
                MethodInfo action = controller.GetMethods()
                    .FirstOrDefault(method => method.Name.ToLower() ==
                        this.RouteData["action"].ToString().ToLower());
                if (action != null)
                {
                    // Create an instance of the controller class
                    object instance = Activator.CreateInstance(controller);
                    // Invoke the action method that has validation logic
                    object response = action.Invoke(instance, new object[] { value });
                    if (response is JsonResult)
                    {
                        object jsonData = ((JsonResult)response).Data;
                        if (jsonData is bool)
                        {
                            return (bool)jsonData ? ValidationResult.Success :
                                new ValidationResult(this.ErrorMessage);
                        }
                    }
                }
            }

           
            return new ValidationResult(base.ErrorMessageString);
        }
        //below is several over load for your to use   
        public RemoteClientServerAttribute(string routeName)
            : base(routeName)
        {
        }

        public RemoteClientServerAttribute(string action, string controller)
            : base(action, controller)
        {
        }

        public RemoteClientServerAttribute(string action, string controller,
            string areaName)
            : base(action, controller, areaName)
        {
        }
    }
}

then you could simply use it like this:

public class Customer
    {
       ....
        [StringLength(100)]
        [RemoteClientServer("IsValid", "Account",
        ErrorMessage="Email already in use")] 
//"IsValid" is the method should return Json data at your "Account" controller
        public string Email { get; set; }
       ...
   }

Here is one of solution for write customize both server side and client side validation,but I have another solution also could working in client and server side validation(for those solution is write your server side validation in the model first, then write some more jQuery to the View), this will another post, when I back from my job.

Have Fun!

Custom ActionFilterAttribute to pass data to the viewbag before the action executed

Currently projects the SEO setting has been manually hard code it to the view. But since the SEO setting will be frequently change, so my task is to move those hard code text to database, and for render it out to the page. I just created a ViewBag to pass those information to the view.

Asp.net mvc ActionFilterAttribute allow your to pre-process logic to the method before or after the method just executed.

How to do it, just create a class inherit from: ActionFilterAttribute, and inside this class you could see there are 4 members allow you to override it.

        public virtual void OnActionExecuted(ActionExecutedContext filterContext);      
        public virtual void OnActionExecuting(ActionExecutingContext filterContext);        
        public virtual void OnResultExecuted(ResultExecutedContext filterContext);        
        public virtual void OnResultExecuting(ResultExecutingContext filterContext);

For my task, I just overide the OnActionExecuting method.
here is my solution:
step1, create a class named: SeoSettingAttribute.cs
step2,

   public class SeoSettingAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {

            string action = filterContext.ActionDescriptor.ActionName;
            filterContext.Controller.ViewBag.SeoSettings = GetSeoSettings(action);
        }

        private SeoSettingViewModel GetSeoSettings(string actionStr)
        {
            SeoSettingViewModel seoModel = new SeoSettingViewModel();
            var AdminService = NinjectWebCommon.CurrentKernel.Get();
            seoModel = AdminService.GetCurrentSeoSetting(actionStr);
            NinjectWebCommon.CurrentKernel.Release(AdminService);

            return seoModel;
        }
    }

step3:

decorate it to your the method inside your controller that you like use it.

      [SeoSettingFilter]
        public ActionResult Index(){....}

How to write your own HtmlHelper render input text in asp.net mvc and working with Date Range Picker

When I was built a tourist website my client’s like have a date range picker. So I search internet found there is this bootstrap date range picker, I am big fun with bootstrap and this is the frond end css framework I am very confident with it.

You could just write html to your view to display it, but what happen if I need use in multiply place and different projects. So I like customize one HtmlHelper to render it.

For how to figure it out in html,.here is the github links.

https://github.com/dangrossman/bootstrap-daterangepicker

I also have picture show:customizeHtmlHelperRenderDateRange02

This date range pick also allow single date pick, how see the help document, because in here I focus how to make it to a Htmlhelper.

Step1: download it, and you only need the moment.js(this is big, if you worries the speed,use the CDN, why CDN help the speed? this is because when the moment.js also used in another projects online, and it already open, then someone go to your website and click it, then you don’t need download it) and daterangepicker-bs3.css

customizeHtmlHelperRenderDateRange01

For me I used it in my asp.net mvc project, I simple just put it inside my bundle.

Step2: start HtmlHelper to render text input box, this time I like also bind it to my model, so it will just like the existing htmlhelper render input text,but write your own one helper you understand how the HtmlHelper works.

here is the method

public static MvcHtmlString MyOwnTextBoxFor<TModel, TValue>(
     this HtmlHelper htmlHelper,
     Expression<Func<TModel, TValue>> expression,
     string type,
     string title,
     string placeholder,
     bool isRequired,
     bool isAutoFocus,
     object htmlAttributes = null)
        {
            MvcHtmlString html = default(MvcHtmlString);
            Dictionary<string, object> attributesOptions = new Dictionary<string, object>();

            if (htmlAttributes != null)
            {
                var attributes =
                  HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
                foreach (var item in attributes)
                {
                    attr.Add(item.Key, item.Value);
                }
            }

            attr.Add("type", type);
            attr.Add("class", "form-control");
            if (!string.IsNullOrEmpty(title))
            {
                attr.Add("title", title);
            }
            if (!string.IsNullOrEmpty(placeholder))
            {
                attr.Add("placeholder", placeholder);
            }
            if (isAutoFocus)
            {
                attr.Add("autofocus", "autofocus");
            }
            if (isRequired)
            {
                attr.Add("required", "required");
            }

            html = InputExtensions.TextBoxFor(htmlHelper,
                                              expression,
                                              attr);

            return html;
        }

And here is the several over load method, so in the view you have more options

public static MvcHtmlString MyOwnTextBoxFor<TModel, TValue>(
      this HtmlHelper htmlHelper,
      Expression<Func<TModel, TValue>> expression,
      object htmlAttributes = null)
        {
            return MyOwnTextBoxFor(htmlHelper, expression,"text", string.Empty, string.Empty, false, false, htmlAttributes);
        }


        public static MvcHtmlString MyOwnTextBoxFor<TModel, TValue>(
          this HtmlHelper htmlHelper,
          Expression<Func<TModel, TValue>> expression,
          string type,
          object htmlAttributes = null)
        {
            return MyOwnTextBoxFor(htmlHelper, expression, type, string.Empty, string.Empty, false, false, htmlAttributes);
        }


        public static MvcHtmlString MyOwnTextBoxFor<TModel, TValue>(
          this HtmlHelper htmlHelper,
          Expression<Func<TModel, TValue>> expression,
          string title,
          object htmlAttributes = null)
        {
            return MyOwnTextBoxFor(htmlHelper, expression, "text", title, title, false, false, htmlAttributes);
        }

        public static MvcHtmlString MyOwnTextBoxFor<TModel, TValue>(
          this HtmlHelper htmlHelper,
          Expression<Func<TModel, TValue>> expression,
          string type,
          string title,
          object htmlAttributes = null)
        {
            return MyOwnTextBoxFor(htmlHelper, expression, type, title, title, false, false, htmlAttributes);
        }


        public static MvcHtmlString MyOwnTextBoxFor<TModel, TValue>(
          this HtmlHelper htmlHelper,
          Expression<Func<TModel, TValue>> expression,
          string title,
          bool isRequired,
          bool isAutoFocus,
          object htmlAttributes = null)
        {
            return MyOwnTextBoxFor(htmlHelper, expression, "text", title, title, isRequired, isAutoFocus, htmlAttributes);
        }

and in the view simple just use it like this

@Html.MyOwnTextBoxFor(m=>m.StartDate)

For date range pick you need a piece of javascript

@section scripts
{// }//for more option could read the github helper document

actually the input text type could be “text”, “email”, “number”, so make it more useful create a enum hoder it,then for the type replace it with that enum method.

public enum MyInputTypesOption
        {
            text,
            color,
            date,
            datetime,
            email,
            month,
            number,
            password,
            range,
            search,
            tel,
            time,
            url,
            week
        }

After this you need change

//string type, //comment out this
MyInputTypesOption type,//use this

//so for the overload method instead of type "text", could use MyInputTypesOption.text

And in the method this time for the attribute I put it in

Dictionary<string, object> attributesOptions = new Dictionary<string, object>();

that is because in the end there use the

InputExtensions.TextBoxFor(htmlHelper,
                                              expression,
                                              attributesOptions);

the big benefit for this just you don’t need send a anonymous objects, and you could pass the html attributes straight way and with intelligence.

And one thing is important, is the date range need input type is “text”.
to create the string.

Have fun!

Intergrate with google analytics — retrieve data back from google analytics

Recently task need retrieve data back from google analytics and put it into the existing website. Online have a lot tutorial explain how to do it.

Here I just record my one over there hope could help my fans.

And I have a exsiting asp.net C# mvc project is on life and they are already embedded google analytics, and my job is to create a admin function so when the projects admin login there is a page display that google analytics datas.

And google already have a client side library for the .net developer to user it.

Step1: PM> Install-Package Google.Apis.Analytics.v3

Step2: For retrieve data from google analytics, you actially just need do two things.

1) Authenticate with google analytics

2) using Google analytics core reporter

I have some words about  Authenticate with google analytics, for me I personally think that just use the Service Account is fine. if you like restrict who is going to see that datas in asp.net mvc is very easily to retrieve it.

So for Authenticate with google analytics using Service Account steps:

1) you need have google account, I mean google account, could be your gmail account and could be your company email.

2)Then use you google account register a Google analytics account.

3) go to the google console enable Google analytics.

4)Create a service account by:

  • First click create a new OAuth2 projects( that is google recommend), then pop out a new window choose the service account, and download that key, put this key into your application,
  • Second don’t forget to go back to google analytics, register that service account email to give permission to this email address.

All of above things ready, now you could start write your code to using the core reporter service.

Here is my implementation class for

   
namespace XXX.XXX.Service.GAServices
{
    #region two way Authenticate with google analytics
    public  class GAAuthenticateHelper
    {
        public static AnalyticsService AuthenticateServiceAccount(string serviceAccountEmail, string keyFilePath)
        {

            // check the file exists
            if (!File.Exists(keyFilePath))
            {
                Console.WriteLine("An Error occurred - Key file does not exist");
                return null;
            }

            string[] scopes = new string[] { 
                
                      AnalyticsService.Scope.Analytics,  // view and manage your analytics data
                     // AnalyticsService.Scope.AnalyticsEdit,  // edit management actives
                     // AnalyticsService.Scope.AnalyticsManageUsers,   // manage users
                      AnalyticsService.Scope.AnalyticsReadonly
                                           };     // View analytics data            

            var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
            try
            {
                ServiceAccountCredential credential = new ServiceAccountCredential(
                    new ServiceAccountCredential.Initializer(serviceAccountEmail)
                    {
                        Scopes = scopes
                    }.FromCertificate(certificate));

                // Create the service.
                AnalyticsService service = new AnalyticsService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "GA Dashboard", // to do this
                });
                return service;
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.InnerException);
                return null;

            }
        }
    }

    #endregion


    #region Google analytics core reporter
    // Google analytics query help for retrieve datas https://ga-dev-tools.appspot.com/query-explorer/
  public class GAReportingHelper
  {
      public class OptionalValues
      {
          private string dimensions { get; set; }
          private string filter { get; set; }
          private string sort { get; set; }
          private string segment { get; set; }
          private int maxResults { get; set; }
          private DataResource.GaResource.GetRequest.SamplingLevelEnum sampleingLevel = DataResource.GaResource.GetRequest.SamplingLevelEnum.DEFAULT;

          public string Dimensions { get { return dimensions; } set { dimensions = value; } }
          public string Filter { get { return filter; } set { filter = value; } }
          public string Sort { get { return sort; } set { sort = value; } }
          public string Segment { get { return segment; } set { segment = value; } }
          public int MaxResults { get { return maxResults; } set { maxResults = value; } }
          public DataResource.GaResource.GetRequest.SamplingLevelEnum Sampling { get { return sampleingLevel; } set { sampleingLevel = value; } }
          public OptionalValues()
          {
              this.dimensions = null;
              this.filter = null;
              this.sort = null;
              this.segment = null;
              this.sampleingLevel = DataResource.GaResource.GetRequest.SamplingLevelEnum.DEFAULT;
              this.maxResults = 1000;
          }
      }

      public static GaData Get(AnalyticsService service, string profileId, string startDate, string endDate, string metrics, OptionalValues optionalValues)
      {
          DataResource.GaResource.GetRequest request = service.Data.Ga.Get(String.Format("ga:{0}", profileId), startDate, endDate, metrics);
          if (optionalValues == null)
          {
              request.MaxResults = 1000;
          }
          else
          {
              request.MaxResults = optionalValues.MaxResults;
              request.Dimensions = optionalValues.Dimensions;
              request.SamplingLevel = optionalValues.Sampling;
              request.Segment = optionalValues.Segment;
              request.Sort = optionalValues.Sort;
              request.Filters = optionalValues.Filter;

          }
          return ProcessResults(request);
      }
      // Just loops though getting all the rows.  
      private static GaData ProcessResults(DataResource.GaResource.GetRequest request)
      {
          try
          {
              GaData result = request.Execute();
              List<IList> allRows = new List<IList>();
              // Loop through until we arrive at an empty page
              while (result.Rows != null)
              {
                  //Add the rows to the final list
                  allRows.AddRange(result.Rows);
                  // We will know we are on the last page when the next page token is
                  // null.
                  // If this is the case, break.
                  if (result.NextLink == null)
                  {
                      break;
                  }
                  // Prepare the next page of results             
                  request.StartIndex = request.StartIndex + request.MaxResults;
                  // Execute and process the next page request
                  result = request.Execute();
              }
              GaData allData = result;
              allData.Rows = (List<IList>)allRows;
              return allData;
          }
          catch (Exception ex)
          {
              Console.WriteLine(ex.Message);
              return null;
          }

      }
  }

#endregion


}

If you have some red line show, just use ctrl + . to add the reference.
Now I create a google analytics service class file, so in my controller, I just need call that method.
And In my controller, I create a method:

private static GADATA.GaData GaReportingDatas(AdminStatsVM model)//I only allow my admin to see, here I pass my admin data over there, and once I got the data will asign to my adminStatsVM model
        {
            AnalyticsService gaService;            
            string email = "yourserviceaccountemail@developer.gserviceaccount.com";
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"bin\yourprivatekey.p12");
  //this is your download file put here, 
            gaService = GAAuthenticateHelper.AuthenticateServiceAccount(email, path);
            string profileId = "XXXXX";//this can find in google analytics
            string metricsStr = "ga:sessions,ga:users,ga:pageviews,ga:pageviewspersession,ga:avgsessionDuration,ga:bounces,ga:percentNewSessions";//google allow pass multiply metrics by separate by comma
            string startDate = model.StartDate.ToString("yyyy-MM-dd");
            string endDate =  model.EndDate.ToString("yyyy-MM-dd"); 
            var gaData = GAReportingHelper.Get(gaService, profileId, startDate, endDate, metricsStr, null);
            return gaData;
        }

Now you can use it anyway you like.
How to renter to the page?
here in my model I have one member

 public GaData GaDatas { get; set; }

so in my controller, I assign it to

 var record = new AdminStatsVM
            {
                ...
                GaDatas = GaReportingDatas(model);//call that method above

            };

here in the view:

@foreach (var item in Model.GaDatas.Rows)
                        {
                                                        
                            foreach (string col in item)
                            {@col.FormatDigit()

} }@Model.GoogleAnalyticsUrl

What is FormatDigit(),

 public static String FormatDigit(this String input)
        {
            decimal myNum = decimal.Parse(input);           
            return  String.Format((Math.Round(myNum,2) == myNum) ? "{0:0.00}" : "{0:0}",myNum);
        }

I was following

http://www.daimto.com/google-analytics-api-v3-with-c/#Google_Analytics_API_8211_Introduction

Anything don’t understand it, could have view this link first.

very helpful.

Have fun!