ネタ元:
.NET のアプリケーション アーキテクチャ実装例 (Part 3) (むらさん、DALC編)
.NET のアプリケーション アーキテクチャ実装例 (Part 2) (むらさん、BE編)
WCF&LINQを使った場合のDALCとBE ( Part1 ) (けろ-みお、BE編)
WCF&LINQを使った場合のDALCとBE( Part2 ) (けろ-みお、DALCで、LINQ to SQL)
WCF&LINQを使った場合のDALCとBE ( Part3 ) (けろ-みお、SIで、WCF)
WCF&LINQを使った場合のDALCとBE( Part4 ) ~UI は、ASP.NETとWPFだ!特別編 ~ (けろ-みお、UIで、ASP.NET&WPF)
今日のむらさんの.NET のアプリケーション アーキテクチャ実装例 (Part 3) (DALC編)を見て、
トランザクションには、5種類あるんだなと思いました。
しかも、キレイに纏めていらっしゃる。正直、悔しい。
くそー、負けてたまるかwww
実は、むらさんが今回BlogにUPしたDALCには、LINQのことは一切触れられていない。
たぶん、むらさんなりに考えて、あえて使わない方法を検討したのかもしれません。
(トランザクションの扱いに苦しんだのかなぁと思いました)
複雑なトランザクションを必要としない場合は、前回、けろがご紹介した、「LINQ to SQL」&「WCF」を使った対応でも
十分いけると思うんですよ。
なので、用途に応じて、むらさんの案とけろの案が事前にベースとして用意されていて
いざ実装する際は、どの手法を使ってコーディングするか、Choiceできるような、アーキテクトが理想だと、私は考えます。
(テンプレート用意するのが大変になりますけど、生産性を上げるには1つの手だと思います)
前回、LINQ to SQL & WCF で両方扱えるEntity(ビジネスエンティティ)を定義したサンプルをご紹介しましたが、
今回は、このサンプルを更に拡張して、「BC」(ビジネスコンポーネント)の構成に焦点を当ててみました。
ヒントにしたのは、「Multi tier architecture for Linq to Sql」 で紹介しているコードです。
「Multi tier architecture for Linq to Sql」 では、DataContextの操作やEntityとのマッピングを
コントローラー(今回、むらさんが紹介しているアーキテクトでは、UIPの部分にあたります)で、処理するやり方で紹介されている。
下記一部抜粋(C#です)
1: [System.ComponentModel.DataObject]
2: public class GenericController<TEntity, TDataContext> where TDataContext : DataContext
3: {
4: public static List<TEntity> SelectAll()
5: {
6: ...
7: }
8: public static void Insert(TEntity entity)
9: {
10: ...
11: }
12: public static void Update(TEntity entity)
13: {
14: ...
15: }
16: public static void Delete(TEntity entity)
17: {
18: ...
19: }
20: }
しかし、これらのメソッドをコントローラーで、定義させるのは問題ないが、DataContextの処理をコントローラーで定義するのは、
個人的には納得いかない。
小規模なシステムなら保守もできるとは思いますが、大規模システムになると、コントローラーでDataContextの操作をすると、
コントローラーの膨大化につながり、保守性は下がることはあっても上がることはないと思っています。
やはり、DataContextの処理は、DALC、もしくは、譲ったとしても、BC・BWに任せるべきだと考えます。
説明するより、コードで説明した方が早いので、簡単なサンプルコードを書いてみます。
前回のシリーズで取り上げたソースとダブっている箇所は、省略します。
前回のシリーズのソースを参考にされたい方は、過去のネタ元を見て下さい。
(特にWCF&LINQ to SQL の両方で利用が可能なEntity = BEの部分は、省略します)
(あくまでアーキテクトとしての実装例なので、LINQ の細かい構文までは気にして書いてません。なので使い方がヘンです。ご了承ください。)
■DALC(LINQ to SQL)
○ ぴんくまメンバーDataContext (オリジナルのDataContextクラスを自前で用意します)
VB用
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Data.Linq
Imports System.Data.Linq.Mapping
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Reflection
<System.Data.Linq.Mapping.DatabaseAttribute(Name:="TEST")> _
Partial Public Class ぴんくまDataContext(Of TEntity)
Inherits System.Data.Linq.DataContext
Private Shared mappingSource As System.Data.Linq.Mapping.MappingSource = New AttributeMappingSource
Shared Sub New()
End Sub
Partial Private Sub OnCreated()
End Sub
Public Sub New(ByVal connection As String)
MyBase.New(connection, mappingSource)
OnCreated()
End Sub
Public Sub New(ByVal connection As System.Data.IDbConnection)
MyBase.New(connection, mappingSource)
OnCreated()
End Sub
Public ReadOnly Property GetQuery() As IQueryable(Of TEntity)
Get
Return Me.GetTable(GetType(TEntity)).AsQueryable()
End Get
End Property
End Class
C#用
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
[System.Data.Linq.Mapping.DatabaseAttribute(Name = "TEST")]
partial public class ぴんくまDataContext<TEntity> : System.Data.Linq.DataContext
{
private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
static ぴんくまDataContext()
{
}
private partial void OnCreated()
{
}
public ぴんくまDataContext(string connection) : base(connection, mappingSource)
{
OnCreated();
}
public ぴんくまDataContext(System.Data.IDbConnection connection) : base(connection, mappingSource)
{
OnCreated();
}
public IQueryable<TEntity> GetQuery {
get { return this.GetTable(typeof(TEntity)).AsQueryable(); }
}
}
○ ぴんくまメンバーDALC (DataContextの操作)
VB用
Imports System.Configuration
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Data.Linq
Public Class ぴんくまメンバーDALC(Of TEntity)
' DB接続文字列
Private _ConnectionString As String
' コンストラクタ
Public Sub New()
_ConnectionString = "app.config or machine.config から接続文字列取得です"
End Sub
' LINQでデータ取得
Public Function SelectAll() As List(Of TEntity)
Dim records As IQueryable(Of TEntity)
Dim returnValue As List(Of TEntity)
Using db As New ぴんくまDataContext(Of TEntity)(_ConnectionString)
records = (From rows In db.GetQuery() Select rows)
returnValue = records.ToList()
End Using
Return returnValue
End Function
' LINQでデータ取得(1件のみ)
Public Function SelectBy(ByVal key As Integer) As TEntity
Dim returnValue As TEntity
Using db As New ぴんくまDataContext(Of TEntity)(_ConnectionString)
For Each record In From rows In db.GetTable(GetType(TEntity)) Select rows
If record.番号 = key Then
returnValue = record
Continue For
End If
Next
End Using
Return returnValue
End Function
End Class
C#用
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Linq;
public class ぴんくまメンバーDALC<TEntity>
{
// DB接続文字列
private string _ConnectionString;
// コンストラクタ
public ぴんくまメンバーDALC()
{
_ConnectionString = "app.config or machine.config から接続文字列取得です";
}
// LINQでデータ取得
public List<TEntity> SelectAll()
{
IQueryable<TEntity> records;
List<TEntity> returnValue;
using (ぴんくまDataContext<TEntity> db = new ぴんくまDataContext<TEntity>(_ConnectionString)) {
records = From rows In db.GetQuery() Select rows
returnValue = records.ToList();
}
return returnValue;
}
// LINQでデータ取得(1件のみ)
public TEntity SelectBy(int key)
{
TEntity returnValue;
using (ぴんくまDataContext<TEntity> db = new ぴんくまDataContext<TEntity>(_ConnectionString)) {
foreach( record In From rows In db.GetTable(GetType(TEntity)) Select rows) {
if (record.番号 == key) {
returnValue = record;
continue;
}
}
}
return returnValue;
}
}
■BC(ビジネスコンポーネント)
○ IBussinessCompornent(BCのインターフェース)
# BCの操作は単一トランザクションが多いので、
CURD操作単位のメソッドを用意することを「決まり」にしてしまっても良いと思うという考えからインターフェースにしました。
VB用
Public Interface IBussinessComponent(Of TEntity)
Function SelectAll() As List(Of TEntity)
Function SelectBy(ByVal key As Object) As TEntity
Sub Insert(ByVal entity As TEntity)
Sub Update(ByVal entity As TEntity)
Sub Delete(ByVal entity As TEntity)
End Interface
C#用
public interface IBussinessComponent<TEntity>
{
List<TEntity> SelectAll();
TEntity SelectBy(object key);
void Insert(TEntity entity);
void Update(TEntity entity);
void Delete(TEntity entity);
}
○ 上記インターフェースを適用したクラスをBC上に用意。BCから、CURD別に該当するDALCを呼ぶ
VB用
<System.Runtime.InteropServices.ComVisible(False)> _
Public Class ぴんくまメンバーBC(Of TEntity)
Implements IBussinessComponent(Of TEntity)
Private _DALC As DALC.ぴんくまメンバーDALC(Of TEntity)
' コンストラクタ
Public Sub New()
_DALC = New DALC.ぴんくまメンバーDALC(Of TEntity)
End Sub
' 全件検索
Public Function SelectAll() As System.Collections.Generic.List(Of TEntity) Implements IBussinessComponent(Of TEntity).SelectAll
Return _DALC.SelectAll()
End Function
' キー指定検索
Public Function SelectBy(ByVal key As Object) As TEntity Implements IBussinessComponent(Of TEntity).SelectBy
Return _DALC.SelectBy(Convert.ToInt32(key))
End Function
' 削除
Public Sub Delete(ByVal entity As TEntity) Implements IBussinessComponent(Of TEntity).Delete
End Sub
' 登録
Public Sub Insert(ByVal entity As TEntity) Implements IBussinessComponent(Of TEntity).Insert
End Sub
' 更新
Public Sub Update(ByVal entity As TEntity) Implements IBussinessComponent(Of TEntity).Update
End Sub
End Class
C#用
[System.Runtime.InteropServices.ComVisible(false)]
public class ぴんくまメンバーBC<TEntity> : IBussinessComponent<TEntity>
{
private DALC.ぴんくまメンバーDALC<TEntity> _DALC;
// コンストラクタ
public ぴんくまメンバーBC()
{
_DALC = new DALC.ぴんくまメンバーDALC<TEntity>();
}
// 全件検索
public System.Collections.Generic.List<TEntity> IBussinessComponent<TEntity>.SelectAll()
{
return _DALC.SelectAll();
}
// キー指定検索
public TEntity IBussinessComponent<TEntity>.SelectBy(object key)
{
return _DALC.SelectBy(Convert.ToInt32(key));
}
// 削除
public void IBussinessComponent<TEntity>.Delete(TEntity entity)
{
}
// 登録
public void IBussinessComponent<TEntity>.Insert(TEntity entity)
{
}
// 更新
public void IBussinessComponent<TEntity>.Update(TEntity entity)
{
}
}
あとは、SI(WCF)が、これらのBCを呼ぶだけです。
Entity(BE)もWCFとLINQ用で両方対応するような作りにしておけば、DALCやBCで複雑なことをしなくても済む。
単一トランザクションや単純なマスタメンテナンス画面であれば、これだけで十分です。
複数トランザクションや複雑な業務ロジックに対処するには、BW(Bussiness Workflow)内で、ロジックを書くか、
あとは、BW内に、WFを定義してタスクを実行させる‥で対処できると思っています。
WFが、各BCのコンポーネントを操作できるかどうかは、まだ検証していないので、わかりませんが、BCの定義さえきちんとしておけば
問題ないというのが、私の考えです。
2008には、いったいどのアプリケーションアーキテクトが良いのか、まだまだ研究中です・・・
(突っ込みどころがまだまだあることは、自分でもわかってるのが、つらいところです。)
2008/02/18
私の旧友(同級生です)が、最近、Expression Webのお勉強を始めた。
でも、その同級生、IT関係の仕事しているわけじゃないので、素人です。
簡単なHTMLぐらいなら書けるらしく、今、JavaScriptとCSS、XHTMLも合わせてお勉強中らしいですw
(XHTMLは、難しいと言って、断念してましたけど、本当に断念しちゃうのかな?)
そのため、最近、私宛にいろんな質問をしてきます。(HTMLとCSS、JavaScriptに関してですけどね)
何事も熱心に何かを吸収しようとしたり、勉強することは、良いことですね。
その勉強熱心さを、私も見習わなきゃと思いました。
近い将来、Expression Web の使い方をマスターされたら、きっと私にこう言うんだろうなぁ・・・
「ええ?みおちゃん、そんなことも知らないの?」
それだけは、絶対、絶対、避けたいw いちおー、それで、飯食ってるんでw
というわけで、私も負けてられないと思い、Expression Webを今回、初めて使ってみました。
Expression Web は、HTMLやCSSなど、静的サイトだけでなく、ASP.NETのアプリが比較的簡単に作れるのが良いですね。
しかし、1つだけ、ASP.NETのWebサイトをExpression Webで作成する場合、サポートしていないものを見つけてしまった・・・
それは、「Skinファイルが作れない」です。
どこ探してもないんですよね。スタイルシートとか、マスターページはあるのに。
仕方がないので、新規作成メニューから「標準」を選び、「テキストファイル」を選択して、
そこでサーバーコントロールのデザイン定義を記述するようにしました。
なんで、Skinファイルは、Expression Webに含めなかったのか、不思議です。
テキストファイルで代用できるからいいや!だったのか、または、
あんまり用途がないという意味で、削られちゃったのかなぁ?
私、個人的には削らないで欲しかったと思いました。やっぱり、Visual Studio の方がいいやw
2008/02/11
mixiでも、ノートPCを買いに行くんですというお話をしていたら、UNYORAさんが、マウスコンピューターの「J131S」という
ノートPCを教えてくれました。
「J131S」
http://www.mouse-jp.co.jp/company/news/2008/news_20080124_01.html
http://www.mouse-jp.co.jp/m-book/luvbookj/0802/j131s.html
この機種が大変気に入って、一昨日、マウスコンピューターの秋葉原ダイレクトショップに行って、
「119,700円」(税込だと122,850円)で、購入しました。(値段も安かったので、キャッシュで購入しました。)
しかし、今、在庫がないらしく、後日、取りに行って参ります。
手元に来るのが本当楽しみです!
最後の最後まで購入するPCを悩んだのですが、このスペックとこの価格に負けてしまいました orz
いろいろとアドバイスをくださった皆様に、心からお礼申し上げたいと思います。ありがとうございます。
また、決め手となる情報を教えて頂きました、UNYORAさんにも、お礼申し上げます。
それから、ノートPCとは別に、emobileのデータカードを「1円」で購入しました。
D01NE ってやつですね。
恐らく、某王子が使っているものと同じ?かなと思います。
新しいノートPCで出先で使用できるように購入してみました。
もともと、emobileは、S01SH の端末を持っていましたので、月額利用料金の請求は1つに纏めて欲しいと
お願いしたところ、問題なしとのことだったので、データカードの購入に踏み切った次第です。
そういえば、ひろえむさんも先日、S01SH ?(かな?)を購入されたようで、
RSSリーダーはどうしてますか?と聞かれました。なかなか良いものがないので、使ってないですと
申し上げたのですが、Windows Mobile で使用できるお勧めのRSSリーダーがあれば、
是非、教えて頂きたいなと思います。
2008/01/29
ネタ元:http://oshiete1.goo.ne.jp/qa3708995.html (教えて! goo)
ネタ元の質問の概要ですが、
・ DBの中には、300個のテーブルがある、これをすべてビューにしたい
・ しかし、1ずつテーブルからビューを作るのは、時間がかかるので、効率良くやりたい(ようは、ツール化、スクリプト化したい)
・ ただし、元テーブルには、text型の項目がいくつかあり、varchar型に変換して、VIEWにしたい。
また、text型の項目はテーブルによって、含まれている場合と含まれていない場合がある。
だそうです。
おもしろそうだったので、やってみました。
まずは、こんな感じのテーブルを3つ用意してみた。
(この質問では、300個のテーブルとなっているが、そこまでは作らなかったです)
-- テーブル1
CREATE TABLE [テーブル1] (
[id] [int] NOT NULL ,
[aaa] [nvarchar] (50) COLLATE Japanese_CI_AS NULL ,
[bbb] [text] COLLATE Japanese_CI_AS NULL ,
[createdatetime] [datetime] NULL ,
CONSTRAINT [PK_テーブル1] PRIMARY KEY CLUSTERED
(
[id]
) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
-- テーブル2
CREATE TABLE [テーブル2] (
[id] [int] NOT NULL ,
[aaa] [nvarchar] (50) COLLATE Japanese_CI_AS NULL ,
[bbb] [text] COLLATE Japanese_CI_AS NULL ,
[createdatetime] [datetime] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
-- テーブル3
CREATE TABLE [テーブル3] (
[id] [int] NOT NULL ,
[aaa] [nvarchar] (50) COLLATE Japanese_CI_AS NULL ,
[bbb] [text] COLLATE Japanese_CI_AS NULL ,
[createdatetime] [datetime] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
次に、ビュー名称は、上記のテーブル名称に、”v_” を付けて 「v_テーブル1」「v_テーブル2」「v_テーブル3」..........................................のようにしてみた。
これらを効率良くビューにするのであれば、下記のポイントさえ押さえれば、簡単に自動化(スクリプト化)できる。
・DB内に作成されている、すべてのユーザテーブルは、「dbo.sysobjects」(2005 だと、sys.objects だったっけかな?)という
システムテーブルに保存されているので、この「dbo.sysobjects」からDB内に定義されているすべてのユーザテーブル名称を取得する。
・次にテーブル内で定義されている項目情報は、システムテーブルの「dbo.syscolumns」(2005だと、sys.columns だったっけかな?)に
保存されているので、この情報を取れば、項目名、型、サイズ等が取得できる。
・「dbo.sysobjects」と「dbo.syscolumns」は、親子関係にある。(リレーションは張られてたっけかな?忘れた)
これだけわかれば、あとは、T-SQLでスクリプトを作ってやるだけ。初心者プログラマさんでもできる。
-- VIEWの作成元になるテーブル名称格納エリア
DECLARE @table_name varchar(256)
DECLARE @view_name varchar(256)
-- テーブル項目情報格納変数定義
DECLARE @column_name sysname
DECLARE @type_name sysname
DECLARE @length smallint
-- CREATE VIEW用のSQL文格納エリア
DECLARE @sql varchar(8000)
-- ループカウンタ
DECLARE @cnt smallint
-- DB内に作成されている全テーブルを取得する
DECLARE tablelist CURSOR FOR
SELECT [name] AS table_name FROM dbo.sysobjects WHERE type = 'U' and info = '4'
OPEN tablelist
FETCH NEXT FROM tablelist INTO @table_name
-- VIEW作成元の対象テーブルがなくなるまで
WHILE @@FETCH_STATUS = 0
BEGIN
-- テーブル内のカラム情報を取得する
DECLARE colmnlist CURSOR FOR
SELECT
c.[name] AS [column_name],
t.[name] AS [type_name],
c.[length]
FROM dbo.syscolumns c INNER JOIN dbo.systypes t ON
c.[xtype] = t.[xtype]
WHERE [id] = OBJECT_ID(@table_name) AND RTRIM(t.[name]) <> 'sysname'
ORDER BY c.[colid]
OPEN colmnlist
FETCH NEXT FROM colmnlist
INTO @column_name, @type_name, @length
-- ループカウンタの初期化
SET @cnt = 0
SET @view_name = 'v_' + @table_name
SET @sql = 'CREATE VIEW ' + @view_name + ' AS '
SET @sql = @sql + ' SELECT '
-- カラム情報がなくなるまで
WHILE @@FETCH_STATUS = 0
BEGIN
IF @cnt <> 0
BEGIN
SET @sql = @sql + ', '
END
-- もし、カラムがtext型項目の場合は、VARCHARにする。また改行コードをカットする
IF @type_name = 'text'
BEGIN
-- text型の場合は、改行文字をカットする
SET @sql = @sql + 'REPLACE(CAST(' + @column_name + ' AS varchar(8000)), ''0x0D0A'', '' '') AS ' + @column_name
END
ELSE
BEGIN
-- text型以外の場合
SET @sql = @sql + @column_name
END
-- 次のカラム情報へ
FETCH NEXT FROM colmnlist
INTO @column_name, @type_name, @length
-- ループカウンタをインクリメント
SET @cnt = @cnt + 1
END
SET @sql = @sql + ' FROM ' + @table_name
-- VIEWを作成する
EXEC(@sql)
PRINT @sql
-- カラム情報のカーソルを閉じる
CLOSE colmnlist
DEALLOCATE colmnlist
FETCH NEXT FROM tablelist INTO @table_name
END
-- カーソルを閉じる
CLOSE tablelist
DEALLOCATE tablelist
なんだ、簡単じゃんwww
ただ、ちゃんとしたツールにするのであれば、これらのコードは、ある程度、ストアド化しておいた方がいいですね。
本当、10分でできた。
後は、スレ主さんが、理解してくれればいいのだが・・・・・・・・・・
2008/01/25