MySQL Workbenchのポートフォワード機能を使いラズパイのMySQLに接続する

外部のデータベースサーバーに触るときに気を付けないといけないのが、ログインユーザーとデータの漏洩なんだけど、データベースのポートを公開してしまうとちょっと危ない。社内ネットワークならばいいのだけど、流石に公開済みの WEB サーバーのデータベースのポートを開いてしまうのはかなり危険。

なので、MySQLの場合、phpmyadmin を使ってアクセスをするか、という話が多いのだが、実は、MySQL Workbench から SSH のポートフォワード機能が使えるのを最近知った。最近のレンタルサーバーは SSH が公開されている場合が多いので、この 22 ポートを通して内部の MySQL に接続する。

例えば、先日ラズパイに導入した redmine が使っている MySQL に PC から接続する場合は、こんな感じになる。

image

どうせ、ローカルなネットワークでしか動かさないので、PC から直接 MySQL に接続してもいいのだが(実際、ちょっと前まではそれしかないと思っていた)、クライアント PC で SSH を使って、サーバー(この場合はラズパイ)で sshd が動いていれば、この ssh 同士でポートフォワードしてくれる。動的に作成される(と思う)ローカルな SSH のポートを通して、ラズパイの22番を呼び出す。ラズパイの ssh がこれを受けて、MySQL の 3306番に接続してくれる。

MySQL Workbench で接続を作るときに、connection method で “Standard TCP/IP over SSH” を選択して各種設定を入力する。通常の SSH と同じように key file も使える。

image

“Test Connection” で接続を試す。

image

これが出なくて、ダンマリになる場合は、SSH サーバー名やポートが間違っている可能性が高い。うちの場合、avahi-daemon で *.local で繋がるようにしているのだけど、raspi3.local は接続できるのだけど、VMWare 上の ubuntu.local には接続できなかった。でも、IP アドレスを指定すると通るので、そのあたりもチェックが必要。

image

これでローカルな MySQL やポートを空けた DB サーバーと同じ用に MySQL Workbench から接続ができるようになる。ちなみに、DB サーバーが WEB サーバーとは別にある場合は、こんな設定になる。

image

OutServer とあるのが、いわゆる「踏み台」のサーバーで、SSH を公開しているところ。このサーバーから DB Server に接続するという形になる。この SSH によるポートフォワード機能を使うと、MySQL だけじゃなくてリモートデスクトップにも使えるので、難しい VPN の設定をしなくてもいいんじゃないかなと思ったり。

カテゴリー: 開発 | MySQL Workbenchのポートフォワード機能を使いラズパイのMySQLに接続する はコメントを受け付けていません

C# で Slack に投稿する

いままで忘備録的に、twitter にメモを投稿していたのだけど、半年ほど独り slack に投稿するということをやっている。ツイッターの自分の投稿をググるのも大変というのもあり、仕事に関係するものを投稿して、逆に仕事自体を推測されるのもアレなので(そういアピールだったらいいんだけど、実際、そういうこともやるし)、手元のものは手元でやるということで。

で、せっかくのクローズドな独り slack なのだから、redmine への投稿も slack 経由でやったり、逆に新規のチケットがでてきたら slack のほうに投げ返したりしたらどうか?と思ったりする。

slack の場合、http://slack.com/apps に各種アプリケーションがアップしてあって(twitter の連携アプリのような感じか?)、直接 slack api を触れなくて、適当な連携アプリを探してくるのがベターだったりするみたい。

Incoming WebHooks を使う

そんな中で、Incoming WebHookを使って、C#でSlackに投稿してみる – なか日記 というのがあったので、Incomming WebHook を使ってみる。

Incoming WebHooks | Slack App Directory
https://slack.com/apps/A0F7XDUAZ-incoming-webhooks?page=1

image

  1. Add Configuration する
  2. 投稿先のチャンネルを選んで、Add Incoming WebHooks Integration する
  3. Webhook URL が作成されるので、これを自前アプリで使う。

なか日記さんのほうでは、WebClient が使われているので、HttpClient を使うように書き替えてみる。JSON を送るときの content-type は StringContent を作成するときに指定すれば ok. DynamicJson を使っているけど、Newtonsoft.Json でシリアライズしてもよい。

class Program
 {
     static string WEBHOOK_URL = “”;
    static void Main(string[] args)
     {
         var hc = new HttpClient();
         var json = DynamicJson.Serialize(new
         {
             text = args.Length == 0 ? 
                     DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"):
                     args[0],
             icon_emoji = ":ghost:", //アイコンを動的に変更する
            username = "slackpost"  //名前を動的に変更する
        });

        var content = new StringContent(json, Encoding.UTF8, "application/json");
        var res = hc.PostAsync(WEBHOOK_URL, content).Result;
        Console.WriteLine("投稿しました");
    }
}

ちなみに、WebHook URL を取得するときにチャンネルを指定するけど、登校時に JSON で channel プロパティを指定すれば、指定したチャンネルが使われるので、実質何処にでも投稿ができる。

詳しい解説は Incoming Webhooks | Slack にある。curl を使って post すればよいので、わざわざツールを作るまでもないんだけど、まあ、あれこれ複雑なオプションを外してしまった単発ツールをスクリプトで作ってもよいだろう。

逆にメッセージを監視するときは、Outgoing WebHookを使ってSlackのメッセージを監視する – なか日記 に Outgoing WebHooks の使い方がある…のだけど、別途 Web API を立てる必要がある。外部サーバーで何かをやる場合はこれでいいのだが、結局のところ手元なPCへ通知を出す場合が多いので、真面目に slack api を使ってメッセージをポーリングするのが良いかなと思ったり。message event | Slack このあたり。

カテゴリー: 開発, Slack | C# で Slack に投稿する はコメントを受け付けていません

Xamarin.Formsでの共有部分をPCLから.NET Standardに変える

去年の6月の時点では、.NET Standard化されていなかったので、第6章の「共通ロジックを作成する」で PCL を使っていたのだけど、プロジェクト作成時に「共有プロジェクト」と「.NET Standard」になっているので、そのあたりのサンプルを書き替る。

Xamarinプログラミング入門 C#によるiOS、Androidアプリケーション開発の基本
https://www.amazon.co.jp/dp/4822253503/

コード共有方法で「.NET Standard」を選ぶ

「共有プロジェクト」のほうは、従来通りのコードを共有する方式なので #if を使って iOS/Android/UWP と書き分けられる。これでも、まあ十分なのだけど、ある程度コードが多くなってくるとロジック部分を別途テストしたいときがあるので、ライブラリ化しておくほうが良かったりする。

でもって、PCL(Portable Class Library)で作るときと .NET Standard で作るときとどう違うのかというと端的に言えば、PCLの場合は最小公約数になていて、.NET Standard の場合が最大公約数(というほど最大でもないが、いちおうな標準化)な意味合いで使える。
書籍の説明上、第6章でPCLで共通化させておいて、第8章で個別の動作を DependencyService を使うことになるのだが、.NET Standard の範囲内であればここの DependencyService が要らなくなる。

特に .NET Standard 2.0 の場合は、

  • ファイルまわり
  • HTTPまわり

が共通コードで書ける(System.IO.Fileとか、HttpListenerとか)ので、場合のよっては従来 NuGet でとってきた PCL なライブラリの組み込みが必要なくなる。あと、PCL の場合は、対応するプラットフォームによって条件がかなり厳しくなっていて Profile の番号体系も混乱しているので、さっくりと、.NET Standard 2.0 に対応させてしまったほうがよかったりする。

SampleTodo.XForms.Std

.NET Standard 版の SampleTodo.XForms.Std を新しく作成する。

moonmile/xamarin-samples
https://github.com/moonmile/xamarin-samples

プロジェクトを新しく作成しなくても、既存の PCL のプロジェクトを、

<Project Sdk=&quot;Microsoft.NET.Sdk&quot;>

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include=&quot;Xamarin.Forms&quot; Version=&quot;3.0.0.482510&quot; />
  </ItemGroup>
</Project>

な風に書き替えればよいのだが、Xamarin.Forms のバージョンが「3」になったり(つい5月にver3になっている)していたので、作り直してみた。

小さいサンプルプロジェクトなので、あとは PCL 版の SampleTodo.XForms からコードを移していくだけでよい。

ちなみに、本格的な ToDo アプリを作りたい場合は、テンプレートの Master-Detail から書き起こしていったほうがよい。ViewModel 間のメッセージのやりとりが MessagingCenter.Subscribe になっているのはこれに準じているため。

PCLのコードでは、DependencyServiceを使って、Android/iOSで別のコードを共通化することになっていた。

IToDoStorage storage = DependencyService.Get<IToDoStorage>();
/// <summary>
/// 内部ストレージに保存
/// </summary>

void Save()
{
    using (var st = storage.OpenWriter(&quot;save.xml&quot;))
    {
        viewModel.Items.Save(st);
    }
}
/// <summary>
/// 内部ストレージから読み込み
/// </summary>

void Load()
{
    var items = new ToDoFiltableCollection();
    using (var st = storage.OpenReader(&quot;save.xml&quot;))
    {
        if (st == null || items.Load(st) == false)
        {
            // 初期データを作成する
            items = ToDoFiltableCollection.MakeSampleData();
        }
    }
    viewModel.Items = items;
}

これを、.NET Standard 版で書き替えると、素直に System.IO.File
が使えるようになる。

/// <summary>
/// 内部ストレージに保存
/// </summary>

void Save()
{
    var docs = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
    var path = System.IO.Path.Combine(docs, &quot;save.xml&quot;);
    using (var st = System.IO.File.OpenWrite(path))
    {
        viewModel.Items.Save(st);
    }
}
/// <summary>
/// 内部ストレージから読み込み
/// </summary>

void Load()
{
    var items = new ToDoFiltableCollection();
    // .NET Standard 版では、
    // Android/iOSのコードも、共有プロジェクトに書ける
    var docs = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
    var path = System.IO.Path.Combine(docs, &quot;save.xml&quot;);
    if (System.IO.File.Exists(path))
    {
        var st = System.IO.File.OpenRead(path);
        if (items.Load(st) == false)
        {
            // 初期データを作成する
            items = ToDoFiltableCollection.MakeSampleData();
        }
    }
    else
    {
        // 初期データを作成する
        items = ToDoFiltableCollection.MakeSampleData();
    }
    viewModel.Items = items;
}

だが、UWPを付け加えるとストレージ絡みはやっぱり、DependencyService を使うという羽目に陥る。

IToDoStorage storage = DependencyService.Get<IToDoStorage>();

/// <summary>
/// 内部ストレージに保存
/// </summary>

void Save()
{
    var docs = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
    var path = System.IO.Path.Combine(docs, &quot;save.xml&quot;);
    try
    {
        using (var st = System.IO.File.OpenWrite(path))
        {
            viewModel.Items.Save(st);
        }
    }
    catch
    {
        // UWPを含める場合は従来通り DependencyService を使う
        using (var st = storage.OpenWriter(&quot;save.xml&quot;))
        {
            viewModel.Items.Save(st);
        }
    }
}
/// <summary>
/// 内部ストレージから読み込み
/// </summary>

void Load()
{
    var items = new ToDoFiltableCollection();
    // .NET Standard 版では、
    // Android/iOSのコードも、共有プロジェクトに書ける
    var docs = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
    var path = System.IO.Path.Combine(docs, &quot;save.xml&quot;);
    try
    {
        if (System.IO.File.Exists(path))
        {
            var st = System.IO.File.OpenRead(path);
            if (items.Load(st) == false)
            {
                // 初期データを作成する
                items = ToDoFiltableCollection.MakeSampleData();
            }
        }
        else
        {
            // 初期データを作成する
            items = ToDoFiltableCollection.MakeSampleData();
        }
    }
    catch
    {
        // UWPを含める場合は従来通り DependencyService を使う
        using (var st = storage.OpenReader(&quot;save.xml&quot;))
        {
            if (st == null || items.Load(st) == false)
            {
                // 初期データを作成する
                items = ToDoFiltableCollection.MakeSampleData();
            }
        }
    }
    viewModel.Items = items;
}

これは、UWPの場合だけファイルアクセス方法が違っていて、↓な風に Windows.Storage.ApplicationData にアクセスするという制限からきている。これって、System.Environment.GetFolderPath にエイリアスしてくれないですかね?

public Stream OpenWriter(string file)
{
    var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var t = folder.OpenStreamForWriteAsync(file, Windows.Storage.CreationCollisionOption.ReplaceExisting);
    t.Wait();
    var st = t.Result;
    return st;
}

既存のPCLプロジェクトを.NET Standard化するべきか?

仕事の場合、iOSとAndroidの共通化に特化しているだろうから(UWPプロジェクトは使っていないだろう)、.NET Standard にしても共通で使える部分が多いと思うのだけど、それぞれのプロジェクトに Xamarin.Forms.Dependency で書いていたものを、共通の .NET Standard プロジェクトに移し替えるか?というとかなりリスキーな気がする。

というのも、

  • そもそも、その Xamarin.Forms.Dependency は .NET Standard に入っているのか?
  • NuGet 等で旧来の PCL を使っていないか?

が問題になってくる。NuGet で提供される PCL プロジェクトが .NET Standard 化されていればよいのだが(去年の夏あたりから順次 .NET Standard 対応になっていたけど)、iOS/Android 個別の動作に特化してる部分は、なかなか難しいのではないかな?と。
なので、サンプル的には「共有プロジェクトは PCL から .NET Standardへ」という形で新規プロジェクトとして作ればよいのだが、既存の巨大となってしまった&現在動いているアプリのコードを PCL から .NET Standard に完全移行するかどうかは微妙な感じになる。

ただし、SampleTodo.XForms から SampleTodo.XForms.Std にプロジェクトのコードを引き継いだように、

  1. PCL プロジェクトの *.csproj を .NET Standard にしてビルドして動作確認。DependencyService 部分はそのまま。
  2. 冗長と思われる iOS/Android 個別の Xamarin.Forms.Dependency なところを、.NET Standard に引っ越しさせる。

という2段階で移行させるのがベターだろう。引っ越しさせなくてもよいのだが、iOSとAndroidでコードが二重化されているよりもひとつにまとまっていたほうが今後のメンテナンスが楽だろうし、コード共有で一生懸命 #if しているよりは、.NET Standard で適宜ライブラリ化しておいたほうがビルド時間も減るというものだ。

カテゴリー: 開発, Xamarin | Xamarin.Formsでの共有部分をPCLから.NET Standardに変える はコメントを受け付けていません

.NET Core上でLINQを使ってMySQLにアクセスする

Windows上では、Visual Studioを使ってデザイナを使いながらMySQLからテーブル構造を引っ張ってこれるのだが、じゃあ、.NET Core の場合はどうするのか?
多分、マイグレーション機能を使って、MySql.Data.EntityFrameworkCore.Design あたりを NuGet でとってきてあれこれやるんだろうだが、面倒なので。

  • あらかじめ、Windows の EF デザイナで取得したクラスを取ってくる。
  • 手作業でテーブルクラスを作る

のどちらかがよい。

Redmine のプロジェクト(projects)とチケット(issues)は、こんな風になっている。これ自体は、Windows 上の .NET でも使えるし、Linux 上の .NET Core でも使える。

public class projects
{
    public int id { get; set; }
    public string name { get; set; }
    public string description { get; set; }
    public string homepage { get; set; }
    public int is_public { get; set; }
    public int? parent_id { get; set; }
    public DateTime? created_on { get; set; }
    public DateTime? updated_on { get; set; }
    public string identifier { get; set; }
    public int status { get; set; }
    public int? lft { get; set; }
    public int? rgt { get; set; }
    public int inherit_members { get; set; }
    public int? default_version_id { get; set; }
}

public class issues
{
    public int id { get; set; }
    public int tracker_id { get; set; }
    public int project_id { get; set; }
    public string subject { get; set; }
    public string description { get; set; }
    public DateTime? due_date { get; set; }
    public int? category_id { get; set; }
    public int status_id { get; set; }
    public int? assigned_to_id { get; set; }
    public int priority_id { get; set; }
    public int? fixed_version_id { get; set; }
    public int author_id { get; set; }
    public int lock_version { get; set; }
    public DateTime? created_on { get; set; }
    public DateTime? updated_on { get; set; }
    public DateTime? start_date { get; set; }
    public int done_ratio { get; set; }
    public double? estimated_hours { get; set; }
    public int? parent_id { get; set; }
    public int? root_id { get; set; }
    public int? lft { get; set; }
    public int? rgt { get; set; }
    public int is_private { get; set; }
    public DateTime? closed_on { get; set; }
}

ちなみに、Visual Studio の EF デザイナだと、こんな projects クラスを吐き出す。アノテーションがついているので、asp.net mvc でスキャフォールディングするときに便利っぽい感じが…するが、まあなくても大丈夫

 [Table("redmine.projects")]
public partial class projects
{
    public int id { get; set; }

    [Required]
    [StringLength(255)]
    public string name { get; set; }

    [Column(TypeName = "text")]
    [StringLength(65535)]
    public string description { get; set; }

    [StringLength(255)]
    public string homepage { get; set; }

    public bool is_public { get; set; }

    public int? parent_id { get; set; }

    public DateTime? created_on { get; set; }

    public DateTime? updated_on { get; set; }

    [StringLength(255)]
    public string identifier { get; set; }

    public int status { get; set; }

    public int? lft { get; set; }

    public int? rgt { get; set; }

    public bool inherit_members { get; set; }

    public int? default_version_id { get; set; }
}

いわゆる、Entities なクラスはどうするかというと、NuGet で「MySql.Data.EntityFrameworkCore」を入れた後に、こんな風に手作業で作る。

public partial class RedmineEntities : DbContext
{
    public RedmineEntities()
    {
            
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.UseMySQL(@&quot;server=localhost;user id=redmine;password=redmine;database=redmine;port=3306;sslmode=None&quot;);
    }

    public DbSet<projects> projects { get; set; }
    public DbSet<issues> issues { get; set; }
}

Windows 上の DbContext と違うのは、データベースに接続するための設定が OnConfiguring メソッドをオーバライドして書くことになる。MySQL の場合は UseMySQL メソッドで指定する。この部分は拡張メソッドになっていて、インターフェースを決めるのではなくて、ダックタイピングなところが興味深いところ。以前は Factory だったような気もするのだが、こんな風にメソッド名でなんとなくルールを決めておくのが実に C# っぽい。

ここまで出来たら、後は .NET Core のコンソールアプリを作る。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello MysQL EF ");

        var ent = new RedmineEntities();
        var items = ent.projects.ToList();
        foreach (var it in items)
        {
            Console.WriteLine($"{it.id} {it.identifier} {it.name}");
        }
    }
}

シンプルに Redmine のプロジェクト一覧を表示しているだけなのだが、LINQ が使えるので SQL 文を書く必要がない。このぐらいの SELECT だと、普通に SQL 文を書いても手間は変わらないのだけど、Update とか Insert とかをやり始めると、ent.SaveChanges() で一発で書き込めるのは結構便利だったりする。特に、カラムの数が多い場合とか。

これを .net core の asp.net mvc で web api 化すると便利だったりするのだが、ターミナルを使ってコマンドラインで打てるのもよいだろう。GUI 絡みは .NET Core ではできないので、別途 Windows から MySQL のあるサーバーに繋げて、WPF で書くのがベターだろう。

カテゴリー: 開発, C# | .NET Core上でLINQを使ってMySQLにアクセスする はコメントを受け付けていません

LINQを使ってMySQLにアクセスする

常々、LINQ を使うために SQL Server を使う必要があって、じゃあ、Linux+MySQLの組み合わせに対して C#+LINQ はどうアクセスすればよいのか?と思っていたのだが、いつの間にか Entity Framework 相当のもの、というか MySql.Data.EntityFramework が出来ていたらしい。

MySQL :: Download Connector/Net
https://dev.mysql.com/downloads/connector/net/

いきなり、Connector/Net 8.0.11 というバージョンが上がっていて(あちこちの記事は 6.x だったりするんだが)、どうやら4月頃にアップデートしている。

何ができるのというと、Redmine や WordPress の内部にある MySQL に LINQ を使って直接アクセスできるということだ。こんな感じに、wordpress の wp_posts テーブルにACCESSできる。

image

NuGet から MySql.Data.EntityFramework をインストールする

実は Oracle のサイトから、Connector/Net をインストールしなくても、NuGet にある。

image

プロジェクトのほうは、MySql.Data.EntityFramework をインストールすれば ok. MySql.Data も一緒にインストールされる。

MySQL のプロバイダをインストールする

EF 自体は、NuGet から取れるのだが、SQL Server のようにデザイナを使いたい場合は、MySQL for Visual Studio をインストールする。

MySQL :: Download MySQL for Visual Studio https://dev.mysql.com/downloads/windows/visualstudio/

これを入れると、EF で「新しい接続」を選択したときに “MySQL Database” ってのが出るようになる

image

MySQL のテーブルを取り込む

準備ができたら、プロジェクトに「ADO NET Entity Data Model」を追加する。

image

モデルのコンテンツの選択では、

  • データベースから EF Designer
  • データベースから Code First

が2つある。Designer は、*.edmx ファイルを作ってグラフィカルなテーブルが表示されるもの。特にグラフィック部分が要らないのであれば、Code First で十分だったりする。

image

「新しい接続」ボタンをクリックして、MySQL Database を選ぶと、接続先のサーバー名とかを入れる画面が出る。ここで「テスト接続」をして接続できることを確認しておく。

image

さて、実は Connector/Net 8.0.11 にバグがあるらしく、ここの「詳細設定」を押して、Ssl Mode の値を「None」にしておかないといけない。

image

マニュアルには SslMode はデフォルトで None なのだけど、初期値が間違っているらしく、次の取得するテーブルを選ぶところで落ちてしまうのだ。

SQL Server に LINQ するときと同じようにクラス化するテーブルを選ぶ。

image

redmine の projects と issues が無事取得できる。

image

これで LINQ が使えるので、

private void clickGetIssues(object sender, RoutedEventArgs e)
 {
     var ent = new RedmineModel.RedmineEntities();
     var q = from t in ent.issues
             where t.project_id == 10
             select t;
     lv.ItemsSource = q.ToList();
 }

とか

private void clickGetWpPosts(object sender, RoutedEventArgs e)
{
     var ent = new WordPressModel.WordpressEntities();
     ent.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
     var q = from t in ent.posts
             orderby t.post_date descending
             select new { id = t.ID, title = t.post_title, date = t.post_date }
             ;
     q = q.Take(30);
     lv.ItemsSource = q.ToList();
}

な感じで、MySQL へ LINQ を使ってアクセスができる。

ホスティングしている場合は wordpress の MySQL に直接アクセスはできないだろうけど、ローカルで作っているとか高速にページをアップするとか修正するときに便利かもしれない。

カテゴリー: C#, MySQL | LINQを使ってMySQLにアクセスする はコメントを受け付けていません

ラズパイの Readmine 3.4 をインストールする手順

基本は、以下の redmine.jp の手順に従えばいいのだが、自分のメモ用に。

Redmine 3.4をUbuntu Server 16.04.2 LTSにインストールする手順 | Redmine.JP Blog
http://blog.redmine.jp/articles/3_4/install/ubuntu/
Raspberry Pi 3 に Redmine 3.3 をインストール | スモールサーバ開発部 https://blog.smallserver.jp/raspberry-pi-3-%E3%81%AB-redmine-3-3-%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB/

– Raspberry Pi3 Rasbian Jessie
– Redmine 3.4
– MySQL 5.5
– Apache 2.4.10

pi@raspi3:/usr/local $ uname -a
Linux raspi3 4.9.35-v7+ #1014 SMP Fri Jun 30 14:47:43 BST 2017 armv7l GNU/Linux
pi@raspi3:/usr/local $ mysql --version
mysql  Ver 14.14 Distrib 5.5.60, for debian-linux-gnu (armv8l) using readline 6.3
pi@raspi3:/usr/local $ sudo apachectl -v
Server version: Apache/2.4.10 (Raspbian)
Server built:   Apr  3 2018 16:25:06
pi@raspi3:/usr/local $

パッケージのインストール

sudo locale-gen ja_JP.UTF-8
sudo apt-get update
sudo apt-get install -y build-essential zlib1g-dev libssl-dev libreadline-dev libyaml-dev libcurl4-openssl-dev libffi-dev
sudo apt-get install mysql-server libmysqld-dev

MySQLの準備

外部から直接MySQLに触れるようにIP付きも指定しておく

mysql -u root -p

CREATE DATABASE redmine charset="utf8";
GRANT ALL PRIVILEGES ON redmine.* TO redmine@localhost IDENTIFIED BY 'redmine';
GRANT ALL PRIVILEGES ON redmine.* TO redmine@"172.16.0.%" IDENTIFIED BY  'redmine' WITH GRANT  OPTION;

my.cnf で bind-address をコメントアウト

vi /etc/mysql/my.cnf
↓コメントアウト
# bind-address = 127.0.0.1

opensslをビルド

Rubyがopensslを必要とするのでビルド。ラズパイの場合はパッケージがないので、ソースからビルドする。

wget https://www.openssl.org/source/openssl-1.0.2.tar.gz
tar xvf openssl-1.0.2.tar.gz
cd openssl-1.0.2
./config zlib shared no-ssl2
make && sudo make install

rubyをビルド

ruby 2.4 を使うためにソースからビルドする

curl -O https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.1.tar.gz
tar xvf ruby-2.4.1.tar.gz
cd ruby-2.4.1
./configure --disable-install-doc --with-opt-dir=/usr/local/ssl --enable-shared 
make && sudo make install
cd ..

opensslを入れないと、↓でエラーになる。

sudo gem install bundler --no-rdoc --no-ri

Redmineをインストール

Redmine を svn でとってきて配置。最終的には、http://raspi3.local/redmine でアクセスできるようにするが、/var/lib/redmine にインストールして alias する。

sudo mkdir /var/lib/redmine
sudo chown www-data /var/lib/redmine
sudo -u www-data svn co http://svn.redmine.org/redmine/branches/3.4-stable /var/lib/redmine

database.yml を作成する。データベースは mysql2 を使う。

sudo vi /var/lib/redmine/config/database.yml

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: "redmine"
  port: 3306

gem パッケージをインストールした後、初期データを入れる。初期データを戻したい場合は、drop database redmine ; してから、RAILS_ENV の 2行を実行する。

cd /var/lib/redmine
sudo -u www-data bundle install --without development test --path vendor/bundle

sudo -u www-data bundle exec rake generate_secret_token
sudo -u www-data RAILS_ENV=production bundle exec rake db:migrate
sudo -u www-data RAILS_ENV=production REDMINE_LANG=ja bundle exec rake redmine:load_default_data

Apacheの設定

Passenger をインストールする。Apache上のRailsの実行で使われる。

sudo gem install passenger -v 5.1.12 --no-rdoc --no-ri
sudo passenger-install-apache2-module --auto --languages ruby
passenger-install-apache2-module --snippet

redmine.conf を作成する。
最後の Alias /redmine で、ブラウザからアクセスする場所を決める。ルートにしたい場合は、/etc/apache2/sites-enabled/000-default.conf の DocumentRoot を書き替える。

sudo vi /etc/apache2/conf-available/redmine.conf

# Redmineの画像ファイル・CSSファイル等へのアクセスを許可する設定。
# Apache 2.4のデフォルトではサーバ上の全ファイルへのアクセスが禁止されている。
<Directory &quot;/var/lib/redmine/public&quot;>
  Require all granted
</Directory>

# Passengerの基本設定。
# passenger-install-apache2-module --snippet で表示された設定を記述。
# 環境によって設定値が異なるため以下の5行はそのまま転記せず、必ず
# passenger-install-apache2-module --snippet で表示されたものを使用すること。
#

LoadModule passenger_module /usr/local/lib/ruby/gems/2.4.0/gems/passenger-5.1.12/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
  PassengerRoot /usr/local/lib/ruby/gems/2.4.0/gems/passenger-5.1.12
  PassengerDefaultRuby /usr/local/bin/ruby
</IfModule>

# 必要に応じてPassengerのチューニングのための設定を追加(任意)。
# 詳しくはPhusion Passenger users guide(https://www.phusionpassenger.com/library/config/apache/reference/)参照。
PassengerMaxPoolSize 20
PassengerMaxInstancesPerApp 4
PassengerPoolIdleTime 864000
PassengerStatThrottleRate 10

Alias /redmine /var/lib/redmine/public
<Location /redmine>
  PassengerBaseURI /redmine
  PassengerAppRoot /var/lib/redmine
</Location>

設定の反映と、apacheの再起動

sudo a2enconf redmine
apache2ctl configtest
sudo service apache2 reload

これで、http://raspi3.local/redmine でアクセス可能になる。
最初は、admin/admin で入れる。

ちなみに、ラズパイ3でも結構遅いので実運用する場合は別途PCに立てるのがベターだと思う。

カテゴリー: 開発, RaspberryPi | ラズパイの Readmine 3.4 をインストールする手順 はコメントを受け付けていません

Redmine Client の作り始め

唐突に Readmine の Windows クライアントがが欲しくなったので作り始め。とはいえ、土曜日から作っていて後から知ったのだけど、Redmine の .NET ライブラリって zapadi/redmine-net-api にあったのであった。NuGet からも落とせる https://www.nuget.org/packages/redmine-api/ ので、それを使えばいいのだが。まあ、ほどよく動くところまで出来てきたので、自前のものを使うことにする。

WPF で作る

最近、VB6 から WPF 版の移植をやっていて、無駄に EF+MVVM+DataGrid の組み合わせのノウハウがついてしまったので早速利用する。

redmine の apikey を取ってきて、RmClient.config に置くと、こんな風にプロジェクト一覧、チケット一覧、チケットの編集ができるようになる。

image

ひとまずは、チケットの新規作成と更新ができればよいので、そこだっけ実装して試している。プロジェクトをあちこち移動すること少ない(サブプロジェクトを組んだ時は別だろうけど)ので、プロジェクト単位に config を用意しておいて専用のアプリ(不具合管理と課題管理とか)を立ち上げる、という風にすると手順が少なくて済むかなと思っている。アプリごとお客に配布して、クライアントから直接いれて貰うとかできればいい。Redmine のブラウザよりも機能を絞ってしまう感じ。

コマンドラインで動かす

手元の wordpress 投稿ツールのように、テキストエディタ+コマンドラインで動くようにしておく。

これは、チケットID を指定して、内容を取ってくる。

image

そして、内容をエディタで編集する。

image

そして、コマンドラインから投稿する。

image

ブラウザからツールで見れば、内容やステータスが更新されていることが分かる。

image

チケットの新規作成とかもできるので、数十件のチケットを同時にアップするとかも可能になる。

RmClient.Lib を .NET Standard 化する

コマンドラインツールが、.NET Core でも動くように、ライブラリ自体は .NET Standard で作っておく。

image

RmClient.Console.Core のほうが .NET Core 版で、Linux 上からも使える

dotnet run Get <ticket_id>

のようにパラメータを指定する訳なんだが、このあたりの解説はまた別途。

コード

ひとまず、現状を github にアップ。

moonmile/RmClient: Redmine Client
https://github.com/moonmile/RmClient

カテゴリー: C#, RmClient | Redmine Client の作り始め はコメントを受け付けていません

Visual Studioで指摘される「名前指定の規則違反」を直す

Visual  Studio を使ってボタンのイベントを作ると button1_Click な感じにキャメルケース(camelCase)になるのだが、「名前指定の規則違反」を指摘されて、こんな感じに大文字で始まらなければいけない、と言われる。

image

毎度、なんじゃこりゃ?と思っている訳で、結構前から C# – イベントハンドラーで構文エラー「~button1_Click~」(100021)|teratail 問題があったりするわけだが。これ、命名規約のルールとして、

  • プライベートなメソッドは小文字で始める
  • パブリックなメソッドは大文字で始める

というパターンが妥当で(実際、以前からそうなっていたし)、イベントの自動生成もプライベートなフィールド名前(button1)から自動でイベントメソッド名を付けている訳で、このキャメルケースでいいような気もする。

というか、Microsoft の初期設定はまあいいから、プライベートなメソッドはキャメルケースにして小文字で始めたいんですけど。

どこで命名規約を決めているのか?

「ツール」→「オプション」で、「テキストエディタ」→「C#」→「コードスタイル」→「名前指定」で命名規約は決めてある。

image

デフォルトでは、3つだけ指定してあって、

  • インターフェースを「I」で始めること
  • 型(クラス)をパスカルケース(大文字始まり)にすること
  • フィールド以外のメンバはパスカールケース(大文字始まり)にすること

というのが決めれれていて、最後の「フィールド以外」ってのに引っ掛かっている。なので、手っ取り早いのは、この規約を消してしまうことだ。右にある「×」ボタンを押して規約から外してしまうと、先の自動生成のイベント名に適用されなくなって警告がでなくなる。

「フィールド以外のメンバー」の適用範囲を public のみにする

もう少し意図通りに動かしたいときは、「仕様の管理」をクリックして「フィールド以外のメンバー」の適用範囲を変えてやる。

image

デフォルトは、こんな感じにアクセシビリティが、public から private まで全てになっている。ここで private とかを外して public だけにする。

image

意図的に、public だけパスカルケースにする

image

C# のエディタを閉じて、開き直すと無事警告がでなくなる。

image

命名規約に return がないのが問題?

これで無事、イベントで生成されるメソッド名に警告がでなくなるのだが、じゃあ、最初に話を戻して

  • プライベートなメソッドは小文字で始める
  • パブリックなメソッドは大文字で始める

という2つの規約を作りたいときはどうするのか?という問題が残る。Visual Studio の命名規則の適用って、上から順番に or で対処していくパターンなので、Outlook のメール振り分けのように途中で return ってのはない。なので、重要度に「なし」をするとうまく合格させることもできるのだが、ここで例外を作るとロジック的に複雑になってしまう。なので、適用範囲を絞りながら少しずつ追加するのがベターじゃないかなと。

でもって、プライベートなメソッドは必ずキャメルケース(小文字始まり)で、ということにするには、こんな風に規則を追加する。

image

最後の2つで、メソッドの命名規則(private と public)を分けることになる。

image

カテゴリー: C# | Visual Studioで指摘される「名前指定の規則違反」を直す はコメントを受け付けていません

EFのクラスをMVVMのINotifyPropertyChangedに対応させる裏技

WPFでEF(Entitiy Framework)を使っていると、リスト表示には楽なのだが、データを反映するためにはINotifyPropertyChangedを継承させなくてはいけなくて、そこが面倒くさい。いちいち、ViewModel クラスで EF のクラスをくるめば良いのだが、それを自動化したい。
特にマスター管理用の画面なんかを作るときは、ASP.NET MVCのように足場を自動化させておきたいというのもある。

EFで出力されるクラスは、下記のようなクラスなので、

public partial class 社員名
{
    public int 社員ID { get; set; }
    public Nullable<int> 部署ID { get; set; }
    public Nullable<int> 営業担当FLG { get; set; }
    public string 社員名1 { get; set; }
}

MVVM対応のINotifyPropertyChangedインターフェースを継承したこんなクラスにしたいわけだ。

public partial class 社員名 : ObservableObject 
{
    private int _社員ID ;
    public int 社員ID {
    	get { return _社員ID; }
    	set { SetProperty( ref _社員ID, value, nameof(社員ID)); }
    }
    private Nullable<int> _部署ID ;
    public Nullable<int> 部署ID {
    	get { return _部署ID; }
    	set { SetProperty( ref _部署ID, value, nameof(部署ID)); }
    }
    private Nullable<int> _営業担当FLG ;
    public Nullable<int> 営業担当FLG {
    	get { return _営業担当FLG; }
    	set { SetProperty( ref _営業担当FLG, value, nameof(営業担当FLG)); }
    }
    private string _社員名1 ;
    public string 社員名1 {
    	get { return _社員名1; }
    	set { SetProperty( ref _社員名1, value, nameof(社員名1)); }
    }
}

こうしておくと、リストビューで一覧表示をさせておいて、選択時にテキストボックスへ表示、そしてテキストボックスでの変更がリストビューに反映される、というところが自動化される。

Model1.ttを直接書き替える

元のクラスにスクリプトを通して、MVVM対応のクラスを作ろうかとも思ったのだが、もっと簡単に EF で出力される Model1.tt を直接書き替えてしまう。T4 で書かれている Model1.tt はファイルに保存した途端にテンプレートに従って各クラスが出力される。

public class CodeStringGenerator
{
	...
    public string Property(EdmProperty edmProperty)
    {
        return string.Format(
            CultureInfo.InvariantCulture,
			// ★ここを書き替え
@"private {1} _{2} ;
	{0} {1} {2} {{
		get {{ return _{2}; }}
		set {{ SetProperty( ref _{2}, value, nameof({2})); }}
	}}",

			// "{0} {1} {2} {{ {3}get; {4}set; }}",
            Accessibility.ForProperty(edmProperty),
            _typeMapper.GetTypeName(edmProperty.TypeUsage),
            _code.Escape(edmProperty),
            _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
            _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
    }
	...
    public string EntityClassOpening(EntityType entity)
    {
        return string.Format(
            CultureInfo.InvariantCulture,
			// ★ここを書き替え
            "{0} {1}partial class {2}{3} : ObservableObject ",
            Accessibility.ForType(entity),
            _code.SpaceAfter(_code.AbstractOption(entity)),
            _code.Escape(entity),
            _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)));
    }
	...
}

大ざっぱだが、CodeStringGeneratorクラスのPropertyメソッドとEntityClassOpeningメソッドの内容を書き替えてしまう。

EntityClassOpening は、エンティティのクラスを出力するところなので、INotifyPropertyChangedインターフェースを実装した「ObservableObject」クラスを継承するように書き替える。ObservableObjectクラスは別途自前で用意しておく。
Property メソッドは、プロパティを出力するところなので、SetProperty メソッドを呼び出して変更通知が飛ぶようにする。

これを保存すると、各エンティティのクラスが、

public partial class 社員名 : ObservableObject 
{
    private int _社員ID ;
    public int 社員ID {
    	get { return _社員ID; }
    	set { SetProperty( ref _社員ID, value, nameof(社員ID)); }
    }
	...
}

のように書き変わる。

のような画面が、バインドだけで作れるようになる。

カテゴリー: 開発, C# | EFのクラスをMVVMのINotifyPropertyChangedに対応させる裏技 はコメントを受け付けていません

DataGrid の SelectedItem にバインドしていると、ItemsSource 再設定時に落ちる

ちょっとばかし、ハマったのでメモ書きをしておく。

WPFのDataGridにデータバインドする

WPFでリスト表示をするときに、ListViewかDataGridを使うと思うのだが、ここで MVVM を使って ItemsSource にバインドする。

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height=&quot;50&quot;/>
        <RowDefinition Height=&quot;*&quot;/>
    </Grid.RowDefinitions>
    <DataGrid Grid.Row=&quot;1&quot; x:Name=&quot;lv&quot;
                ItemsSource=&quot;{Binding Items}&quot;
                SelectedItem=&quot;{Binding Item}&quot;
                SelectedIndex=&quot;{Binding ItemIndex}&quot;      >
    </DataGrid>

ViewModel を作って

public class ViewModel : ObservableObject
{
private List<商品> _items;
public List<商品> Items
{
    get { return _items; }
    set { SetProperty(ref _items, value, nameof(Items)); }
}

private 商品 _item;
public 商品 Item
{
    get { return _item; }
    set { SetProperty(ref _item, value, nameof(Item)); }
}
private int _itemIndex;
public int ItemIndex 
{
        get { return _itemIndex; }
        set { SetProperty(ref _itemIndex, value, nameof(ItemIndex)); }
    }
}

ロード時に表示させる。

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    _vm = new ViewModel();
    var ent = new Database1Entities();
    _vm.Items = ent.商品.ToList();
    this.DataContext = _vm;
}
ViewModel _vm;

実行すると、こんな感じ。

行を選択するとバインドしている SelectedItem にオブジェクト(ここでは商品オブジェクト)が入るので、詳細データとかを別画面で表示するには便利だったりする。
SelectedIndex でもいいような気もするのだが、実は、DataGrid はヘッダをクリックするとソートする機能が初めから入っていて、結構便利。でもって、ソートしたときはインデックスが変わってしまうので、SelectedIndex では困るので、SelectedItem から選択したオブジェクトを拾うと良いという訳。渡している List<商品> と DataGird 自身が持っている Items の中身が異なる(ソートされている)のでずれがでてくる。

リストをリフレッシュすると落ちる

さて、普段は DataGrid の中身は変わらないので更新することはないのだが、なんらかの理由でリストを再描画させたいとしよう。このときに、ItemsSource にバインドしている ViewModel 側の Items を更新すればよいと思って、次な感じにすると、

private void clickInit(object sender, RoutedEventArgs e)
{
    var ent = new Database1Entities();
    _vm.Items = ent.商品.ToList();
}

~~~
System.NullReferenceException が発生しました
HResult=0x80004003
Message=オブジェクト参照がオブジェクト インスタンスに設定されていません。
~~~

なる例外が発生して落ちる。それも、_vm.Items に代入しようとしているところで落ちるので始末が負えない。
実は、初期表示をしていて、カーソルを DataGird にあてない状態(選択行が無い状態)の場合は落ちなくて、一度選択した後には例外が発生して落ちるのである。
かなり不思議な現象である。ネットでもあちこち困っている人がいるもの、ぴっちりとした解決策は無いように見える、が、

実は、このように Item に null を代入すると落ちなくなる。

private void clickReload(object sender, RoutedEventArgs e)
{
    var ent = new Database1Entities();
    _vm.Item = null;	// ★
    _vm.Items = ent.商品.ToList();
}

そう、ViewModel を見るとわかるのだが、DataGrid の SelectedItem へのバインドが悪さをしている。Items に新しいリストを設定して、ItemsSource プロパティを更新してしまうと、SelectedItem が行先を見失う(?)ことになって NULL例外が発生するらしい。このため、先に SelectedItem に null を設定してやって、カーソルを外した後で ItemsSource に新しいリストを設定することになる。

おまけ

じゃあ、ObservableCollection を使って、一度クリアしたあとに1つずつ追加しけばいいじゃないか、と思うかもしれないが、実はそれでも落ちる。

private void clickReload(object sender, RoutedEventArgs e)
{
    var ent = new Database1Entities();
    _vm.Item = null;	// ★
    _vm.Items.Clear();
    foreach (var it in ent.商品) _vm.Items.Add(it);
}

普通の Items の更新と同じように、★部分の null の設定がないと、_vm.Items.Clear() 行で例外が発生して落ちてしまう。これも、SelectedItem が示し先を見失ってしまうためらしい。

カテゴリー: 開発, C#, XAML | DataGrid の SelectedItem にバインドしていると、ItemsSource 再設定時に落ちる はコメントを受け付けていません