LINQ と同時実効性制御 その3

前回の予告したように多層アプリケーション時に LINQ to SQL でコンフリクトをどのように捕捉するかを考えます。

まず LINQDataSource の部分をクラス化してあげます。
Web などのステートレスな処理を行う場合は LINQ to SQL の DataContext をリクエストの度に作成する必要があります。

検索と更新のコードはこんな感じでしょうか。

Public Class ComuplusUser

''' <summary>
''' ユーザの一覧を取得する。
''' </summary>
Public Function GetUsers() As List(Of User)
Using db = New こみゅぷらすDataContext()
db.ObjectTrackingEnabled = False
Return db.User.ToList()
End Using
End Function

''' <summary>
''' ユーザを更新する。
''' </summary>
Public Sub UpdateUser(ByVal editUser As User)
Using db = New こみゅぷらすDataContext()
' 更新対象のユーザを検索
Dim targetUser = New User With { _
.UserID = editUser.UserID, _
.UpdateDate = editUser.UpdateDate _
}
db.User.Attach(targetUser)

' 更新情報を格納
targetUser.UserName = editUser.UserName
targetUser.PostNo = editUser.PostNo
targetUser.PostSubNo = editUser.PostSubNo

' 更新を確定
db.SubmitChanges()
End Using
End Sub
End Class

更新時の流れは次のようになります。
1.更新対象のレコードを検索
2.更新情報を格納
3.更新を確定

更新対象のレコードを Attach する前に更新情報を格納しても無視されるようです。
こうは書けない

Public Sub UpdateUser(ByVal editUser As User)
Using db = New こみゅぷらすDataContext()
db.User.Attach(editUser)
db.SubmitChanges()
End Using
End Sub

ページの方では LINQDataSource を Object DataSource に変更して、ObjectDataSource にクラス情報を設定します。

<asp:GridView ID="GridView1" runat="server" 
AutoGenerateColumns="False"
DataSourceID="ObjectDataSource1"
DataKeyNames="UserID, UpdateDate">
<Columns>
<asp:commandfield ShowEditButton="True">
</asp:commandfield>
<asp:boundfield DataField="UserID"
HeaderText="UserID"
ReadOnly="True"
SortExpression="UserID">
</asp:boundfield>
<asp:boundfield DataField="UserName"
HeaderText="UserName"
SortExpression="UserName">
</asp:boundfield>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1"
runat="server"
DataObjectTypeName="User"
SelectMethod="GetUsers"
TypeName="ComuplusUser"
UpdateMethod="UpdateUser">
</asp:ObjectDataSource>

コンフリクトの検知は LINQDataSource と同様 ObjectDataSource の Updated イベントを捕捉し、 Exception を調べます。
ここで注意したいのが、ObjectDataSource の場合 ComuplusUserクラス側で ChangeConflictException が発生しても e.Exception に格納されているのは TargetInvocationException になってしまうので、サービス側で起きた例外を調べたいときは e.Exception.InnerException を調べる必要があるということです。

このあたりはライブラリ化した場合に .NET 3.5 以前から使われることを想定したらサービス側で LINQ の Exception を捕捉して当たり障りのないものに変更したほうがいいのかな?
それとも更新メソッドのインターフェイスを返却用のクラス経由でこんな風にして Updated 側で例外ではなく返却値を調査させた方がいいか。

サービス側

Public Class ServiceResult
Public Status As Boolean
Public Message As String
Public InnerException As Exception
End ClassPublic Function UpdateUser(ByVal editUser As User) As ServiceResult
WebPage側
Protected Sub ObjectDataSource1_Updated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ObjectDataSourceStatusEventArgs) Handles ObjectDataSource1.Updated

Dim result = DirectCast(e.ReturnValue, ServiceResult)
If Not result.Status Then
Label1.Text = Message
End If
End Sub