くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

.netMVC

.net MVCで「roslyn\csc.exe」のエラー

最近、どこからか.netMVCをCloneしてビルドすると↓のエラーが出た。

'bin\roslyn\csc.exe' パスの一部が見つかりませんでした

で、これが出たら↓

パッケージマネージャコンソール(ツール⇒Nugetあたり)

update-package Microsoft.CodeDom.Providers.DotNetCompilerPlatform -r

csc自体はc#のコンパイラだったと思うんだけど、これが見つからないってこと?

ちょっとなぞ。

むかーしむかし、IEのDLLホスティングっていうのがありまして、

そん時に確か、何かのDLL使おうとした時にcsc.exeがねーってエラーが出た記憶があるなー。

だから何だって話ですけど。

参考サイト↓

https://kotaeta.com/62165347

C# .netMVC で MySQL使う

タイトル通り。ついでにEFから呼べるようにもする。まずはNugetで↓をインストール。

sample

そしたら、まず、SQL直接書くやつ。

using MySql.Data.MySqlClient;
public void test()
{
    string constr = @"Database=hogedb;Data Source=fugaserver;User Id=root;Password=passpass";
    string error = "";
    string title = "";
    using (MySqlConnection cn = new MySqlConnection(constr))
    {
        try
        {
            cn.Open();// 接続
            //クエリ発行
            MySqlDataAdapter da = new MySqlDataAdapter("select title from hogetables limit 1;", cn);
            DataTable dt = new DataTable();
            da.Fill(dt);
            foreach (DataRow row in dt.Rows)
            {
                title = row[0].ToString();
            }
            cn.Close();//クローズ
        }
        catch (Exception e)
        {
            error = e.Message;
        }
    }
}

次はEF。多分↑のインストールとは別に、EFを入れないとダメ。

最初はWeb.Config。まーこれでやる必要はないけど何となく。

<connectionStrings>
  <add name="RealConnection" connectionString="Database=hogedb;Data Source=fugaserver;User Id=root;Password=passpass" providerName="MySql.Data.MySqlClient" />
</connectionStrings>

テーブル用のPOCO。

[Table("hogetables")]
public class hogetable
{
    [Key]
    public int id { get; set; }
    public string title { get; set; }
}

public class MOGEModels : DbContext
{
    public MOGEModels() : base("RealConnection")
    {
    }
    public DbSet<hogetable> hogetables{ get; set; }
    
    public void test()
    {
       foreach (var tmp in this.hogetables.Where(n => n.id == 10))
       {
          tmp.title;
       }
    }
}

とゆーことで、普通に使えますねって当たり前か。。。

正直、RedmineとかのDBをちょっと他から触りたくなっただけです。Rubyでプラグインとか作るのめんどー。

.net MVC WebApi のContent-Type

.net MVCでWebApi作ってたんですが、なんだか戻りがXMLになってしまう。Jsonで返したい。

とりあえず、Chromeで取るとデフォでXMLになるっぽい?デフォでJsonにしたい。

stackoverflowに同じ質問あった。さすが。。。

回答は色々あるけど、何となく今は↓が一番良さげ?デフォを変えるなら。

using System.Net.Http.Formatting;//RequestHeaderMapping用

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        //デフォルトのフォーマットを変える↓
        GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(new RequestHeaderMapping("Accept",
                          "text/html",
                          StringComparison.InvariantCultureIgnoreCase,
                          true,
                          "application/json"));
    }
}

WebApiConfigのRegisterの中で↑を追記。

あと、ちょっと違うけど、URLの指定で最後に「~~.json」とか「~~.xml」って指定させて、フォーマットを変える方法。RedmineのAPIはこれ形式すな。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API ルート
        config.MapHttpAttributeRoutes();
        config.Formatters.JsonFormatter.AddUriPathExtensionMapping("json", "application/json");
        config.Formatters.XmlFormatter.AddUriPathExtensionMapping("xml", "application/xml");
        
        //ルートの設定で{ext}が必要
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}.{ext}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

ただ、最初のデフォルト強制する奴つけてると効かなくナリ。

あと、↓の感じでURLは最後の「/」付けないと効かなかったりする。ちょっとハマッた。Routeの設定ちゃんとやればいけるっぽいの。

/api/controller/action.json/

ついでに、Routeの設定ってみんなどうしてるんでしょ。デフォのままだと、Controllerのみだから、GetとかPostしか作れないよね?

Controller増やせばいいんだけど、ファイルいっぱいできるからあまし好きじゃない。なので、action入れてやってたりするんですが、これってありなのかしら。イマイチ分かってない。

.net mvc WebApiにレスポンスヘッダつける

以前、レスポンスヘッダ付けるやつを書いた。あっちはControllerを継承した普通のページ用のコントローラでの話。WebApiでやる場合はちょっと変えないと無理。↓の感じ。

public class WebApiController : ApiController
{
  public HttpResponseMessage Get()
  {
      var hogeModel = somethingModel();
      var response = Request.CreateResponse(HttpStatusCode.OK, hogeModel);
      response.Headers.Add("Access-Control-Allow-Origin", "*");//レスポンスヘッダ付ける
      return response;
  }
}

Jsonで戻したい場合は、オブジェクトで戻すと思うんだけど、↑の感じで書いてあげるとオブジェクトをJsonにして戻してくれる。

.net mvc CSSとかのキャッシュバスティングをやる ver2

以前、書いたやつのちょっと補足。

<link href="/Content/site.css?20160508113250" rel="stylesheet"/>

↑みたいな、ブラウザキャッシュをやめさせるクエリ文字列をファイル更新された自動でつけるやつ。

直すのは「RegisterBundles()」してるとこ。デフォだとBundleConfigっていうクラス名かな。

//CSSはこっち
Bundle addBundle = new StyleBundle("~/Content/css").Include("~/Content/bootstrap.css", "~/Content/site.css");
addBundle.Transforms.Add(new FileHashVersionBundleTransform());
bundles.Add(addBundle);
//JavaScriptはこっち
addBundle = new ScriptBundle("~/lib/hoge").Include("~/Scripts/lib/hoge.js");
addBundle.Transforms.Add(new FileHashVersionBundleTransform());
bundles.Add(addBundle);

「FileHashVersionBundleTransform」の中身は元記事参照。そしたら、cshtml側で↓の感じで使いたいやつを呼ぶ。

@section scripts {
@Scripts.Render("~/lib/hoge")
}

結果は↓の感じ。

<script src="/Scripts/lib/hoge.js?v=20170508210031529"></script>

.netMVCのGDI+エラーをどうにか回避したい その2

以前、GDI+のエラーを回避したい的なのを書いた。その続き。

前回書いたものは今のとこ大丈夫そうなんだけど、使用してるライブラリで内部でDrawing使ってるやつもダメになってるね。当たり前だけども。詰みかな?

ちなみに、エクセル操作するEPPlusってやつを使って、エクセルに画像を貼る際に、Drawing使ってるらしくやられました。

結構、根が深いんじゃなかろーか。さてどうしましょう。

マイクロソフトさんに頑張っていただくしかなかね。。。

C# .netMVCでGDI+のエラー

ちんまいWebアプリの中で、Drawingを使って画像を作ってる場所があるんだけど、最近そこの部分ではまった。

画像を保存するところで、唐突に「GDI+ で汎用エラーが発生しました。」で落ちることがある。発生するとIISResetするまで発生し続ける。ソース的には↓の感じ。

Bitmap img = new Bitmap(100, 64);
Color c = Color.FromArgb(0xFF, Color.FromArgb(Convert.ToInt32("0xD9333F", 16)));
Graphics g = Graphics.FromImage(img);
Pen pen = new Pen(c);
g.DrawLine(pen, 0, 0, 100, 0);
img.Save(".\HOGE\FUGA.png", ImageFormat.Png);//ここでエラー。

検索してみると、「ファイルパスがおかしいと落ちる」・「パスが長すぎると落ちる」・「ファイルから読み込んでそのまま保存とかしようとすると落ちる」・「フォルダの権限が足りないと落ちる」などなどいくつか出てくる。

ただ、↑くらいの話は結構誰でも知ってるポイントなので、何度も確認している。でも落ちる。

基本に立ち返り、MSDNを見てみると↓の事がかいてあった。

img01

・・・・・・ん!?

ASP.NET サービス内での使用はサポートされません。

使うなって事なのねん。何かが競合することでもあるんだろうかと愚考。

つか、.net4.0までは上の注意書きがないとゆー罠。4.5からサポートしなくなった模様。

なんか別のライブラリ的なサムシングエルスを使うしかあるまい。。。

2017/4/12追記 続き

MSDN

.net MVC でNpgsql(PostgreSQL)を使う

タイトル通りなんだけど、普通にNugetからインストールだけだと現状動かない。

Nugetから「EntityFramework」と「Npgsql.EntityFramework」を入れる。入れると「Npgsql」も勝手に入る。あとは普通に書くんだけど、動かしてみると↓のエラーが出る。

要求された .Net Framework データ プロバイダが見つかりません。これは、インストールされていない可能性があります。

インストールしたんですけど。。。

とりあえず、WebConfigに↓を追記しないとダメ。

  <system.data>
    <DbProviderFactories>
      <add name="Npgsql Data Provider" invariant="Npgsql" description="Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, Npgsql" />
    </DbProviderFactories>
  </system.data>

ホントは、Machine.configとかGACをいじっておく必要があるらしい。めんどー。

.net MVCでEntity Framework使用でDBをPostgreSQLにしてみる

いつもはSQLServer使ってたんだけど、たまにはPostgreSQLも使ってみようかと思いまして、やってみた。

MVCはVisualStudioでテキトーにテンプレを使う。メンドーなので認証なし。そしたらNugetで下の3つのパッケージをインストール。

sample01

EFはEFのために必要。EFでPostgre使うためにNpgsql.EntityFrameworkが必要。これのためにNpgsqlが必要。みたいな感じらしい。EFは6。

そしたら次はWeb.configの設定

<!--ここはインストールすると勝手に入る-->
<entityFramework>
  <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
  <providers>
    <provider invariantName="Npgsql" type="Npgsql.NpgsqlServices, Npgsql.EntityFramework" />
    <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
  </providers>
</entityFramework>
<!--ここを手で追記-->
<system.data>
  <DbProviderFactories>
    <add name="Npgsql Data Provider" invariant="Npgsql" description="Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, Npgsql" />
  </DbProviderFactories>
</system.data>
<!--接続文字列の指定-->
<connectionStrings>
  <add name="DefaultConnectionPGSQL" connectionString="Server=localhost;Port=5432;User Id=postgres;Password=*****;Database=hogedb" providerName="Npgsql" />
</connectionStrings>

手で追記する部分があるっぽい。ここ入れないと動かなかった。あとは接続文字列の指定。

ためしに作ったテーブルは下の二つ。日本語列名がいけるか試したいので日本語で。

--主キーがserial(SQLServerで言うIdentity)じゃないやつ
create table dbo.ホゲテーブル
(
ホゲ varchar(100) 
,数値 int
,CONSTRAINT wkpk PRIMARY KEY (ホゲ)
)
--主キーにserial(SQLServerで言うIdentity)
create table dbo.フガテーブル
(
フガid SERIAL
,テスト varchar(100) 
,数値 int
)

で、ワナが一つ。SQLServerはデフォでスキーマ名が「dbo」になる。なので、EFも何もしないとSQLに「dbo.table」みたいに書かれる。なので、Postgreのスキーマはdboで作っておいた方が吉。スキーマを変える場合は後述。

あとは、ためし実装。Model側。

public class HomeModels : DbContext
{
    public HomeModels() : base("DefaultConnectionPGSQL")
    {

    }
    public DbSet<HogeTable> HogeTables { get; set; }
    public DbSet<FugaTable> FugaTables { get; set; }

    public HomeViewModel getTable()
    {
        HomeViewModel resultModel = new HomeViewModel();
        resultModel.hogeT = this.HogeTables.Where(n => n.hogeval > 0).ToList();
        resultModel.fugaT = this.FugaTables.Where(n => n.数値 > 0).ToList();

        return resultModel;
    }
    public void modelAdd()
    {
        HogeTable addM = new HogeTable();
        addM.hogekey = DateTime.Now.ToString("yyyyMMddhhmmssfff");
        addM.hogeval = 123;
        this.HogeTables.Add(addM);


        FugaTable addM2 = new FugaTable();
        addM2.テスト = DateTime.Now.ToString("yyyyMMddhhmmssfff");
        addM2.数値 = 123;
        this.FugaTables.Add(addM2);
        this.SaveChanges();
    }

}
public class HomeViewModel
{
    public List<HogeTable> hogeT { get; set; }
    public List<FugaTable> fugaT { get; set; }
}

[Table("ホゲテーブル")] //テーブル名指定
public class HogeTable
{
    [Key] //主キーの設定
    [Column("ホゲ")] //列名指定
    public string hogekey { get; set; }
    [Column("数値")]//列名指定
    public int hogeval { get; set; }
}

[Table("フガテーブル", Schema = "dbo")] //スキーマ変えたいとき
public class FugaTable
{
    [Key] //主キーの設定
    public int フガid { get; set; }
    public string テスト { get; set; }
    public int 数値 { get; set; }
}

コントローラ側。HomeのIndexだけで、あと、データ追加用のAction。

HomeModels db = new HomeModels();
public ActionResult Index()
{
    HomeViewModel result = db.getTable();

    return View(result);
}

public ActionResult AddAction()
{
    db.modelAdd();
    return RedirectToAction("Index");
}

View側はこれ。

@model mvcdemo.Models.HomeViewModel
@{
    ViewBag.Title = "Home Page";
}

<div>
    @Html.ActionLink("Add","AddAction")
</div>

<h1>ホゲテーブル</h1>
<div>
    @foreach(var tmp in Model.hogeT)
    {
        <table>
            <tr>
                <td>
                    @tmp.hogekey
                </td>
                <td>
                    @tmp.hogeval
                </td>
            </tr>
            
        </table>
    }
</div>

<h1>フガテーブル</h1>
<div>
    <table>
        @foreach (var tmp in Model.fugaT)
        {
            <tr>
                <td>
                    @tmp.フガid
                </td>
                <td>
                    @tmp.テスト
                </td>
            </tr>
        }    
    </table>
</div>

実行結果で、何回か追加した後。

sample02

ちゃんとIDも振られてるし問題なし。。。フツーに使える。よかたよかた。

スキーマ変えたいときはテーブルのModel作るときに属性で「[Table("フガテーブル", Schema = "dbo")] 」って感じにする。全体通してやる場合はDbContextのクラスの中に下を入れておく。

public class HomeModels : DbContext
{
    public HomeModels() : base("DefaultConnectionSQLServer")//DefaultConnectionPGSQL
    {

    }
    //これを入れる
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("dbo");
    }
    public DbSet<HogeTable> HogeTables { get; set; }
    public DbSet<FugaTable> FugaTables { get; set; }
    ・・・

Modelのインスタンス作った時に呼ばれるやつではないかと。。。

ちなみにこの状態のまま、SQLServer側に同じテーブル作って、接続文字列の指定でSQLServer用のものを書けばそのままDB切り替えられました。ためしに色々やるのに便利。。。

Postgerはフツーに使えていい感じなんだけど、なんつーか、Postgreの管理ツールがびみょー。なんかいいの無いのかしら。

pgAdminなるものを使用してみたけど。微妙じゃない?なんつーか自分がMicrosoftに染められてるんだろーけど、インテリセンス弱すぎじゃない?あと、エラーとか警告とか結果の出力とかもろもろもろもろもろ。SQLServerのManegementStudioはやっぱりさすがにさすがにって感じなんだろーか。。。

.NetMVC HTML5 ファイルドロップで複数ファイルアップロード

よくあるやつ。昔はFlash使ってたけど、最近はHTML5でやった方が軽くてよさげ。 受け取り側は.net MVCで。

■HTML

<div>
    <div id="drop_zone" style="width:500px;border : 2px dashed #808080;margin:10px;padding:10px;">
        <span>ファイルをココにドロップ</span>
        <progress id="testprogress" value="0" max="100">0%</progress>
    </div>
</div>

■JavaScript


<script type="text/javascript">
    //必要なのが使えるかチェック
    if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData) {
       //OK
    } else {
        alert('ブラウザ変えてやり直し.');
    }
    function handleFileSelect(evt) {
        //ブラウザのデフォルトの動作をキャンセル
        evt.stopPropagation();
        evt.preventDefault();
        
        var files = evt.dataTransfer.files;//ドロップイベントでドロップされたファイル
        var fd = new FormData();//送信用Formデータ
        for (var i = 0, f; f = files[i]; i++) {
            fd.append('files', f);//送信用Formデータに追加。キーの名前はコントローラの引数の名前と合わせておかないとダメ
        }
        AjaxPost(fd);//Ajaxでファイル送信
    }
    function handleDragOver(evt) {
        //ブラウザのデフォルトの動作をキャンセル
        evt.stopPropagation();
        evt.preventDefault();
        evt.dataTransfer.dropEffect = 'copy';
    }
    //送信
    function AjaxPost(fd) {
        $.ajax({
            type: "POST",
            url: "/Upload/TestUp",
            xhr: function () {//送信の進捗やりたいとき
                XHR = $.ajaxSettings.xhr();
                if (XHR.upload) {
                    XHR.upload.addEventListener('progress',
                            function (e) {
                                progre = parseInt(e.loaded / e.total * 10000) / 100;
                                document.getElementById("testprogress").value = progre;
                            }, false);
                }
                return XHR;
            },
            processData: false,
            contentType: false,
            data: fd,
            success: function (dataobj) {
                console.log(dataobj);
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                console.log(XMLHttpRequest)
            }
        });
    }
    var dropZone = document.getElementById('drop_zone');
    dropZone.addEventListener('dragover', handleDragOver, false);
    dropZone.addEventListener('drop', handleFileSelect, false);
</script>

■Controler

        [HttpPost]
        public ActionResult TestUp(HttpPostedFileBase[] files)
        {
            //filesにファイルが全部入ってるから後は保存でもなんでも好きにやる。
            return View();//きっと成功したらなんか返した方がいいけどとりあえず。
        }

ほんとはフォルダドロップで中のファイルを一括でとかにも対応したいんだけど、 割と面倒そうなので、ファイル複数選択してドロップ。今度、フォルダもやる。

問合せ