SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Monoでのプログラミング実例集

ASP.NET MVC on Mono
――「NerdDinner」をLinux上で動かしてみる

Mono環境でASP.NET MVCの動作確認を行う

  • X ポスト
  • このエントリーをはてなブックマークに追加

DbMetal

 DbLinqに含まれるDbMetal(DbMetal.exe)を使用することで、作成したSQLite DB「NerdDinner.sqlite」に対応するいくつかのクラス(DataContextクラス、各テーブルに対応するエンティティクラス)を含むC#コードを生成できます。

 DbMetal実行前に構成情報ファイル(DbMetal.exe.config)の準備、また「Mono.Data.Sqlite.dll」をDbMetalと同じディレクトリ(もしくは環境変数MONO_PATHに設定したディレクトリ)に配置する必要があります。

「DbMetal.exe.config」
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="providers" type="DbMetal.Configuration.ProvidersSection, DbMetal" />
  </configSections>
  <connectionStrings>
    <add name="DbMetal.Properties.Settings.TempOneConnectionString"
      connectionString="Data Source=.\sqlexpress;Initial Catalog=TempOne;Integrated Security=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="ClientSettingsProvider.ServiceUri" value="" />
  </appSettings>
  <providers>
    <providers>
      <provider name="MySQL" dbLinqSchemaLoader="DbLinq.MySql.MySqlSchemaLoader, DbLinq.MySql" databaseConnection="MySql.Data.MySqlClient.MySqlConnection, MySql.Data" />
      <provider name="Oracle" dbLinqSchemaLoader="DbLinq.Oracle.OracleSchemaLoader, DbLinq.Oracle" databaseConnection="System.Data.OracleClient.OracleConnection, System.Data.OracleClient" />
      <provider name="OracleODP" dbLinqSchemaLoader="DbLinq.Oracle.OracleSchemaLoader, DbLinq.Oracle" databaseConnection="Oracle.DataAccess.Client.OracleConnection, Oracle.DataAccess" />
      <provider name="PostgreSQL" dbLinqSchemaLoader="DbLinq.PostgreSql.PgsqlSchemaLoader, DbLinq.PostgreSql" databaseConnection="Npgsql.NpgsqlConnection, Npgsql" />
      <provider name="SQLite" dbLinqSchemaLoader="DbLinq.Sqlite.SqliteSchemaLoader, DbLinq.Sqlite" databaseConnection="Mono.Data.Sqlite.SqliteConnection, Mono.Data.Sqlite" />
      <provider name="Ingres" dbLinqSchemaLoader="DbLinq.Ingres.IngresSchemaLoader, DbLinq.Ingres" databaseConnection="Ingres.Client.IngresConnection, Ingres.Client" />
      <provider name="Firebird" dbLinqSchemaLoader="DbLinq.Firebird.FirebirdSchemaLoader, DbLinq.Firebird" databaseConnection="FirebirdSql.Data.FirebirdClient.FbConnection, FirebirdSql.Data.FirebirdClient" />
    </providers>
  </providers>
  <system.web>
    <membership defaultProvider="ClientAuthenticationMembershipProvider">
      <providers>
        <add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
      </providers>
    </membership>
    <roleManager defaultProvider="ClientRoleProvider" enabled="true">
      <providers>
        <add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
      </providers>
    </roleManager>
  </system.web>
</configuration>
「DBMetal.exe」実行例
$ ln -s /opt/mono/2.4.2.3/lib/mono/2.0/Mono.Data.Sqlite.dll .
$ mono DbMetal.exe -namespace:NerdDinner.Models -provider:SQLite -conn:"Data Source=NerdDinner.sqlite" -code:NerdDinner.DbMetal.cs
DbLinq Database mapping generator 2008 version 0.19
for Microsoft (R) .NET Framework version 3.5
Distributed under the MIT licence (http://linq.to/db/license)

>>> Reading schema from SQLite database
<<< writing C# classes in file 'NerdDinner.DbMetal.cs'

 生成されたC#コードは、NerdDinnerでの使用に合わせるために多少の修正を施す必要があります。

追加「プロジェクトルート/Models/NerdDinner.DbMetal.cs」から抜粋
using System;
using System.Data;
using System.Data.Linq.Mapping;
using System.Diagnostics;
using System.Reflection;
using DbLinq.Data.Linq;
using DbLinq.Vendor;
using System.ComponentModel;

namespace NerdDinner.Models {

    public partial class NerdDinnerDataContext : DbLinq.Data.Linq.DataContext {

        public NerdDinnerDataContext()
        : this(global::System.Configuration.ConfigurationManager.ConnectionStrings["NerdDinnerConnectionString"].ConnectionString)
        {
        }

        public NerdDinnerDataContext(string connectionString)
        : this(new Mono.Data.Sqlite.SqliteConnection(connectionString))
        {
        }

        public NerdDinnerDataContext(IDbConnection connection)
        : this(connection, new DbLinq.Sqlite.SqliteVendor())
        {
        }

        public NerdDinnerDataContext(IDbConnection connection, IVendor vendor)
        : base(connection, vendor)
        {
        }

        public Table<Dinner> Dinners { get { return GetTable<Dinner>(); } }
        public Table<RSVP> RSVPs { get { return GetTable<RSVP>(); } }
    }
...
}

 DbLinq(r1208)ではエンティティクラス内のOnValidateメソッドによるValidation(検証)の起動は有効にならないようです。ということでDinnerクラスのValidationの起動を変更する必要があります(「Controllers」で後述)。

「プロジェクトルート/Models/Dinner.cs」から抜粋
/* コメント化
partial void OnValidate(ChangeAction action) {
    if (!IsValid)
        throw new ApplicationException("Rule violations prevent saving");
}
 */

Models and Helpers

 元々のNerdDinner DBで定義されているユーザー定義関数は、パラメータとして与えられた位置(経緯度)から100km圏内のDinner情報を返すというものです。そこで、新たに2地点間の距離を測るメソッド(DinnerRepositoryHelper#DistanceBetween)を追加して、DinnerRepository#FindByLocationメソッドで使用されているLINQに組み込みました。

 DinnerRepository#FindUpcomingDinnersメソッドの修正については、SQLiteで定義できるデータ型(DateTime型に対応するのはText型)に基因する例外が発生したので、SQL文を直接記述する修正を行いました。

「プロジェクトルート/Models/DinnerRepository.cs」から抜粋
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using NerdDinner.Helpers; // 追加

namespace NerdDinner.Models {

    public class DinnerRepository : NerdDinner.Models.IDinnerRepository {

        NerdDinnerDataContext db = new NerdDinnerDataContext();

        //
        // Query Methods

        public IQueryable<Dinner> FindAllDinners() {
            return db.Dinners;
        }

        /* 修正前
        public IQueryable<Dinner> FindUpcomingDinners() {
            return from dinner in FindAllDinners()
                   where dinner.EventDate > DateTime.Now
                   orderby dinner.EventDate
                   select dinner;
        }

        public IQueryable<Dinner> FindByLocation(float latitude, float longitude) {
            var dinners = from dinner in FindUpcomingDinners()
                          join i in db.NearestDinners(latitude, longitude) 
                          on dinner.DinnerID equals i.DinnerID
                          select dinner;

            return dinners;
        }
         */

        // 修正後
        public IQueryable<Dinner> FindUpcomingDinners() {
            return (db.ExecuteQuery<Dinner>(
                     "SELECT * FROM Dinners WHERE EventDate > datetime('now', 'localtime') ORDER BY EventDate"))
                    .AsQueryable();
        }

        public IQueryable<Dinner> FindByLocation(float latitude, float longitude) {
            var dinners = from dinner in FindUpcomingDinners()
                          let d = (DinnerRepositoryHelper.DistanceBetween(latitude, longitude, dinner.Latitude, dinner.Longitude) < 100) ? dinner : null
                          where d != null
                          select d;

            return dinners;
        }
...
    }
}
追加「プロジェクトルート/Helpers/DinnerRepositoryHelper.cs」
using System;

namespace NerdDinner.Helpers {

    public static class ExtMethods {

        public static double ToRad(this double degrees) {
            return degrees * Math.PI / 180.0;
        }

        public static double Sin(this double degrees) {
            return Math.Sin(degrees.ToRad());
        }

        public static double Cos(this double degrees) {
            return Math.Cos(degrees.ToRad());
        }

        public static double Acos(this double cos) {
            return Math.Acos(cos);
        }
    }

    public class DinnerRepositoryHelper {

        public static double DistanceBetween(double lat1, double long1, double lat2, double long2) {

            double earthRadius = 6378.1; // kms

            double sinLat1  = lat1.Sin();
            double cosLat1  = lat1.Cos();

            double sinLong1 = long1.Sin();
            double cosLong1 = long1.Cos();

            double sinLat2  = lat2.Sin();
            double cosLat2  = lat2.Cos();

            double sinLong2 = long2.Sin();
            double cosLong2 = long2.Cos();

            double cos1_2   = ( cosLat1 * cosLong1 * cosLat2 * cosLong2 ) +
                              ( cosLat1 * sinLong1 * cosLat2 * sinLong2 ) +
                              ( sinLat1 * sinLat2 );

            return earthRadius * cos1_2.Acos();
        }
    }
}

次のページ
Views

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Monoでのプログラミング実例集連載記事一覧
この記事の著者

sta(エステーエー)

風来坊blog:sta.blockhead

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/4669 2010/01/21 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング