Archives

Archives / 2010 / September
  • Google API for .NET architecture (Part 2)

    Part 1: Google API for .NET architecture (Part 1) 

    Today is Harvey Nash - 10 years in Viet Nam celebration day. I very happy about that, and I also drunk some beer, so I have been eager to write this post. In last post, I had been analyze about Domain Model of Gapi4net, some of people maybe like it. That is a reason I write this post in next. And what will I write in this post? As you knew in last post, I will be analyze about some technical I used in Gapi4net. Three of techniques that I will present is download data, build the Url and make Fluent Interface for Gapi4net.

    + Download data: I use a technical that post from this link for implemented the asynchronous request to Google services. I want to execute it in asynchronous because it will save a little bit time when request to Google services, and I will continue to process something else until the request to Google services are completed. I thought I will not explain it clearly, because everyone also knew why we use the asynchronous request. Next step, I only need to code the Downloader, this code will be show as below:

        public interface IDownloader : IDisposable
        {
            void Add(string url);

            int Count { get; }

            IEnumerable<IAsync> DownloadAll(Action<Async<string>> action);
        }
        internal class Downloader : IDownloader
        {
            private readonly IList<string> _urls;

            public Downloader()
            {
                _urls = new List<string>();
            }

            public void Add(string url)
            {
                Contract.Assert(!string.IsNullOrEmpty(url), "Url is null or empty");

                _urls.Add(url);
            }

            public IEnumerable<IAsync> DownloadAll(Action<Async<string>> action)
            {
                return _urls.Select(url => Async.Parallel(
                    ProcessResultFromDownloadContentFromUrl(url, action)
                                               ));
            }

            private IEnumerable<IAsync> ProcessResultFromDownloadContentFromUrl(string url, Action<Async<string>> action)
            {
                var req = WebRequest.Create(url);

                // asynchronously get the response from http server
                var response = req.GetResponseAsync();
                yield return response;

                var resp = response.Result.GetResponseStream();

                // download HTML using the asynchronous extension method
                // instead of using synchronous StreamReader
                var html = resp.ReadToEndAsync().ExecuteAsync<string>();

                yield return html;

                action(html);
            }

            public void Dispose()
            {
                GC.SuppressFinalize(this);
            }

            public int Count
            {
                get { return _urls.Count; }
            }
        }

     + Build the Url: Because my request is a Url, so I must to build the Url to request to Google services. I will convert from the input entities to Url, so I must to reflection the entities for get metadata and use it to build Url. To be clearly this, I will show one sample, assume that I have a input entity as:

        public abstract class EntityBase : IEntity
       {
            public string Query { getset; }

            public string Version { getset; }

            public string UserIp { getset; }

            public int ResultSize { getset; }

            public string HostLanguage { getset; }

            public string Key { getset; }


            public int Start { getset; }

            public abstract string SearchWebUrl();
        }
        public class Web : EntityBaseIWeb
        {
            public string UniqueId { getset; }

            public string Linked { getset; }

            // Safe type
            public Safe Safe { getset; }

            // ParticularLanguage type
            public ParticularLanguage ParticularLanguage { getset; }

            // Filter type
            public Filter Filter { getset; }

            // CountryCode type
            public CountryCode CountryCode { getset; }
         }

    It is a web's entity, so we need knowing what are the fields that we need to build? To do that, we need creating a custom attribute as:

        [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property,
                        AllowMultiple = false,
                        Inherited = true)]
        public class UrlElementAttribute : Attribute
        {
            public string Name { getprivate set; }

            public bool Required { getprivate set; }

            public UrlElementAttribute(string name)
            {
                Name = name;
                Required = true;
            }
        }

    After that I will decorate this custom attribute on my web's entity as:

        public abstract class EntityBase : IEntity
        {
            [UrlElement("q")]
            public string Query { getset; }

            [UrlElement("v")]
            public string Version { getset; }

            public string UserIp { getset; }

            public int ResultSize { getset; }

            public string HostLanguage { getset; }

            public string Key { getset; }

            [UrlElement("start")]
            public int Start { getset; }

            public abstract string SearchWebUrl();
        }

    Finally, I will write code for exploring this entity as:

        public interface IUrlBuilder<T> where T : IEntity
        {
            string BuildUrl();

            T Entity { getset; }
        }
        public abstract class UrlBuilderBase<T> : IUrlBuilder<T> where T : IEntity
        {
            public T Entity { getset; }

            public virtual string BuildUrl()
            {
                var builder = new StringBuilder();

                var propertyInfos = Entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

                foreach (var info in propertyInfos)
                {
                    var attrs = info.GetCustomAttributes(typeof(UrlElementAttribute), true);

                    if (attrs.Length == 0)
                    {
                        continue;
                    }

                    var urlAttr = attrs[0] as UrlElementAttribute;

                    builder.AppendFormat("{0}={1}&", urlAttr.Name, info.GetValue(Entity, null));
                }

                return builder.ToString().Substring(0, builder.ToString().Length - 1);
            }
        }
        public class WebUrlBuilder : UrlBuilderBase<Web>
        {
        } 

    All thing is fine, and now we can convert from the entities to Url with a little bit code. Are you like that too?

    + Make Fluent Interface: As you knew, Gapi4net is written for easy used, so I implemented the Fluent Interface inside it and applied the Lambda Expression to association with Fluent Interface. It make the cleanly structure for invoked the APIs as:

        Gapi4NetFactory<T>
                    .Init()
                    .With(x => x.Version, "1.0")
                    .With(x => x.Query, searchText)
                    .Create();

    I copied some of code from this link, and modified something to fixed with my solution. It is so cool! Below is full of this code:

        public interface IGenericFactory<T>
        {
            IGenericFactory<T> With(Expression<Func<T, object>> property, object value);

            T Create();
        }

        public class GenericFactory<T> : IGenericFactory<T>
        {
            private T _entity;

            public GenericFactory(T entity)
            {
                _entity = entity;
            }

            public IGenericFactory<T> With(Expression<Func<T, object>> property, object value)
            {
                PropertyInfo propertyInfo = null;

                if (property.Body is MemberExpression)
                {
                    propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
                }
                else
                {
                    propertyInfo = ((MemberExpression)((UnaryExpression)property.Body).Operand).Member as PropertyInfo;
                }

                propertyInfo.SetValue(_entity, value, null);

                return this;
            }

            public T Create()
            {
                return _entity;
            }
        }
     
        public static class Gapi4NetFactory<T> where T : IEntitynew()
        {
            public static IGenericFactory<T> Init()
            {
                return new GenericFactory<T>(new T());
            }
        }

    That is the main techniques I used in Gapi4net. For more information please download my library and look it clearly. In next post, I will show you some of technical in ASP.NET MVC 3 Preview 1. Hope you will welcome it. Thanks for you read this post and see you next time.

  • Google API for .NET architecture (Part 1)

    Part 2: Google API for .NET architecture (Part 2)

    Today, I have just released a OSS with named is Gapi4net library at codeplex. This is a wrapper some of API's Google for search Web, Local, Video, Blog, News, Book, Image, Patent and language translation. In the past, I also saw some wrapper for Google API, specially are a google-api-for-dotnet, gapidotnet. But as I realized that they are hard to using and when you used them, you must remember many input parameters. For example, if you use the google-api-for-dotnet, you must input as:

    GwebSearchClient client = new GwebSearchClient(/* Enter the URL of your site here */);
       
    IList<IWebResult> results = client.Search("Google API for .NET", 32);  
       
    foreach(IWebResult result in results)  
       
    {  
           
    Console.WriteLine("[{0}] {1} => {2}", result.Title, result.Content, result.Url);  
       
    }

    As you see, it does not support for you to easy in your work. So I decided to wrapping it according to my method. I will focus on purpose of user when using my library. And main feature is easy to use it. So why is my library easy to use than some of OSS before? I will show you this as below:

    var result = Gapi4NetFactory<Web>
                    .Init()
                    .With(x => x.Version, "1.0")
                    .With(x => x.Query, searchText)
                    .With(x => x.Start, start)
                    .Create();

    I wrapped it and expose it with Fluent Interface and Lambda Expression. So input parameters is more meaningful than google-api-for-dotnet. And the input parameter is strong name now. You do not need to many comment on each parameter. And if you comment many line on each parameter, are you sure the end-user will see it? Trend now is avoiding the comments in code. Just simple that if you comment your code today, and next day you have the change request in your code, you maybe forget update your comment in this code. That make your comment is out of date. And why do you keep some of comments that not meaningful? So I usually make my code better possible and try to keep a little comment on my code, make my code self-talking with the end user about its function. In this sample above, I use Lambda Expression for make strong name and meaningful for input parameters. I am usually eager about this.

    After talking many about my Gapi4net, I hope you will be enjoy about it in your practice. Now I will explain about architecture using in this library. The first thing is domain model, next I will explain some of technical, and finally is a example that write in ASP.NET MVC 3 Preview 1 for proving my solution.

    Now, we shall jump into Gapi4net's domain model. You should see this link for APIs from Google. After see it, you will realize that many things in it. Don't worry about that. Just see for fun because I wrapped all of it in Gapi4net. And the main things I wrapped is Web, Local, Video, Blog, News, Book, Image, Patent and language translation. I worked on nine kind of APIs of Google in 2 weeks. Finally all things is going on well. And now I will show all my working in this post.

    + Domain Model's Web Search:

    + Domain Model's Local Search:

    + Domain Model's Video Search:

    + Domain Model's Blog Search:

    + Domain Model's News Search:

    + Domain Model's Book Search:

    + Domain Model's Image Search:

    + Domain Model's Patent Search:

    + Domain Model's Language Translation:

    And I used the Json.net library for convert the Json data to my entities. Please see my previous post about converting technical in Json.net library. In this post I only show some line of code in Web Search for saving space in this post. All of them was put at here. And this is a code for web search:

        [JsonObject]
        public class WebSearchResult : ContractBase
        {
            [JsonProperty("responseData")]
            [JsonConverter(typeof(CustomObjectCreationConverter<IResponseDataResultResponseDataResult>))]
            public IResponseDataResult ResponseData { getset; }
        }

        [JsonObject]
        public class ResponseDataResult : IResponseDataResult
        {
            [JsonProperty("cursor")]
            [JsonConverter(typeof(CustomObjectCreationConverter<ICursorResultCursorResult>))]
            public ICursorResult Cursor { getset; }

            [JsonProperty("results")]
            [JsonConverter(typeof(CustomArrayCreationConverter<IMainResultMainResult>))]
            public IMainResult[] MainResult { getset; }
        }
        [JsonObject]
        public class MainResult : IMainResult
        {
            [JsonProperty("GsearchResultClass")]
            public string GsearchResultClass { getset; }

            [JsonProperty("cacheUrl")]
            public string CacheUrl { getset; }

            [JsonProperty("content")]
            public string Content { getset; }

            [JsonProperty("title")]
            public string Title { getset; }

            [JsonProperty("titleNoFormatting")]
            public string TitleNoFormatting { getset; }

            [JsonProperty("unescapedUrl")]
            public string UnescapedUrl { getset; }

            [JsonProperty("url")]
            public string Url { getset; }

            [JsonProperty("visibleUrl")]
            public string VisibleUrl { getset; }
        }
    Next post I will continue to analyze some of technical that I used in Gapi4net and a example write in ASP.NET MVC 3 Preview 1. 
    Good bye and see you next time!