ラベル .NET Framework の投稿を表示しています。 すべての投稿を表示
ラベル .NET Framework の投稿を表示しています。 すべての投稿を表示

2016年9月15日木曜日

ユニットテストでlog4netのログを出力する方法

いまさらな内容だけど備忘録としてユニットテストでlog4netのログを出力する方法を記しておく。

static constructorで下記設定を行っておけばユニットテストの結果Windowの下にログが出力される。

[Subject("Test")]
public class TestSpec
{
        static TestSpec()
        {
                // MEMO : ユニットテストでlog4netを使用するための設定
                var consoleAppender = new log4net.Appender.ConsoleAppender { Layout = new SimpleLayout() };
                BasicConfigurator.Configure(consoleAppender);
        }

        // 以下省略
}

2015年2月19日木曜日

.NETプログラム実行時にDLLの参照エラーになったときようのツール

実行時にDLLが見つからずにエラーになるのはままあることで原因を特定するのはさほど難しくないけれど面倒な作業である。とくにライブラリで参照しているDLLと呼び出し元のプロジェクトで参照しているDLLに差が出ることは、Nugetでパッケージを取得することが増えた昨今はよく起こる。

ということが今の職場でよく起きてそれを調べる用のツールを作ったので公開する。

Missing Reference Finder
https://github.com/yooontheearth/missing-reference-finder



使い方はいたって簡単。Pathにbin等のフォルダを指定してcheckボタンを押下するだけ。指定のフォルダかGACにないものはMISSING扱いになる。

2011年7月2日土曜日

LambdaExpressionで動的な検索条件を作る

最近の仕事で固定の検索条件ではなく動的に検索条件を作りたいという仕様があり、それもあってExpression周りをいじくりまわしていて楽しかったので解説する。

今回のサンプルコードはこちらからダウンロードできる。


Linqを使用する場合に動的に検索条件を作るためにはExpression Treeと戯れる必要がある。Linq To SqlであればDynamic Linqという文字列を解析してExpression Treeに変換してくれる拡張関数群が公開されているので、文字列をいじくるのを苦にしなければかなり簡単に動的な検索条件を作成することが可能だ。しかし、Linq To Entityだとそうはいかないのでちょっと込み入ったことをしなくてはならない。

動的検索条件と言っても、すべての検索条件がAnd条件であるならば、単純にWhereメソッドを下記のように順々に呼び出していけばよいだろう。

if ( 条件1 )
  query = query.Where(c => c.Name == "四郎" );

if ( 条件2)
  query = query.Where(c => c.Age > 15 );

※今回の解説の仕様として、等号などのオペレーターや対象プロパティは事前に決まっていてどの条件を組み合わせるかのみ動的に設定できる、という設定とする。オペレーターやプロパティもすべてを動的に作る場合にはParameterExpressionなどを使ってさらに深くExpression Treeをいじる必要があるのでその解説は次回以降行う、かもしれない。

しかし、Or検索が間に入ってくると話が変わってくる。前述の方法ではWhereメソッド間はAnd条件になるので動的なOr検索を行うことはできない。というわけで、LambdaExpressionの登場だ。こいつを上手に使ってやると下記のような条件を簡単に動的に組み立てることができる。

query.Where(c => (c.Name.Contains("白") || c.Bushous.Any(b => b.Age > 36)) && c.Name.EndsWith("城"));

今回使用するモデルは下記。DBの作成はEF Code First4.1に全部任せている。
public class Bushou
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int CastleId { get; set; }
    public Castle Castle { get; set; }
}

public class Castle
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual IList<Bushou> Bushous { get; set; }
}
    
public class Ikusa : DbContext
{
    public DbSet<Bushou> Bushous { get; set; }
    public DbSet<Castle> Castles { get; set; }
}

前述の各条件を表すLambdaExpressionは下記になる。

Expression<Func<Castle, bool>> condition1 = c => c.Name.Contains("白");
Expression<Func<Castle, bool>> condition2 = c => c.Bushous.Any(b => b.Age > 36);
Expression<Func<Castle, bool>> condition3 = c => c.Name.EndsWith("城");

このままでは各LambdaExpressionはばらばらなので一つにまとめる必要がある。その際に注意が必要なのだが、LambdaExpressionを統合するには同一の引数を使用するように変更してやらなければならない。上記の定義を見てもらえば分かると思うけれど、各LambdaExpressionにはcという引数が定義されている。Expression Tree的にはcondition1のcとcondition2、condition3のcはまったくの別物なので、こいつらを一つにまとめる必要がある。

で、そんな面倒なことを行う際に便利なクラスがC#4.0から提供されている。それがExpressionVisitorクラスだ。このクラスの使いどころは次のような場合だ。Expressionには多種多様なタイプが用意されている。go toステートメントを表すGoToExpression、ループを表すLoopExpression、メンバーアクセスを表すMemberExpressionなどなど数十種類に及ぶ。で、今回は引数をいじくる必要があるのでParameterExpressionが対象になる。しかしExpression Treeを順々にたどっていって任意のExpressionを探すのは骨が折れる。なのでそんなときはExpressionVisitorクラスを使用する。

使い方はExpressionVisitorクラスを継承し、自分が気になるタイプのExpressionを引数としているメソッドをオーバーライドするだけだ。今回はParameterExpressionをいじくるのでVisitParameterがオーバーライドするメソッドになる。

class ParameterVisitor : ExpressionVisitor
{
    private ReadOnlyCollection<ParameterExpression> _fromParams, _toParams;
    public ParameterVisitor(ReadOnlyCollection<ParameterExpression> from, ReadOnlyCollection<ParameterExpression> to)
    {
        _fromParams = from;
        _toParams = to;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        for (var i = 0; i < _fromParams.Count; i++)
        {
            if (node == _fromParams[i])
                return _toParams[i];
        }
        return node;
    }
}

ParameterVisitorクラスは変更元の引数コレクションと変更先の引数コレクションを保持し、ParameterExpressionが見つかるたびに、その見つかった引数(上記で言うところのnode)が変更元の引数と同一かをチェックし、同一であれば、変更先の引数を返却している。変更先の引数を返却することで変更元のExpression Treeは差し替えられている。つまり、変更元の引数の数と、変更先の引数の数が異なる場合はアボンするということなので注意が必要だ。ParameterVisitorクラスのコンストラクタにそれようのチェックコードを入れるのも良いだろう。

実際にParameterVisitorクラスの使用方法は下記になる。

class Helper
{
    public static Expression<Func<T, bool>> AndAlso<T>(Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
    {
        var newExp = new ParameterVisitor(right.Parameters, left.Parameters).Visit(right.Body);
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left.Body, newExp), left.Parameters);
    }

    public static Expression<Func<T, bool>> OrElse<T>(Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
    {
        var newExp = new ParameterVisitor(right.Parameters, left.Parameters).Visit(right.Body);
        return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left.Body, newExp), left.Parameters);
    }
}

各メソッドともに、まずはVisitメソッドを呼び出し、二個目の引数のLambdaExpressionの引数を一個目の引数のLambdaExpressionの引数で差し替えている。その後にAndAlso、またはOrElseを呼び出しExpressionの統合を行っている。

Helperクラスの使い方は下記になる。

var condition = Helper.AndAlso(Helper.OrElse(condition1, condition2), condition3);

そうしてできたLambdaExpressionをWhereメソッドに渡してやれば下記と同等の記述になる。

query.Where(c => (c.Name.Contains("白") || c.Bushous.Any(b => b.Age > 36)) && c.Name.EndsWith("城"));

query = query.Where(condition);

かなり冗長になるけれど、上記の方法以外にも下記のようにWhereメソッドを呼び出すこともできる。

var resultExp = Expression.Call(typeof(Queryable),
                                "Where",
                                new Type[] { typeof(Castle) },
                                query.Expression,
                                Expression.Quote(condition));
query = query.Provider.CreateQuery<Castle>(resultExp);

今回の例では下のほうの呼び出し方をする必要はまったくないけれど、オペレーターやプロパティも含めてすべてを動的で行う場合などで、Anyやら何やらを動的に呼び出す必要がある場合は、最後に解説したような呼び出し方をしなければならない。

2010年7月4日日曜日

POCOとはなんぞや 自分的まとめ

以前、MIX 10のセッションビデオの中で「こういうのはPOCOで開発するんだよ」と解説してくれているのがあって(どのセッションかは失念)、そのときはたいして気にもしていなかったのだが、最近データアクセス層を作っているときに、おっ、ここはいっちょPOCOってみるか、と思いつき色々調べたらよく分からなかったので、今後のためにまとめておく。
(※間違っているかも知れないので話半分で聞いてください)

理解の元ネタは下記から収集。
POCO vs DTO at stackoverflow
POCO As A Lifestyle
What is the Difference Between a DTO and a POCO?

POCO vs DTOとなっているのは、一見POCOとDTOは似ているので、POCOのことを正確に理解していない人は「POCOってのはDTOと一緒だよ」とのたまうらしく、そちらの世界に導かれると「じゃぁなんでPOCOが必要なの?」って質問に答えられなくなるので、ホントのところはどうなってんだよ?という魂の叫びの表れである。

DTOはData Transfer Objectの略称で、DTOの唯一の目的は状態(データとかも含むのかな?)を他の層(とか他のアプリなど)へ渡すことにあって、OOP(オブジェクト指向プログラミング)的な振る舞い(特にビジネスロジック的なもの)を持つべきではない。必要な情報を渡すためだけの箱であって、それ以上でも以下でもない。

例:
// 正しいDTO
class PersonRightDTO 
{
 public string Name { get; set; }
 public DateTime Birthday { get; set; }
}

// 正しくないDTO
class PersonWrongDTO
{
 public string Name { get; set; }
 public DateTime Birthday { get; set; }
 public int GetAge() { 
  return // Calculate here.
 }
}

POCOはPlain Old CLR Objectの略称で、これはPOJO(Plain Old Java Object)から来ている(POJOのWikiはこちら)。で、POCOはPOJOと同じく、クラスの設計に.net frameworkをかませない(命令させない、規制させない)で行おう、という考え方。

なので、ここまでをまとめると、POCOはシンプルなOOPへのアプローチ方法の説明で、DTOはデータをどっかに渡すためのパターン、となる


で、ここから自分の解釈になるのだけど、こんな風になるのかな、と図解してみた。


ビジネスエンティティやドメインモデルと呼ばれるているオブジェクトがPOCOである。各層をつなぐ矢印があるけれど、その際に渡すオブジェクト(があるならば)はDTOでもPOCOでも良い。ドメインモデルと合致しないようなデータを渡すだけならば、それようにDTOを作って渡す方が良い。なので、DTOはPOCOよりもシンプルな構造になる。

最後に、POCOはPersistence Ignorance(PI:永続化に無知。変な日本語で申し訳ないけれど訳し方が分からない)でなければならない。つまり、Save()や、GetDataById()のような、永続性を持つロジック(データの読み込み、保存など)を実装してはならない。そのようなロジックはデータアクセス層に実装しよう。

ということでPOCOを使うと各層を明確に分離できるので、より良い構造になる。


以下にDDD(Domain Driven Design)を理解するのに良いと勧められていた本を紹介しておく。Google BooksにPreviewがあるのでそちらもどうぞ。

Domain-Driven Design: Tackling Complexity in the Heart of Software

2010年4月8日木曜日

EncodingとDecodingをクライアントアプリで行う .NET Framework 4.0

.NET Frameworkを使ったWindowsアプリで何かWeb用のデータなどを編集するようなものやURLをいじくるものの場合はEncoding、Decodingがしたくなるものだ。で、ASP.NETを使っている開発者ならばすぐにSystem.Web.dllにあるSystem.Web.HttpUtilityへと思いが飛び、HttpUtility.HtmlEncode(string), HttpUtility.HtmlDecode(string)を使おうとするのだが、Windowsアプリの初期状態のプロジェクトテンプレートにはSystem.Web.dllが含まれていないのでインテリセンスでは当然ながら見つからず、「あれ?あのユーティリティどこ(の名前空間)にあったっけ?」と思い出すまでやきもきするのだが、.NET Framework 4.0からはSystem.dllのSystem.Net名前空間にWebUtilityクラスが実装されたのでそのモヤモヤともついにおさらばだ。

WebUtilityのMSDN

以前からクライアントアプリなのにEndoding, DecodingするためだけにASP.NETのアセンブリを参照することに抵抗があったのでSystem.dllにWebUtilityが実装されて嬉しい限りだ。ちなみに今回の情報は下記のサイトから。

Encoding/Decoding URIs and HTML in the .NET 4 Client Profile

2010年1月30日土曜日

文字列のフォーマット

.net frameworkで1500という値を文字列で1,500と表示したい場合や、1,500.05と表示したい場合、下記のように記述する。

var number = 1500.05;
Console.WriteLine(string.Format("{0:N0}", number));
Console.WriteLine(string.Format("{0:N2}", number));

// Output
// 1,500
// 1,500.05

で、数字を任意のフォーマットに変更する場合、目的と合致するフォーマット文字(?NとかCとか)を調べるのが思いのほか大変だったので、msdnへのリンクを下記に記載しておく。

Formatting Overview

2010年1月15日金曜日

英単語勉強用アプリ作成シリーズ ~ Study WPF

前回Linq To XSDについて調査をし、いよいよ実装に入ろうとしたのだが、UI周りを通常のWindowsアプリとは違う動きのあるものにしたいな、と思い立ちWindows Presentation Foundationを使うことにした。

で、MSDNのGetting Startedにも目を通したけれど、順を追っての説明がなかったのでいまいち要領を得ない。というわけで本を購入した。



すこぶる良書なのでWPFの勉強を1から行いたい人にお勧めだ。サンプルを作りながら一つ一つの機能について詳しく説明してくれるので慣れながら学べる。
※注:.Net Framework初心者用の本ではないので、読者にはC#と.Net Framework 3.5の知識が多少なりともないと厳しいと思う。

2010年1月12日火曜日

英単語勉強用アプリ作成シリーズ ~ Linq to XSDが動くまで

英単語勉強用に使えるアプリが無いかと探したけれどびびっと来るのがないので自作することにした。行く行くはモバイル用も作って出先でも使えるようにしたいけれど、まずはWindowsアプリでざっくりと仕上げてその簡易版をモバイル用にしようと考えている。

で、元データに使えるものが無いかと探していたら良いソースを見つけたので下記のものを使うことにした。

The EDICT Dictionary File

上記のサイトでは英単語の情報がXML形式で提供されているので、そのxmlファイルをそのままDataSourceとすることにした。もちろんそのときは頭でLinq To Xmlを使(つこ)うちゃる!と息巻いていたのだが、このLinq To Xml、調べれば調べるほどに落胆の色が隠せない。というのも下記のコードを見てもらえば分かるが、Linq To Xmlでは型付けされていない情報を扱うので、何らかの値を取得するためには常にCastが必要となるからだ。

XElement root = XElement.Load("PurchaseOrderInNamespace.xml");
XNamespace aw = "http://www.adventure-works.com";
IEnumerable<xelement> address =
    from el in root.Elements(aw + "Address")
    where (string)el.Attribute(aw + "Type") == "Billing"
    select el;
foreach (XElement el in address)
    Console.WriteLine(el);

これは大変よろしくない。かなりの手間だ。そんなプログラミングは楽しくないのでわざわざ余暇を使ってする意味が無い。それなので色々とGoogle先生にお伺いを立てたらナイスなプロジェクトをCodePlexで発見した。

LINQ to XSD
(このプロジェクトを使用したい場合は17994とかそういう番号になっているLinkからソースコードをダウンロードしよう)

そう、名前からも分かるとおり、このプロジェクトを使えばXSDで強力に型付けされたClassを扱うことができる。上記のコードが下記のような感じになる。

var list_books = from c in xmlFile.Customers
                 order by c.Name
                 select c;

-------------------------
ではこれからその導入方法の説明に入ろう。Code Projectにコードのサンプルがあったので以下に併記しておく。

LINQ to XSD Sample at the Code Project

Linq To XSDを使うためには、はじめに環境変数を追加する必要があるので下記の手順どおりに追加して欲しい。

コンピュータ -> プロパティ -> システムの詳細設定 -> 環境変数 -> システム環境変数の新規ボタン
変数名:LinqToXsdBinDir
変数値:c:\LinqToXSDBin(※このパスはどこでもよい)

環境変数の追加ができたら下記の手順どおりに進めて欲しい。

  1. 前述したCodePlexのLinkからダウンロードした.zipを解凍する
  2. 解凍したフォルダの中にあるXObjects\XObjects.slnを起動しコンパイルする(先ほど設定した環境変数のパスにdllなどが出力されているのを確認すること)
  3. Linq To XSDを使用したいプロジェクトファイル(yourproject.csprojなど)をnotepadなどで開き下記の内容をコピペする。
    ※注意:必ず他のImport要素たちの後ろに追加すること(<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />など)

    <PropertyGroup>
    <LinqToXsdBinDir Condition="'$(LinqToXsdBinDir)' == ''">$(SolutionDir)</LinqToXsdBinDir>
    </PropertyGroup>
    <Import Project="$(LinqToXsdBinDir)\LinqToXsd.targets" />
  4. 上記のプロジェクトファイルをVisual Studioで起動しXSDファイルを追加する
  5. 追加したXSDファイルのビルドアクションをLinqToXSDSchemaとする
  6. プロジェクトをコンパイルするとインテリセンスが使用できるようになっていることを確認すること

これでLinq To XSDを使う準備が整った。次回以降、開発中に気付いたことなどがあれば紹介する予定だ。

2009年12月9日水曜日

LinqToSqlでランダムセレクト

今回はLinqToSqlでレコードをランダムに取得する方法を解説する。ランダムに画像を複数件表示しなければならないときに実装した方法だ。

通常のSQLでは下記のようなクエリとなる。

select top 10 * from table order by NEWID()

これで10件のレコードをランダムに取得できる。これをLinqで記述すると下記になる。

partial class MyDataContext { 
     [Function(Name="NEWID", IsComposable=true)]  
     public Guid Random()  
     {
         throw new NotImplementedException();  
     } 
} 

var cust = (from row in db.Table
           order by db.Random() 
           select row).Take(10); 

元ネタはStack Overflowの下記のリンクだ。処理の詳細はSQL ServerのNEWID関数をRandomというDataContextの関数として実装し、前述のSQLをLinq上で再現している。

http://stackoverflow.com/questions/648196/random-row-from-linq-to-sql

上記リンク先でも指摘されているが、この方法は小さいテーブル用だ。多量のレコードに対して行うと大変な負荷をかけるので避けるようにしたい。大きいテーブル用のアプローチ方法としては全レコードに対してNEWIDするのではなく、ある程度小さくした集団に対して使用するべきだろう。上記リンク先にも大きいテーブル用のアプローチ方法が記載されいてるがここで紹介している内容と処理内容が異なるのでとくには解説しない。

ランダム、ランダムと書いていたらランダムがゲシュタルト崩壊してきた・・・。

2009年11月29日日曜日

LinqToSqlでの開発の注意点 その2

今回はLinqToSqlを使用してDBアクセスする際の注意点を解説する。

LinqToSqlで開発する場合、DBMLファイルを使用しDBMLで生成されたDataContextを使用するのが常道だろう。その際に、LinqToSqlを介しDBのテーブルにマッピングされたクラスを使用していると、ともするとメモリ上のデータを扱っているような錯覚を起こす場合がある。そのため経験の浅いプログラマはパフォーマンスを考慮せずに実装したりするのだが、実際にCo-opの学生が実装したコードを次に紹介しよう。

using( var db = new HogeDataContext())
{
 foreach( var user in db.Users.Where(x=> x.Hoge))
 {
  DoSomething(user);
  var userClients = user.UserClients.Select( x => new SimpleUserClient(){ Name=x.Name, Address=x.Address });
  DoSomethingWithClients(userClients);
 }
}

上記の例は実際に彼が実装していたのとは異なるのだが、やっているコンセプトは同じだ。もちろん上記のコードで期待した動作はできるのだがDBのパフォーマンス上よろしくない。

その理由は上記のコードをSqlに展開するとよく分かる。展開したSqlが下記になる。

select * from Users where Hoge=1
select Name, Address from UserClients where UserId=1
select Name, Address from UserClients where UserId=2
select Name, Address from UserClients where UserId=n




という風に2行目以降は1行目で取得したUser分だけ生成、実行されることになる。仮にUserを100人分取得した場合は100回DBにアクセスすることになる。SqlServer 2008とともにインストールされているSql Server Profilerを起動してから上記のコードを実行すると実際にSqlServer上で実行されているSqlが参照できるので良く分かると思う。

実際にはこのように書くべきだ。

using( var db = new HogeDataContext())
{
 foreach( var user in db.Users.Where(x=> x.Hoge).Select(x=> new { User=x, Clients=x.Clients.Select(x=>new SimpleUserClient(){ Name=x.Name, Address=x.Address }) })
 {
  DoSomething(user.User);
  DoSomethingWithClients(user.Clients);
 }
}

これでSqlの実行は一度だけになりDBのリソースを無駄に消費することはなくなった。最後に上記のコードから生成されるSqlを紹介しておこう。(実際に生成されたSqlの不要な部分を削ったりしてあります)

SELECT [t0].[UserId], [t0].[UserName], [t1].[Name], [t1].[Address](
SELECT COUNT(*)
FROM [dbo].[UserClients] AS [t2]
WHERE [t2].[UserId] = [t0].[UserId]
) AS [value]
FROM [dbo].[Users] AS [t0]
LEFT OUTER JOIN [dbo].[UserClients] AS [t1] ON [t1].[UserId] = [t0].[UserId]
ORDER BY [t0].[UserId], [t1].[Id]

Co-opの彼が担当した部分に今回紹介したようなコードが大量に散見されたために卒倒しそうになったので、今回紹介したようなコードを安易に大量生成するのはやめよう。

2009年10月26日月曜日

LinqToSqlでの開発の注意点 その1

LinqToSqlは.net framework3.5より導入されたORM(Object-relational mapping)だ。LinqToSql以前はORMといえばNHibernateなどが主流だったと思う。いまだにいくつかの点でNHibernateに譲るけれど(2nd Level Cacheやクエリーベースの取得など)、LinqToSqlのほうがよりシンプルな印象を持った。私見だがLinqToSqlを使用してのプロジェクト開発は、ORMを使用しないプロジェクトに比して段違いに開発スピードが速い。これから複数回に分けてLinqToSqlの注意点について解説していきたいと思う。

まず一番初めに直面するであろう問題はデータの更新時に訪れる。プロジェクトへdbmlファイルを追加し、必要なTableも追加し、データを取得、挿入までトントンと進むと思われるが、dbmlへ何も対処を施していないと挿入したデータを更新する際に下記の例外に直面する。

An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy
(注意:実際に発生する例外は若干違うかもしれない。今回は実地に試す環境がないので思い出しながら解説しています)

ソースコードはこのようなものを想定している。

void Update(Cilp clip)
{
 var db = new MyClipDataContext();
 db.Clips.Attach(clip, true);
 db.SubmitChanges();
}

この例外は並列処理時の上書き更新を防ぐための処理が初期状態でdbmlに施されているために発生する。これを避けるためには各テーブルにTimeStamp列を追加し、更新を下記のように変更する必要がある。

void Update(Cilp clip)
{
 var db = new MyClipDataContext();
 var dbClip = db.Clips.Single(x => x.Id==clip.Id);
 dbClip.Value = clip.Value;
 db.SubmitChanges();
}

これでひとまず例外が発生することは無くなった。ただ上記の例では上書き更新を防いでいるとはいえないだろう。実際には下記のように使うのが正しい。ちなみにこのソースコードは一つ目の例と同じだ。ただClipsテーブル上にTimeStamp列が追加されているものとし、さらに(ここが重要なのだが)引数clipは新規にnewしたものではなく、DBから取得したデータをSerialize, Deserializeしたものとする。

void Update(Cilp clip)
{
 var db = new MyClipDataContext();
 db.Clips.Attach(clip, true);
 db.SubmitChanges();
}

これにより、データ取得時のTimeStampを損なうことなく正確な上書き防止処理が期待できる。

ただ、上書き防止などを必要としない処理を行っている場合は、このような実装を行う必要はなく、下記のようにdbml上で各列ごとにUpdateCheckをNeverに設定すればよい。



これでTimeStampなどを気にすることなく更新処理が行えるようになる。ここで注意しなければならないのは、SQL Server上でテーブルを更新した場合、その更新をdbmlに反映するには再度テーブルをdbmlにD&Dする必要がある(信じられないことにVS2008にはそれ以外にSQL Server上の変更をdbmlへ反映する手段が無い)。このときにUpdateCheckをNeverに変更する処理を忘れずに行うようにしよう。