normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

Windows Azure メディアサービスでバージョンを指定して MediaProcessor を取得する方法

Window Azure メディアサービスでは、用意されたエンコーダやエンクリプタを利用して、動画のエンコーディングや暗号化が可能だ。今回はサンプルコードベースでバージョンを指定した MediaProcessor の取得方法を紹介する。

サンプルコードと実行結果

さっそくサンプルコードを紹介する。NuGet から Windows Azure Media Service のSDKを取得後、以下のコードを作成する

using Microsoft.WindowsAzure.MediaServices.Client;
using System;
using System.IO;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            CloudMediaContext context = new CloudMediaContext("<アカウント名>", "<アカウントキー>");

            Console.WriteLine("Media Processor 一覧");
            foreach (var mediaProcessor in context.MediaProcessors)
            {
                Console.WriteLine(string.Join(",", new object[]{
                    mediaProcessor.Id,
                    mediaProcessor.Name,
                    mediaProcessor.Vendor,
                    mediaProcessor.Version
                }));
            }
            Console.WriteLine("");

            // OrderBy 内の Version 操系で NotSupportedException がでるので、直前で ToList()
            Console.WriteLine("最新の Windows Azure Media Encoder だけ取得");
            var mediaEncoderVerCurrent = context.MediaProcessors.ToList()
                .OrderByDescending(wame => new Version(wame.Version)).FirstOrDefault();
            Console.WriteLine(string.Join(",", new object[]{
                    mediaEncoderVerCurrent.Id,
                    mediaEncoderVerCurrent.Name,
                    mediaEncoderVerCurrent.Vendor,
                    mediaEncoderVerCurrent.Version
                }));
            Console.WriteLine("");

            Console.WriteLine("特定の Windows Azure Media Encoder だけ取得");
            // OrderBy 内の Version 操系で NotSupportedException がでるので、直前で ToList()
            var mediaEncoderVer23 = context.MediaProcessors.ToList()
                .FirstOrDefault(wame => new Version(wame.Version) == new Version("2.3"));
            Console.WriteLine(string.Join(",", new object[]{
                    mediaEncoderVer23.Id,
                    mediaEncoderVer23.Name,
                    mediaEncoderVer23.Vendor,
                    mediaEncoderVer23.Version
                }));
            Console.ReadKey();
        }
    }
}

上記の実行結果は以下となる。エンコーダやエンクリプタの他、ストレージの複合化やパッケージング用の Media Processor もあるらしい。この辺りは Process Assets with the Media Services SDK for .NET - Accessing a Media Processor を参照してほしい。

Media Processor 一覧
nb:mpid:UUID:70bdc2c3-ebf4-42a9-8542-5afc1e55d217,Windows Azure Media Encoder,Microsoft,2.3
nb:mpid:UUID:38a620d8-b8dc-4e39-bb2e-7d589587232b,Windows Azure Media Encryptor,Microsoft,2.4
nb:mpid:UUID:aec03716-7c5e-4f68-b592-f4850eba9f10,Storage Decryption,Microsoft,1.7
nb:mpid:UUID:a2f9afe9-7146-4882-a5f7-da4a85e06a93,Windows Azure Media Packager,Microsoft,2.4
nb:mpid:UUID:2e7aa8f3-4961-4e0c-b4db-0e0439e524f5,Windows Azure Media Encoder,Microsoft,3.1

最新の Windows Azure Media Encoder だけ取得
nb:mpid:UUID:2e7aa8f3-4961-4e0c-b4db-0e0439e524f5,Windows Azure Media Encoder,Microsoft,3.1

特定の Windows Azure Media Encoder だけ取得
nb:mpid:UUID:70bdc2c3-ebf4-42a9-8542-5afc1e55d217,Windows Azure Media Encoder,Microsoft,2.3

Windows Azure Media Services .NET SDK 2.3.0.0 の変更 - 通知APIに Queue ストレージを利用編

前回の記事で id:waritohutsu:20130715:1373856716 複数のストレージアカウントをアタッチする方法を紹介したが、今回は主要更新の一つである通知APIに Queue ストレージを利用する方法を紹介する。詳細な手順やサンプルコード全体は Listening to Windows Azure Media Services Notifications に記載されているため、本記事を通読後に一読することをお勧めする。
現時点でのQueue ストレージを利用した通知APIの考慮点は以下になる。

通知APIでQueue ストレージを利用する

まず、Media Service の通知結果を格納するためのストレージサービスを作成する。同ストレージサービスは Media Service にアタッチされているものでも構わない。以下に実行結果も含めたスクリーンキャプチャなので、構成の参考にしてほしい。

次に、Media Service SDK .NET版を利用した以下の様なC#コード(抜粋で恐縮だが)を作成する。

private static IJob EncodeToMp4(CloudMediaContext context, string inputAssetId)
{

    var inputAsset = context.Assets.Where(a => a.Id == inputAssetId).FirstOrDefault();
    if (inputAsset == null)
        throw new ArgumentException("Could not find assetId: " + inputAssetId);

    var encodingPreset = "H264 Adaptive Bitrate MP4 Set SD 16x9";

    IJob job = context.Jobs.Create("Encoding " + inputAsset.Name + " to " + encodingPreset);

    //通知に利用する方法(Queueストレージ)とエンドポイント(myqueue)を指定
    INotificationEndPoint notificationEndPoint = context.NotificationEndPoints.Create(
            Guid.NewGuid().ToString(), NotificationEndPointType.AzureQueue, "myqueue");

    //全てのイベント(Schduled -> processing -> finished)で通知を行うことを設定
    //job.JobNotificationSubscriptions.AddNew(NotificationJobState.FinalStatesOnly, notificationEndPoint);
    job.JobNotificationSubscriptions.AddNew(NotificationJobState.All, notificationEndPoint);

    IMediaProcessor latestWameMediaProcessor = (from p in context.MediaProcessors where p.Name == "Windows Azure Media Encoder" select p).ToList()
                                                                 .OrderBy(wame => new Version(wame.Version)).LastOrDefault();

    ITask encodeTask = job.Tasks.AddNew("Encoding", latestWameMediaProcessor, encodingPreset, TaskOptions.None);

    encodeTask.InputAssets.Add(inputAsset);
    encodeTask.OutputAssets.AddNew(inputAsset.Name + " as " + encodingPreset, AssetCreationOptions.None);

    job.StateChanged += new EventHandler<JobStateChangedEventArgs>(JobStateChanged);
    job.Submit();
    job.GetExecutionProgressTask(CancellationToken.None).Wait();

    return job;
}

現時点では NotificationEndPointType.AzureQueue しか通知の方法が用意されていないが、今後は通知方法の拡張が期待される。また、通知情報は"完了時のみ(NotificationJobState.FinalStatesOnly)" or "イベント毎の通知(NotificationJobState.All)" or "通知しない(NotificationJobState.None)" を選択が可能だ。

上記のコードを含めた処理を実行した結果、以下のJSON形式の文字列がQueueストレージに格納される。「1.登録 → 2.スケジュール → 3.処理中 → 4.完了」の流れが格納されていることが確認できる。

{"MessageVersion":"1.0","EventType":"NotificationEndPointRegistration","ETag":"a756c1f67e96d95273e38e5e5e918a77cbef37bdfaded6069eb0a8bd800bae81","TimeStamp":"2013-07-21T02:08:04","Properties":{"NotificationEndPointId":"nb:nepid:UUID:4e4199b8-3db1-43ea-851b-95576d17e60b","State":"Registered","Name":"8822be8d-2791-4ad0-a991-54ef1b74388a","Created":"2013-07-21T02:08:01"}}

{"MessageVersion":"1.0","EventType":"JobStateChange","ETag":"f9cb50428bc9927d4f637d8efe9a82aa3de0c65ded214cde17ef378d5388e218","TimeStamp":"2013-07-21T02:08:17","Properties":{"JobId":"nb:jid:UUID:10a68b86-fe65-b943-bd2d-6ad2ba1cd3aa","JobName":"Encoding azure to H264 Adaptive Bitrate MP4 Set SD 16x9","NewState":"Scheduled","OldState":"Queued","AccountName":"xxxxxxxxxxxxxxxxxxx"}}

{"MessageVersion":"1.0","EventType":"JobStateChange","ETag":"b70613d1c78821f96a05bda3c7ec16623424441ecd37b0b9417cbaa70761faab","TimeStamp":"2013-07-21T02:08:22","Properties":{"JobId":"nb:jid:UUID:10a68b86-fe65-b943-bd2d-6ad2ba1cd3aa","JobName":"Encoding azure to H264 Adaptive Bitrate MP4 Set SD 16x9","NewState":"Processing","OldState":"Scheduled","AccountName":"xxxxxxxxxxxxxxxxxxx"}}

{"MessageVersion":"1.0","EventType":"JobStateChange","ETag":"74830e61be956a9c7566585d09dec148365806b42b9813405d4f97a1e1b3861a","TimeStamp":"2013-07-21T02:09:36","Properties":{"JobId":"nb:jid:UUID:10a68b86-fe65-b943-bd2d-6ad2ba1cd3aa","JobName":"Encoding azure to H264 Adaptive Bitrate MP4 Set SD 16x9","NewState":"Finished","OldState":"Processing","AccountName":"xxxxxxxxxxxxxxxxxxx"}}

下記のJSONオブジェクトを利用する場合は個別にクラスを作成する必要があるので、以下どちらかの手法を利用してほしい。

また、キューストレージのエンドポイントが不正な形式の場合、以下のレスポンスが帰ってくるので参考にしてほしい。

"<?xml version=\"1.0\" encoding=\"utf-8\"?><m:error xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\"><m:code /><m:message xml:lang=\"en-US\">NotificationEndPoint.EndPointAddress must match rules described in http://msdn.microsoft.com/en-us/library/windowsazure/dd179349.aspx</m:message></m:error>"

Windows Azure Media Services .NET SDK 2.3.0.0 の変更 - 複数のストレージアカウントをアタッチ編

2013/7/1 に NuGet にて Windows Azure Media Service 2.3.0.0 がリリースされた。今回のリリースにおける主要な更新は以下となる。

こちらの メディア with Microsoft - Windows Azure Media Services .NET SDK 2.3.0.0 の更新 記事にも SDK 2.3.0.0 の更新内容がまとまっているので、別途参照して頂きたい。

複数のストレージアカウントをアタッチする方法

サーバ側には複数のストレージアカウントをアタッチする機能が追加されたが、現時点(2013/7/15)では管理ポータルにも.NET SDK にも本機能を利用するインターフェースは用意されておらず、REST API を利用することによってのみ本機能が利用可能だ。
そのため、今回はHow to: Use Media Services Management REST API に記載されたクラスを参考にしてREST API を操作するコードを以下のように作成する。

ManagementRESTAPIHelper helper =
new ManagementRESTAPIHelper("https://management.core.windows.net",
    "<証明書のThumbprintを記載。管理ポータルを参照のこと>",
    "<上記の証明書に紐づいたサブスクリプションID。同じく管理ポータルを参照のこと>");

// Initialize the AccountInfo class.
MediaServicesAccountInfo accountInfo = new MediaServicesAccountInfo();
accountInfo.MediaServicesAccountName = "<メディアサービスのアカウント名>";
accountInfo.Region = "<メディアサービスを作成したリージョン例:East Asia>";
accountInfo.StorageAccountName = "<ストレージサービスにアタッチしたストレージアカウント名>";
accountInfo.StorageAccountKey = "<ストレージサービスにアタッチしたストレージアカウントのキー>";
accountInfo.BlobStorageEndpoint = "<BLOBストレージのエンドポイント 例:http://xxxxxxxxxxxxxxx.blob.core.windows.net/>";

var storageAccount = new AttachStorageAccountRequest()
{
StorageAccountName = "<アタッチしたいストレージアカウント名>",
StorageAccountKey = "<アタッチしたいストレージアカウントのキー>",
BlobStorageEndpointUri = "<アタッチしたいBLOBストレージのエンドポイント>"
};

Call one of the public methods and attach storage account to media service 
helper.AttachStorageAccountToMediaServiceAccount(accountInfo, storageAccount);

内部では直接REST API を叩いているため、特に Media Service SDK を取得しなくても操作は可能だ。上記のパラメータのうち、証明書に関する情報を取得する方法を以下に図でまとめたので参考にしてほしい。

次に、上記のコードをコンソールアプリケーション等で実行する。処理が正常に実行された場合は以下のメッセージが表示される(別に parimary key が再生成された様子はないがなんでだろう?)。

The primary key was regenerated.

また、すでに自身のメディアサービスにアタッチ済みのストレージアカウントを再アタッチしようとした場合は System.Net.WebException が発生し、以下のメッセージが返される。

{"リモート サーバーがエラーを返しました: (409) 競合"}

System.Net.WebException.Response.StatusCode は System.Net.HttpStatusCode.Conflict で返ってくるので、必要ならば参考にしてほしい。

REST API の処理が完了した後、数十秒後(直後では反映されていなかった)に以下のコードを実行するとメディアサービスに複数のストレージサービスがアタッチされていることが確認できる。また以下のコードは Media Service .NET SDK を利用している。

CloudMediaContext context = new CloudMediaContext("<メディアサービスのアカウント名>", "<メディアサービスのキー名>");

foreach (var account in context.StorageAccounts)
{
Console.WriteLine("Name={0}, IsDefault={1}", account.Name, account.IsDefault);
}

実行結果は以下となる(二つのストレージアカウントを紐づけた後に上記のコードを実行した)。最初にメディアサービスに紐づけたストレージアカウントがデフォルトとして扱われる点に注意してほしい。

Name=xxxxxxmediaservicestorage1, IsDefault=False
Name=xxxxxxmediaservicestorage2, IsDefault=True
Name=xxxxxxmediaservicestorage3, IsDefault=False

参考

Windows版のFirefoxは ver.21 からデフォルトで H.264形式動画が再生可能に

今回は Windows Azure Media Service をいじっていて得た Windows版 Firefox に関する情報を備忘録として記載する。
実は Windows 版に限るが、FirefoxH.264 動画再生については以下の環境になっている様だ

さっそく Firefox ver.22 × Windows 8 で動作検証

以下のコードを利用し、Firefox ver.22 で H.264 動画を再生した。

<!DOCTYPE HTML>
<html>
<head>
<title>サンプルページ</title>
</head>
<body>
<h2>動画</h2>
<video style="width:400pt;" src="mymovie.mp4" controls></video>
</body>
</html>

配置したのは H.264 の動画のみでありOGGやWebM系の動画は配置していないが、以下の様に動画再生が可能だった。

HTML5 な動画再生については…

まだ色々ありますな(汗

MultiTouch Behavior を利用して Silverlight でゼスチャー操作をしてみる

今回は SilverlightWPF に対して ゼスチャー操作を利用可能にする MultiTouch Behavior について紹介する。

MultiTouch Behavior のインストール

MultiTouch Behavior パッケージは NuGet で利用可能であり、パッケージマネージャコンソールで以下のコマンドを入力することで利用可能になる。

PM> Install-Package MultiTouchBehaviors 

といいつつ、まだまだバージョンは 0.6.9 な辺りでどうなんだろうかと思ったり(汗。インストールして確認したところ、どうやら Silverlight 4 向けにビルドされたらしく Silverlight 5 では利用できなかった。。。。
仕方がないので CodePlex - Windows Phone, Silverlight and WPF Multi-Touch Manipulations から最新版のソースコードを取得し、Silverlight 5 向けにビルドしなおすことで MultiTouch Behavior を Silverlight 5 でも利用可能となった。

MultiTouch Behavior を利用したアプリケーソン

特にこだわりがなければ、まずは一旦 Silverlight 4 アプリケーションを新規に作成し、NuGet を利用して MultiTouch Behavior をインストールする。更に、 XAML を以下のように書き換えて Silverlight アプリケーションを起動する。

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:behaviors="clr-namespace:MultiTouch.Behaviors.Silverlight4;assembly=MultiTouch.Behaviors.Silverlight4"
    xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    mc:Ignorable="d"
    d:DesignHeight="600" d:DesignWidth="800">

    <Grid x:Name="LayoutRoot" Background="White">
        <Canvas>
            <Image Source="Images/P1000555.JPG" Width="675" Canvas.Left="61" Canvas.Top="43" Height="506">
                <interactivity:Interaction.Behaviors>
                    <behaviors:MultiTouchBehavior IsRotateEnabled="True" IsScaleEnabled="True" 
                                              MinimumScale="100" MaximumScale="500"  />
                </interactivity:Interaction.Behaviors>
            </Image>
        </Canvas>
    </Grid>
</UserControl>

アプリケーション後、回転・拡大・移動のゼスチャ操作を行うことが可能となる。

また、ソースコード内を確認したところ、Touch系のイベントが存在しない PC向け Silverlight は、マウス系のイベントを美味く利用して対応しているようだ。興味のある方はソースコードを参照しても良いだろう。

参考サイト

利用可能な操作等々は以下の動画を参考にして頂ければよいだろう。

JSR 303 Bean Validator を使ってみる

JavaEE6 から? JSR の標準に取り込まれた JSR 303 Bean Validatorを使ってみた。JavaEE5 時代は Hibernate Validator としてOSSとして実装されていたものが JavaEE6 では Bean Validator として標準仕様として取り込まれている(中身の実装はやはり Hibernate Validator だが)。今回は使えるまでの手順ベースで手短にささっと書いてしまうが、参考になれば幸いだ*1

依存ライブラリのインストール

まずは Bean Validator に依存しているライブラリを解決する。本体である validation-api.jar(インターフェース部分) と hibernator-validator.jar(実装部分)はもちろん必要だが、単体環境でBean Validator を利用する場合は el-api.jar や el-impl.jar も必要な点に注意して頂きたい。TomcatGlassFish、JBoss AS 等の AP サーバは独自に el-api.jar, el-impl.jar を持っているが、単体環境で実行する場合はこれらの依存関係も解決してやる必要がある。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mydomain.sample.study</groupId>
	<artifactId>beanvalidator-sample</artifactId>
	<packaging>jar</packaging>
	<version>0.9.0-SNAPSHOT</version>
	<name>Bean Validator Sample</name>
	<dependencies>
		<!-- http://mvnrepository.com/artifact/javax.validation/validation-api/1.1.0.Final -->
		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>1.1.0.Final</version>
		</dependency>
		<!-- http://mvnrepository.com/artifact/org.hibernate/hibernate-validator/5.0.1.Final -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>5.0.1.Final</version>
		</dependency>
		<!-- http://mvnrepository.com/artifact/javax.el/el-api/2.2 -->
		<dependency>
			<groupId>javax.el</groupId>
			<artifactId>el-api</artifactId>
			<version>2.2</version>
		</dependency>
		<!-- http://mvnrepository.com/artifact/org.glassfish.web/el-impl/2.2 -->
		<dependency>
			<groupId>org.glassfish.web</groupId>
			<artifactId>el-impl</artifactId>
			<version>2.2</version>
		</dependency>
	</dependencies>
</project>

サンプルコード

Javaソースコードを記載する。DTOクラスのプロパティに対し、@Size や @Min のアノテーションを付与してバリデーション属性を指定する。以下の例では @Size で3文字以上かつ20文字以下の文字列を許可文字列とし、エラーメッセージも別途指定してる。また、@Min で0未満の値は入力できないようにしている。

package com.mydomain.sample.study.dto;

import javax.validation.constraints.Min;
import javax.validation.constraints.Size;

public class PersonDto {
	@Size(min = 3, max = 20)
	private String name;

	@Min(value = 0, message = "流石に  0 歳以下は無いと思いますので、{value} は嘘じゃないかと…")
	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

以下は PersonDto に対して不正な値を入力したうえでバリデーションを実行するソースコードになる。

package com.mydomain.sample.study;

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import com.mydomain.sample.study.dto.PersonDto;

public class Main {
	public static void main(String[] args) {
		PersonDto dto = new PersonDto();
		dto.setAge(-10);
		dto.setName("ああ");

		ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
		Validator v = vf.getValidator();
		Set<ConstraintViolation<PersonDto>> results = v.validate(dto);
		for (ConstraintViolation<PersonDto> result : results) {
			System.out.println(result.getMessage());
		}
	}
}

サンプルの実行

サンプルの実行結果は以下になる。バリデーションが実行されているのが確認できると思う。

6 08, 2013 11:08:19 午前 org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 5.0.1.Final
size must be between 3 and 20
流石に  0 歳以下は無いと思いますので、0 は嘘じゃないかと…

*1:自分がちょこちょこ確認するるので備忘録気味

RazorEngine の使い方 〜それなりな応用編 その2〜

前回紹介した RazorEngine のアセンブリ参照方法 id:waritohutsu:20130514:1368525761 について http://jsakamoto.tumblr.com/post/50628186640/razorengine-wa-na のコメントを頂いたので、実際に試してみた結果を共有する。

コンソールアプリの場合

コンソールアプリの場合、前回の投稿で記載した通り、Assembly.LoadFrom()等で明示的にサードパーティ製のアセンブリを読み込むことで対応が可能な様だ。以下のコードを利用することで、RazorEngine内でMarkdownSharpのアセンブリを利用している。また、出力時のHTMLエスケープ処理を抑止するために @Raw() メソッドを利用している点に留意して欲しい。

namespace RazorTemplateConsoleApp
{
   public class Person
    {
        public string Name { get; set; }
        public uint Age { get; set; }
        public IList<string> FavoriteTechs { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Assembly.LoadFrom(Path.Combine(Directory.GetCurrentDirectory(), "MarkdownSharp.dll"));

                var viewBag = new DynamicViewBag();
                viewBag.AddValue("Comment", @"
# 見出し h1
## 見出し h2
### 見出し h3");
                Console.WriteLine(RazorEngine.Razor.Parse(@"@{
var str = new MarkdownSharp.Markdown().Transform(ViewBag.Comment);
}
@Raw(str)", STR_LIST, viewBag, "mycache")); //キャッシュ名がnullだと落ちる
            }
            catch (Exception e)
            {
                Console.WriteLine("ここは通らないと思われる:" + e.Message);
            }
        }
    }
}

アプリケーションの出力結果は以下となる。

<h1>見出し h1</h1>

<h2>見出し h2</h2>

<h3>見出し h3</h3>

ASP.NET MVC の場合

ASP.NET の場合は BuildManager が動的にアセンブリを読み込んでいるため、サードパーティ製のアセンブリを利用する場合でも明示的なアセンブリの読み込み処理は不要だ。以下のコードでも問題なく動作するので、是非確認して欲しい。また、こちらでも出力時のHTMLエスケープ処理を抑止するために @Raw() メソッドを利用している。

  • HomeController.cs の抜粋
    public class HomeController : Controller
    {
        private readonly string STR_LIST = "";
        public ActionResult Index()
        {
            var viewBag = new DynamicViewBag();
            viewBag.AddValue("Comment", @"
# 見出し h1
## 見出し h2
### 見出し h3");
            ViewBag.Message = RazorEngine.Razor.Parse(@"@{
var str = new MarkdownSharp.Markdown().Transform(ViewBag.Comment);
}
@Raw(str)", STR_LIST, viewBag, "mycache"); //キャッシュ名がnullだと落ちる

            return View();
        }
  • Index.cshtml の抜粋
@{
    ViewBag.Title = "Home Page";
}
@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>

出力結果は以下となる。

BuildManager の参考

ASP.NET MVC の内部に踏み込んでいるわけではないが、BuildManager の機能の参考にはなるだろう