ラベル C# の投稿を表示しています。 すべての投稿を表示
ラベル C# の投稿を表示しています。 すべての投稿を表示

2020年9月17日木曜日

MagicOnionはIISにはホストできない?

MagicOnionを試してみたくてIISにホストする方法を探しておりましたが… 

ASP.NET Core 3.0 アプリケーションは Windows Server 上の IIS でホストできますが、HTTP/2 の機能の一部がサポートされていないため、現在 IIS で gRPC アプリケーションをホストすることはできません。 この機能は、Windows サーバーの将来の更新の目標です。
アプリケーションは、Windows サービスとして実行できます。 または、.NET Core 3.0 ホスティング拡張機能の新機能により、 systemdによって制御される Linux サービスとして実行することもできます。

InProcessではなくOutOfProcessなら回避する方法があるのでしょうか。
いずれにしても別の方法を検討する必要がありそうです。疲れたのでここまで。

2019年7月10日水曜日

【C#】varで宣言された変数の定義へ移動【Visual Studio】

varにマウスポインタをフォーカスしてF12

var variable = Hoge();

のようなコードでvariableにフォーカスしてF12を押してもここが変数宣言をしている場所なのでクラスの定義に飛んでくれません。
varにフォーカスしてF12を押せば飛んでくれます。

たぶん常識中の常識なんでしょうが初めて知りました。。。

以上メモまで。

2017年12月4日月曜日

YouTube Data API を使って YouTube 再生リストを高評価順でソートできる表を作る【C#】

試作品

作ったものはこちら
引数つきでもアクセスできます。
http://tools.ehost.jp/youtube/Playlist/index/PLUL2Tx9e4jQxRfYXbOQB0UYYFXg_z-Yc6


概要

Youtube はチャンネル内の動画は「並べ替え」→「人気の動画」でソートすることができますが、再生リスト単位ではできません。
再生リストのキーを元に Youtube API から再生リストの動画情報を取得し、jQuery プラグインの DataTables でソートできるようにします。
https://www.youtube.com/playlist?list=PLUL2Tx9e4jQxRfYXbOQB0UYYFXg_z-Yc6
の list?= 以下「PLUL2Tx9e4jQxRfYXbOQB0UYYFXg_z-Yc6」が再生リストのキーです。


準備

まずはオフィシャルのドキュメントをご覧頂くのが良いかと思います。


Youtube API を利用するにはまず API キーを取得する必要があります。
API キーはリファラーなどでアクセス制限をつけることができます。

今回は C# と .NET用のGoogle APIのクライアントライブラリを使用します。
nuget パッケージで Google.Apis.YouTube.v3 をプロジェクトにインストールします。


Youtube API

https://www.googleapis.com/youtube/v3/videos?id=28LHGS9hn_g&part=snippet,contentDetails,statistics,status&key=取得したAPIキー
API キーを取得すれば上記のようなURLにアクセスすることで以下のような情報を取ってくる事ができるようになります。

{
 "kind": "youtube#videoListResponse",
 "etag": "\"7991kDR-QPaa9r0pePmDjBEa2h8/IUqB8ABuJ4Ti-yT_NEhPLuW8lKY\"",
 "pageInfo": {
  "totalResults": 1,
  "resultsPerPage": 1
 },
 "items": [
  {
   "kind": "youtube#video",
   "etag": "\"7991kDR-QPaa9r0pePmDjBEa2h8/-iOhnLdrI3sM37vS6XdtYJPit1s\"",
   "id": "28LHGS9hn_g",
   "snippet": {
    "publishedAt": "2014-12-24T06:16:45.000Z",
    "channelId": "UCwQNrx9GQZamvdxVuVgaMtQ",
    "title": "猫 赤ちゃんと初対面Cat First Meeting with Baby",
    "description": "出産のため入院していた嫁と赤ちゃんが帰ってきました。\n嫁との1週間ぶりの対面、赤ちゃんとの初対面。\nさて、シイの反応やいかに・・・?\n\n【日記:http://www.neko-jirushi.com/user_home/diary/diary_detail-84394-46706.html】\n【ブログ:http://catlife0601.blog.fc2.com/】",
    "thumbnails": {
     "default": {
      "url": "https://i.ytimg.com/vi/28LHGS9hn_g/default.jpg",
      "width": 120,
      "height": 90
     },
     "medium": {
      "url": "https://i.ytimg.com/vi/28LHGS9hn_g/mqdefault.jpg",
      "width": 320,
      "height": 180
     },
     "high": {
      "url": "https://i.ytimg.com/vi/28LHGS9hn_g/hqdefault.jpg",
      "width": 480,
      "height": 360
     },
     "standard": {
      "url": "https://i.ytimg.com/vi/28LHGS9hn_g/sddefault.jpg",
      "width": 640,
      "height": 480
     }
    },
    "channelTitle": "catlife",
    "tags": [
     "猫",
     "Cat",
     "シイ",
     "ペット",
     "動物"
    ],
    "categoryId": "15",
    "liveBroadcastContent": "none",
    "localized": {
     "title": "猫 赤ちゃんと初対面Cat First Meeting with Baby",
     "description": "出産のため入院していた嫁と赤ちゃんが帰ってきました。\n嫁との1週間ぶりの対面、赤ちゃんとの初対面。\nさて、シイの反応やいかに・・・?\n\n【日記:http://www.neko-jirushi.com/user_home/diary/diary_detail-84394-46706.html】\n【ブログ:http://catlife0601.blog.fc2.com/】"
    }
   },
   "contentDetails": {
    "duration": "PT1M45S",
    "dimension": "2d",
    "definition": "hd",
    "caption": "false",
    "licensedContent": true,
    "projection": "rectangular"
   },
   "status": {
    "uploadStatus": "processed",
    "privacyStatus": "public",
    "license": "youtube",
    "embeddable": true,
    "publicStatsViewable": true
   },
   "statistics": {
    "viewCount": "1482776",
    "likeCount": "2274",
    "dislikeCount": "81",
    "favoriteCount": "0",
    "commentCount": "59"
   }
  }
 ]
}

取得できるデータはリソースと呼ばれ以下の種類があります。

リソースの種類

activity 特定のユーザーが YouTube サイトで行った操作に関する情報が格納されています。アクティビティ フィードで報告されるユーザー操作は、動画の評価、動画の共有、お気に入りへの動画の追加、チャンネルのお知らせメッセージの投稿などです。
channel 単一の YouTube チャンネルに関する情報が格納されています。
channelBanner 新しくアップロードされた画像をチャンネル用のバナー画像として設定するために使う URL を示します。
guideCategory コンテンツや人気などの指標に基づいて YouTube がチャンネルに関連付けるカテゴリを示します。guideCategory により、YouTube ユーザーが目的のコンテンツを容易に見つけられるようにチャンネルを整理できます。チャンネルは 1 つ以上のガイド カテゴリに関連付けられる場合がありますが、何らかのガイド カテゴリに属することが保証されているわけではありません。
playlist 単一の YouTube 再生リストを表します。再生リストとは、順序を付けて表示し、他のユーザーと共有できる動画のコレクションを指します。
playlistItem 再生リストを構成する動画などのリソースを示します。また、playlistItem リソースには、対象となるリソースが再生リストでどのように使用されるのかを説明する詳細な情報も格納されています。
search result API リクエストで指定した検索パラメータに一致する YouTube 動画、チャンネル、または再生リストに関する情報が格納されています。検索結果は、動画など一意に識別可能なリソースを出力しますが、検索結果自体は永続的なデータを持ちません。
subscription YouTube ユーザーの登録チャンネルに関する情報が格納されています。subscription は、新しい動画がチャンネルに追加された場合や、別のユーザーが YouTube で動画のアップロード、動画の評価、動画へのコメントといった何らかの操作を行った場合に、ユーザーに通知します。
thumbnail リソースに関連付けられたサムネイル画像を示します。
video 単一の YouTube 動画を表します。
videoCategory アップロードした動画に関連付けられているか、関連付けることができるカテゴリを示します。


API に指定できるパラメータはリソースによって異なりますが、主な2つを紹介します。

part パラメータ

必須項目です。カンマ区切りで複数指定することができ、上記 videos リソースの例で言うと、part=snippet,contentDetails,statistics,status の部分です。
取得するデータの items 要素の中身でどの部分を取得するかを指定します。


fields パラメータ

カンマ区切りで複数指定することができ、fields=pageInfo,items(snippet/title,snippet/thumbnails/default) のように記述します。
part パラメータでは子要素を全て含みますが、その内容をさらに詳細にフィルタリングすることができます。
(正直 part と fields は冗長な気もいたしますが…)


.NET用のGoogle APIのクライアントライブラリを使用したデータ取得

.NET用のGoogle APIのクライアントライブラリのガイドやリファレンスはこちら


やりたい事は YouTube 再生リスト内の動画を評価順にソートすることなのですが、まず playlist リソースから再生リストの情報を取ってきます。
続いて playlistItem リソースから再生リスト内の動画情報を取得しますが、この動画情報の中には評価は含まれていません。
面倒ではありますが、いったん動画の Id を取得するにとどめて、その動画 Id で videos リソースから評価情報を取得いたします。
それぞれリソースから取得できる件数は最大50件までなので、50件を越える場合には複数回リソースにアクセスして取得することになります。
コードの一例は以下の通りです。

//YouTubeServiceクラスを生成
var youtube = new YouTubeService(new BaseClientService.Initializer()
{
    ApiKey = "APIキー"
});

//Playlist検索
var requestPlaylist = youtube.Playlists.List("snippet");
requestPlaylist.Id = key;
requestPlaylist.Fields = "pageInfo,items(snippet/title,snippet/thumbnails/default)";
var responsePlaylist = requestPlaylist.Execute();
var vm = responsePlaylist.Items.Select(x => new PlaylistViewModel()
{
    Id = key,
    Title = x.Snippet.Title,
    Url = "https://www.youtube.com/playlist?list=" + key,
    ThumbnailUrl = x.Snippet.Thumbnails.Default__.Url,
    Videos = new List<videoviewmodel>()
}).FirstOrDefault();

//Playlist内の動画id一覧取得
List<string> videoIds = new List<string>();
var requestPlaylistItems = youtube.PlaylistItems.List("id,contentDetails");
var responsePlaylistItems = new PlaylistItemListResponse();
requestPlaylistItems.PlaylistId = key;
requestPlaylistItems.MaxResults = 50;
requestPlaylistItems.Fields = "nextPageToken,pageInfo,items(contentDetails/videoId)";
do
{
    requestPlaylistItems.PageToken = responsePlaylistItems.NextPageToken;
    responsePlaylistItems = requestPlaylistItems.Execute();
    videoIds.AddRange(responsePlaylistItems.Items.Select(x => x.ContentDetails.VideoId));
} while (responsePlaylistItems.NextPageToken != null && responsePlaylistItems.NextPageToken != "");

//動画情報の取得
var requestVideos = youtube.Videos.List("snippet,statistics");
var responseVideos = new VideoListResponse();
var size = 50;
var currentPage = 1;
var totalCount = videoIds.Count;
requestVideos.MaxResults = size;
requestVideos.Fields = "items(id,snippet/publishedAt,snippet/title,snippet/thumbnails/default,statistics/likeCount,statistics/viewCount)";
do
{
    requestVideos.Id = string.Join(",", videoIds.Skip(size*(currentPage-1)).Take(size));
    responseVideos = requestVideos.Execute();
    vm.Videos.AddRange(
        responseVideos.Items.Select(x => new VideoViewModel
        {
            Id = x.Id,
            Title = x.Snippet.Title,
            LikeCount = (int)x.Statistics.LikeCount,
            ViewCount = (int)x.Statistics.ViewCount,
            PublishedAt = (DateTime)x.Snippet.PublishedAt,
            ThumbnailUrl = x.Snippet.Thumbnails.Default__.Url,
            Url = "https://www.youtube.com/watch?v=" + x.Id
        })
        );
    currentPage++;
} while (totalCount > size * (currentPage - 1));

上記では playlistItems からいったん動画 Id のリストを全て取得しましたが、1ページ分(50件)取得する毎に videos リソースからデータを取ってきた方が楽そうですね。

(参考)




2016年10月18日火曜日

GmailのSMTP経由でメールを送信する【C#】

C#を使ってGmailのSMTP経由でメールを送信する方法は以下をご参考ください。

DOBON.NET様サイト SSL/TLSを使用してSMTPでメールを送信する


そして更にGoogleアカウント側で設定が必要です。

安全性の低いアプリがアカウントにアクセスするのを許可する

安全性の低いアプリ



以上メモまで

2015年8月26日水曜日

【Visual Studio】自動生成したコードをプロジェクトに追加する時にエラー

T4Templateなどでファイルを自動生成し、プロジェクトに読み込ませるために
以下のようなコードを実行いたします。

using Microsoft.Build.Evaluation;

Project project = new Project(projectFileName);
if (!project.Items.Any(x => x.ItemType == "Compile" && x.UnevaluatedInclude == generatedFileName))
{
    project.AddItem("Compile", generatedFileName);
    project.Save();
}

ところが、これを同一プロジェクトに対し複数回実行するとエラーとなり
以下のようなメッセージが出ます。


等価なプロジェクト (グローバル プロパティとツール バージョンが同じプロジェクト) がプロジェクト コレクションのパスxxxに既に存在します。 等価なプロジェクトをこのプロジェクト コレクションに読み込むには、最初にこのプロジェクトをアンロードしてください。


ということでアンロードします。

ProjectCollection.GlobalProjectCollection.UnloadProject(project);


(参考)
http://codeyourself.net/microsoft-build-evaluation-project-loading-oddities/

以上メモまで。

2015年2月8日日曜日

【C#】都道府県コード順の列挙型都道府県

public enum Prefecture
    {
        北海道 = 1,
        青森県 = 2,
        岩手県 = 3,
        宮城県 = 4,
        秋田県 = 5,
        山形県 = 6,
        福島県 = 7,
        茨城県 = 8,
        栃木県 = 9,
        群馬県 = 10,
        埼玉県 = 11,
        千葉県 = 12,
        東京都 = 13,
        神奈川県 = 14,
        新潟県 = 15,
        富山県 = 16,
        石川県 = 17,
        福井県 = 18,
        山梨県 = 19,
        長野県 = 20,
        岐阜県 = 21,
        静岡県 = 22,
        愛知県 = 23,
        三重県 = 24,
        滋賀県 = 25,
        京都府 = 26,
        大阪府 = 27,
        兵庫県 = 28,
        奈良県 = 29,
        和歌山県 = 30,
        鳥取県 = 31,
        島根県 = 32,
        岡山県 = 33,
        広島県 = 34,
        山口県 = 35,
        徳島県 = 36,
        香川県 = 37,
        愛媛県 = 38,
        高知県 = 39,
        福岡県 = 40,
        佐賀県 = 41,
        長崎県 = 42,
        熊本県 = 43,
        大分県 = 44,
        宮崎県 = 45,
        鹿児島県 = 46,
        沖縄県 = 47

    }


ASP.NET MVC 5.1では、Razorに列挙型の情報をもとにドロップダウンリストボックスを生成するEnumDropDownListメソッドが追加されました。
上記のような都道府県の列挙型クラスを用意して以下のように渡してやれば

@Html.EnumDropDownListFor(model => model.Prefecture)

以下のように展開されます。

<select data-val="true" id="Prefecture" name="Prefecture">
<option value="1">北海道</option>
<option value="2">青森県</option>
<option value="3">岩手県</option>
<option value="4">宮城県</option>
<option value="5">秋田県</option>
・
・
・
</select>

列挙型にいちいち値を指定しているのは、都道府県コードに一致させたいためです。
(値を指定しないと列挙型の内部的な値は0,1,2・・・となります)
HTMLに展開された時にドロップダウンリストのvalueの値が列挙型の内部の値になるため、
都道府県コードと合わせておいてやるとAjaxZip3なんかの郵便番号自動補完に対応することができます。


2014年4月9日水曜日

【Visual Studio 2012】Unit Test Generator拡張機能 "controller must have testable element property setup"

Unit Test Generator拡張機能をインストールし、早速実行してみたところ

『controller must have testable element property setup』

というエラーメッセージが出てしまいました。

Unit Test Generator で Test Method を作成するには
1.public Class であること
2.public Method であること
が条件です。

VSでクラスを作成するとデフォルトでアクセス修飾子の無いクラス(internal)になるので
エラーとなってしまいました。

以上メモまで。

(参考)
アクセス修飾子 (C# リファレンス)
http://msdn.microsoft.com/ja-jp/library/wxh6fsc7.aspx

2013年12月4日水曜日

【LINQ to XML】名前空間の記述でエラー

LINQ to XML でXMLを操作する際に
「System.Xml.XmlException: ':' 文字、16 進数値 0x3A を名前に含むことはできません。」
というエラーが吐出されて困ったのでメモ。

エラーになったのは以下のようなコード(C#)

var xml = new XElement("Root", "Root Value", 
              new XAttribute("xml:lang","ja"));

コードとエラーを見て頂ければ分かると思いますが、
「:」を属性名で使ってはいけないということです。
(要素名にも使ってはいけません)
 「xml:lang」の「xml」の部分は名前空間として記述しなくてはなりません。

以下が修正したコードです。

var xml = new XElement("Root", "Root Value", 
              new XAttribute(XNamespace.Xml + "lang","ja"));

これで以下のようなXMLが作成されます
Root Value 


(参考)
XNamespaceクラス
http://msdn.microsoft.com/ja-jp/library/system.xml.linq.xnamespace(v=vs.110).aspx

2013年4月5日金曜日

MVC Music Store で Edit ができない

ASP.NET MVC のサンプルアプリケーションとして紹介されている
「MVC Music Store」ですが、現在ASP.NET MVC3に対応したバージョン3が公開されております。

【ASP.NET】MVC Music Store Tutorial
http://www.asp.net/mvc/tutorials/mvc-music-store

【CodePlex】MVC Music Store
http://mvcmusicstore.codeplex.com/

ただし、チュートリアル通りに進めていくと、管理画面のEditのところで、
OptimisticConcurrencyExceptionが発生しエラーとなってしまいます。


原因は Album Model が AlbumId をバインドしないようになっているからです。

Album.cs
namespace MvcMusicStore.Models
{
    [Bind(Exclude = "AlbumId")]  //←ココ
    public class Album
    {
        [ScaffoldColumn(false)]
        public int AlbumId { get; set; }

        [DisplayName("Genre")]
        public int GenreId { get; set; }
...


解決するには、この部分を消してしまうか、
Album.cs
namespace MvcMusicStore.Models
{
    //[Bind(Exclude = "AlbumId")]
    public class Album
    {
        [ScaffoldColumn(false)]
        public int AlbumId { get; set; }

        [DisplayName("Genre")]
        public int GenreId { get; set; }
...

コントローラー側で別途バインドしてやる必要があります。
StoreManagerController.cs
namespace MvcMusicStore.Models
{
    [Authorize(Roles = "Administrator")]
    public class StoreManagerController : Controller
    {
        ...

        //
        // POST: /StoreManager/Edit/5

        [HttpPost]
        public ActionResult Edit(Album album, int id)
        {
            if (ModelState.IsValid)
            {
                album.AlbumId = id;  //←ココ
                db.Entry(album).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
            ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
            return View(album);
        }

        ...


【参考】
http://mvcmusicstore.codeplex.com/workitem/6648

2012年1月10日火曜日

ASP.NET Enterキーを無効にする方法

Enterキーを無効にする方法として
以下のようなjavascriptを差し込む方法がよく紹介されていますが、

<SCRIPT LANGUAGE=javascript>
    <!--
    //Enterキー無効処理
    document.onkeypress = enter;
    function enter(){
        if( window.event.keyCode == 13 ){
            if( window.event.srcElement.type == 'text'){
                return false;
            }
        }
    }
    //-->
</SCRIPT>

ASP.NET 3.5、IIS7、マスターページ、AjaxControlToolkit 3.0 という環境でヘッダのContetPlaceHolderに上記javascriptを記述しても動作しませんでした。
(※そもそも alert('test'); は動作するけど alert("test"); は動作しないというおかしな状況で、 なぜ動作しないのか不明です。分かる方がいたら教えていただけますと幸いです。)


今回はダミーボタンを作成することでEnterキーを無効にする方法を紹介いたします。
EnterキーでのPOST動作はform内に記述された最初のsubmitボタンに割り当てられます。
(※詳細な条件は後日手の開いている時にでも調べます)
なので、ボタンが押されても実行されないボタンを用意することで、Enterキーによるポストを無効にできます。

まず、aspxページにボタンコントロールを配置し(その他のボタンより上部に)、
ButtonDummyのような適当な名前を割り当てて、EnableプロパティをFalseにします。
また、このボタンが見えてしまうと見栄えが悪くなるのでスタイルシートで
display:none;
としておきます。
これだけだとなぜかIEでEnterが無効にならなかったのですが、
コードビハインド側で(C#)以下の記述を加えてダミーボタンをデフォルトボタンにすることで動作するようになりました。

this.Form.DefaultButton = this.ButtonDummy.UniqueID;


理解が深くないままの解決ですが、取り急ぎメモまで。

2011年3月31日木曜日

C#で全角⇔半角変換する方法

C#には全角⇔半角の変換を行うライブラリが用意されていません。
自作関数を作るかMicrosoft.VisualBasicにあるStrConvメソッドを使用します。
Strings.StrConv メソッド

StrConvメソッドを使用するには、事前準備としてVisual Studio上で
「Webサイト」→「参照の追加」→「.NET」タブから「Microsoft.VisualBasic」を選んで追加します。
1つ目の引数が対象となる文字列
2つ目の引数が全角(VbStrConv.Wide)、半角(VbStrConv.Narrow)の指定
3つ目の引数がロケール(日本語のロケールIDは0x0411)です。(省略可)

using Microsoft.VisualBasic;

string source = "アイウエオABCDE012345@#あいうえお";

//半角→全角
Console.WriteLine(Strings.StrConv(source, VbStrConv.Wide, 0x0411));
//結果 アイウエオABCDE012345@#あいうえお

//全角→半角
Console.WriteLine(Strings.StrConv(source, VbStrConv.Narrow, 0x0411));
//結果 アイウエオABCDE012345@#あいうえお

2010年9月13日月曜日

Visual Studio 入力候補を表示するショートカットキー

インテリセンスを表示するには
Ctrl + Space

パラメータヒントを表示するには
Ctrl + Shift + Space

2010年8月23日月曜日

SCOPE_IDENTITY()の型はDecimal

表題の通り(C#)

SqlConnection con = new SqlConnection();
SqlCommand cmd
 = new SqlCommand("INSERT INTO ...(略); SELECT SCOPE_IDENTITY();", con);
int i = (int)cmd.ExecuteScalar();
con.Close();

実際にはobject型で返ってくるのだが、内部的にdecimalなのでそのままintにキャストしようとすると
エラーとなってしまう。

こんな感じか。

int i = (int)(decimal)cmd.ExecuteScalar();