ASP.NET MVC 3 で jQuery Mobile を使ってみる(その3)

この記事は、jQuery Mobile を現時点の MVC 3 で使おうとするとどうなるかな、という興味から始めてみたものです。前回、モデルとデータアクセス系について書いた記事の続きになります。

サービス系

サービス系を作ります。プロジェクトに Services フォルダを追加します。作るのは「IBookService.cs」「BookService.cs」「RssReader.cs」の3つです。新刊情報を取得するのに、株式会社オライリー・ジャパン の新刊情報 RSS を参照するように書いています(RSS で配信している出版社がもっとたくさんあるかと思っていたんですけど、案外少ないですねぇ。。。)。RSS のソース別の管理や RSS 選択のメニューなどは実装していません 😉

IBookService.cs

using System.Linq;
using GetExternalXml001.Models;

namespace GetExternalXml001.Services
{
    public interface IBookService
    {
        /// <summary>
        /// 新刊情報を RSS から取得します。
        /// 新たな情報の取得は一定時間おきになります。
        /// </summary>
        void GetBooksByRss();
        /// <summary>
        /// Books テーブルへの IQueryable アクセスを提供します。
        /// </summary>
        /// <returns></returns>
        IQueryable<Book> FindBooks();
        /// <summary>
        /// Books テーブルから指定された ID のレコードを取り出します。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        Book GetBook(int id);
    }
}

BookService.cs

using System;
using System.Linq;
using System.Xml;
using GetExternalXml001.DAL;
using GetExternalXml001.Models;

namespace GetExternalXml001.Services
{
    public class BookService : IBookService
    {
        private const string RSS_URL = "http://www.oreilly.co.jp/catalog/soon.xml";

        private IBooksRepository _repository;

        public BookService()
        {
            _repository = new BooksRepository();
        }

        public void GetBooksByRss()
        {
            if (_repository.IsExistUpdateRecord() &&
                !IsExpire(_repository.GetUpdateDate())) return;

            var feed = RssLoad();
            _repository.ClearBooks();

            foreach (var n in feed.Items)
            {
                var book = new Book {
                    Title = n.Title,
                    Link = n.Link.AbsoluteUri,
                    Description = n.Description,
                    Author = n.Creater,
                    PublicationDay = n.PublishDate
                };

                _repository.AddBook(book);
            }

            var updateInfo = new UpdateInfo {
                UpdateInfoId = 1,
                Date = DateTime.Now
            };
            if (_repository.IsExistUpdateRecord())
                _repository.UpdateDate(updateInfo);
            else
                _repository.AddDate(updateInfo);
        }

        public IQueryable<Book> FindBooks()
        {
            return _repository.FindBooks();
        }

        public Book GetBook(int id)
        {
            return _repository.GetBook(id);
        }

        private PublicationInfo RssLoad()
        {
            PublicationInfo feed = null;

            using (XmlReader xrdr = XmlReader.Create(RSS_URL))
            {
                feed = RssReader.Load(xrdr);
            }

            return feed;
        }

        /// <summary>
        /// 引数 date で渡された日時が期限切れであるかどうかを返します。
        /// 有効期限は 30分
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private bool IsExpire(DateTime date)
        {
            return (DateTime.Now.AddMinutes(-30d) > date);
        }
    }
}

RssReader.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

namespace GetExternalXml001.Services
{
    public class PublicationInfo
    {
        // RSS 発信元
        public string Sender { get; set; }
        // RSS 情報の概要
        public string Description { get; set; }
        // RSS Item から取得した情報のリスト
        public List<PublicationInfoItem> Items { get; set; }
    }

    public class PublicationInfoItem
    {
        // 新刊のタイトル
        public string Title { get; set; }
        // 新刊情報へのリンク
        public Uri Link { get; set; }
        // 新刊の概要
        public string Description { get; set; }
        // 新刊の刊行日
        public DateTime? PublishDate { get; set; }
        // 新刊の著者
        public string Creater { get; set; }
    }

    public static class RssReader
    {
        /// <summary>
        /// 新刊情報の RSS を読み取り、新刊情報を取得します。
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        public static PublicationInfo Load(XmlReader reader)
        {
            PublicationInfo pubInfo = null;

            var xdoc = XDocument.Load(reader);
            if (xdoc.Root.Name.LocalName == "RDF")
            {   // RSS 1.0
                pubInfo = GetRss1_0Info(xdoc);
            }
            else if (xdoc.Root.Name == "rss")
            {   // RSS 2.0
                pubInfo = GetRss2_0Info(xdoc);
            }
            else
            {   // RSS としては未知の形式
                throw new FormatException("RSS のルートタグが未知のものです。");
            }

            return pubInfo;
        }

        /// <summary>
        /// RSS 1.0 形式の情報を取得
        /// </summary>
        /// <param name="xdoc"></param>
        /// <returns></returns>
        private static PublicationInfo GetRss1_0Info(XDocument xdoc)
        {
            var pubInfo = new PublicationInfo();

            if (xdoc.Root.Elements().Any(n => n.Name.LocalName == "channel"))
            {
                if (xdoc.Root.Elements().Where(n => n.Name.LocalName == "channel")
                    .Elements().Any(n => n.Name.LocalName == "publisher"))
                {
                    pubInfo.Sender = xdoc.Root.Elements().Where(n => n.Name.LocalName == "channel")
                                        .Elements().Where(n => n.Name.LocalName == "publisher").FirstOrDefault().Value;
                }
                else if (xdoc.Root.Elements().Where(n => n.Name.LocalName == "channel")
                    .Elements().Any(n => n.Name.LocalName == "creator"))
                {
                    pubInfo.Sender = xdoc.Root.Elements().Where(n => n.Name.LocalName == "channel")
                                        .Elements().Where(n => n.Name.LocalName == "creator").FirstOrDefault().Value;
                }

                if (xdoc.Root.Elements().Where(n => n.Name.LocalName == "channel")
                    .Elements().Any(n => n.Name.LocalName == "description"))
                {
                    pubInfo.Description = xdoc.Root.Elements().Where(n => n.Name.LocalName == "channel")
                                        .Elements().Where(n => n.Name.LocalName == "description").FirstOrDefault().Value;
                }
            }

            var pubInfoItems = new List<PublicationInfoItem>();
            if (xdoc.Root.Elements().Any(n => n.Name.LocalName == "item"))
            {

                var items = xdoc.Root.Elements().Where(n => n.Name.LocalName == "item");
                foreach (var n in items)
                {
                    var pubInfoItem = new PublicationInfoItem();
                    if (n.Elements().Any(w => w.Name.LocalName == "title"))
                    {
                        pubInfoItem.Title = n.Elements().Where(w => w.Name.LocalName == "title").FirstOrDefault().Value;
                    }
                    if (n.Elements().Any(w => w.Name.LocalName == "link"))
                    {
                        pubInfoItem.Link = new Uri(n.Elements().Where(w => w.Name.LocalName == "link").FirstOrDefault().Value);
                    }
                    if (n.Elements().Any(w => w.Name.LocalName == "description"))
                    {
                        pubInfoItem.Description = n.Elements().Where(w => w.Name.LocalName == "description").FirstOrDefault().Value;
                    }
                    if (n.Elements().Any(w => w.Name.LocalName == "date"))
                    {
                        pubInfoItem.PublishDate = DateTime.Parse(n.Elements().Where(w => w.Name.LocalName == "date").FirstOrDefault().Value);
                    }
                    if (n.Elements().Any(w => w.Name.LocalName == "creator"))
                    {
                        pubInfoItem.Creater = n.Elements().Where(w => w.Name.LocalName == "creator").FirstOrDefault().Value;
                    }

                    pubInfoItems.Add(pubInfoItem);
                }
            }
            pubInfo.Items = pubInfoItems;

            return pubInfo;
        }

        /// <summary>
        /// RSS 2.0 形式の情報を取得
        /// </summary>
        /// <param name="xdoc"></param>
        /// <returns></returns>
        private static PublicationInfo GetRss2_0Info(XDocument xdoc)
        {
            var pubInfo = new PublicationInfo();
            var pubInfoItems = new List<PublicationInfoItem>();

            if (xdoc.Root.Elements().Any(n => n.Name == "channel"))
            {
                if (xdoc.Root.Elements().Where(n => n.Name == "channel").Elements().Any(n => n.Name == "copyright"))
                {
                    pubInfo.Sender = xdoc.Root.Elements().Where(n => n.Name == "channel")
                        .Elements().Where(n => n.Name == "copyright").FirstOrDefault().Value;
                }
                if (xdoc.Root.Elements().Where(n => n.Name == "channel").Elements().Any(n => n.Name == "description"))
                {
                    pubInfo.Description = xdoc.Root.Elements().Where(n => n.Name == "channel")
                        .Elements().Where(n => n.Name == "description").FirstOrDefault().Value;
                }

                if (xdoc.Root.Elements().Where(n => n.Name == "channel").Elements().Any(n => n.Name == "item"))
                {
                    var items = xdoc.Root.Elements().Where(n => n.Name == "channel").Elements().Where(n => n.Name == "item");
                    foreach (var n in items)
                    {
                        var pubInfoItem = new PublicationInfoItem();

                        if (n.Elements().Any(w => w.Name == "title"))
                        {
                            pubInfoItem.Title = n.Elements().Where(w => w.Name == "title").FirstOrDefault().Value;
                        }
                        if (n.Elements().Any(w => w.Name == "link"))
                        {
                            pubInfoItem.Link = new Uri(n.Elements().Where(w => w.Name == "link").FirstOrDefault().Value);
                        }
                        if (n.Elements().Any(w => w.Name == "description"))
                        {
                            pubInfoItem.Description = n.Elements().Where(w => w.Name == "description").FirstOrDefault().Value;
                        }
                        if (n.Elements().Any(w => w.Name == "pubDate"))
                        {
                            pubInfoItem.PublishDate = DateTime.Parse(n.Elements().Where(w => w.Name == "pubDate")
                                .FirstOrDefault().Value);
                        }
                        else if (n.Elements().Any(w => w.Name.LocalName == "date"))
                        {
                            pubInfoItem.PublishDate = DateTime.Parse(n.Elements().Where(w => w.Name.LocalName == "date")
                                .FirstOrDefault().Value);
                        }
                        if (n.Elements().Any(w => w.Name.LocalName == "creator"))
                        {
                            pubInfoItem.Creater = n.Elements().Where(w => w.Name.LocalName == "creator").FirstOrDefault().Value;
                        }
                        else if (n.Elements().Any(w => w.Name == "author"))
                        {
                            pubInfoItem.Creater = n.Elements().Where(w => w.Name == "author").FirstOrDefault().Value;
                        }

                        pubInfoItems.Add(pubInfoItem);
                    }
                }
            }

            pubInfo.Items = pubInfoItems;

            return pubInfo;
        }

    }
}

ヘルパー系

次にヘルパー系。プロジェクトに Helpers フォルダを追加します。作るのは「MobileHelper.cs」です。この MobileHelper.cs で実装する IsSmartPhone メソッドでアクセス元がスマートフォンかどうかを判定し、GetViewName メソッドで表示するビュー名を返します。

MobileHelper.cs

using System.Web;
using System.Web.Mvc;

namespace GetExternalXml001.Helpers
{
    static public class MobileHelper
    {
        /// <summary>
        /// アクセス元の種類(スマートフォン, その他)により表示するビュー名を返します。
        /// ビュー名は ControllerContext.RouteData 中のアクション名から取得します。
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        static public string GetViewName(ControllerContext context)
        {
            if (IsSmartPhone(context.HttpContext))
                return context.RouteData.Values["Action"].ToString() + ".Mobile";
            else
                return context.RouteData.Values["Action"].ToString();
        }

        /// <summary>
        /// アクセス元がスマートフォンであるかどうかを返します。
        /// 判断対象のスマートフォンは Android と iPhone になります。
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        static public bool IsSmartPhone(HttpContextBase context)
        {
            return (context.Request.UserAgent.Contains("Android") ||
                    context.Request.UserAgent.Contains("iPhone"));
        }
    }
}

長くなったので、その4へ続けることにします。


ASP.NET MVC 3 で jQuery Mobile を使ってみる (インデックス)


4 thoughts on “ASP.NET MVC 3 で jQuery Mobile を使ってみる(その3)

  1. Thanks for the amazing info. I find these posts have a lot of material. I can’t wait to get a chance to impliment all these great posts. Thank you very much.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です