Imports System.Runtime.Serialization
#Region "DataAccess"
Public Class DataManager
#Region "private fields"
'XML ファイルのルート要素
Private ReadOnly rootElement As XElement
'XML ファイルへのパス
Private ReadOnly dataPath As String
#End Region
#Region "Constructors"
Public Sub New(ByVal dataPath As String)
Me.dataPath = dataPath
'XML ファイルをロードする
rootElement = XElement.Load(dataPath)
End Sub
#End Region
#Region "public methods"
#Region "Read"
Public Function GetCategories() As IEnumerable(Of Category)
'XML ファイルから Category 要素をすべて取得し、Category オブジェクトを作成します。
Dim categories = rootElement.Descendants("Category").Select(Of Category)(Function(cElement) CreateCategory(cElement))
Return categories.ToList()
End Function
Public Function GetProducts() As IEnumerable(Of Product)
'XML ファイルから Product 要素をすべて取得し、Product オブジェクトを作成します。
Dim products = rootElement.Descendants("Product").Select(Of Product)(Function(pElement) CreateProduct(pElement))
Return products.ToList()
End Function
Public Function GetProductsByCategoryID(ByVal categoryID As Integer) As IEnumerable(Of Product)
'カテゴリ ID を指定して Product 要素を取得し、Product オブジェクトを作成します。
Dim products = rootElement.Descendants("Product").Where(Function(pElement) CInt(pElement.Element("CategoryID")) = categoryID).Select(Of Product)(Function(pElement) CreateProduct(pElement))
Return products.ToList()
End Function
Public Function GetCategoriesAndProducts() As IEnumerable(Of Category)
'Category および Product 要素を取得し、Category および Product オブジェクトを作成します。
Dim categories = rootElement.Descendants("Category").Select(Of Category)(Function(cElement) CreateCategoryAndProducts(cElement))
Return categories.ToList()
End Function
#End Region
#Region "update"
Public Function UpdateCategory(ByVal updatedCategory As Category) As System.Nullable(Of Integer)
'指定した Category オブジェクトに対応する Category 要素への参照を取得します。
Dim originalCategoryElement = rootElement.Descendants("Category").SingleOrDefault(Function(cElement) CInt(cElement.Element("CategoryID")) = updatedCategory.CategoryID)
'追加の場合は、新しい categoryID を返します。
Dim categoryID As System.Nullable(Of Integer) = Nothing
'元のカテゴリ XElement が見つかった場合は、変更を再生します
'そうでない場合は、変更を再生する前にそのカテゴリの XElement を新規作成します。
If originalCategoryElement IsNot Nothing Then
ReplayCategoryChanges(updatedCategory, originalCategoryElement)
Else
Dim categoryElement As XElement = Me.AddCategory()
categoryID = CInt(categoryElement.Element("CategoryID"))
ReplayCategoryChanges(updatedCategory, categoryElement)
End If
'ファイルに保存します
Me.rootElement.Save(dataPath)
Return categoryID
End Function
'実装は UpdateCategory と同じです
Public Function UpdateProduct(ByVal updatedProduct As Product) As System.Nullable(Of Integer)
Dim originalProduct = rootElement.Descendants("Product").SingleOrDefault(Function(pElement) CInt(pElement.Element("ProductID")) = updatedProduct.ProductID)
Dim productID As System.Nullable(Of Integer) = Nothing
If originalProduct IsNot Nothing Then
ReplayProductChanges(updatedProduct, originalProduct)
Else
Dim productElement As XElement = Me.AddProduct()
productID = CInt(productElement.Element("ProductID"))
ReplayProductChanges(updatedProduct, productElement)
End If
Me.rootElement.Save(dataPath)
Return productID
End Function
#End Region
#Region "delete"
Public Sub DeleteCategory(ByVal deletedCategory As Category, ByVal deleteProductsInCategory As Boolean)
'指定した Category オブジェクトに対応する Category 要素を削除します。
rootElement.Descendants("Category").Single(Function(cElement) CInt(cElement.Element("CategoryID")) = deletedCategory.CategoryID).Remove()
If deleteProductsInCategory Then
'指定した Category オブジェクトに含まれる Product 要素をすべて削除します。
rootElement.Descendants("Product").Where(Function(pElement) CInt(pElement.Element("CategoryID")) = deletedCategory.CategoryID).Remove()
End If
'XML ファイルに対する変更を保持します。
Me.rootElement.Save(dataPath)
End Sub
Public Sub DeleteProduct(ByVal deletedProduct As Product)
'指定した Product オブジェクトに対応する Product 要素を削除します。
rootElement.Descendants("Product").Single(Function(pElement) CInt(pElement.Element("ProductID")) = deletedProduct.ProductID).Remove()
Me.rootElement.Save(dataPath)
End Sub
#End Region
#End Region
#Region "private methods"
'Category オブジェクトを作成し、XElement のデータに基づいてそのプロパティを設定します
Private Function CreateCategory(ByVal categoryElement As XElement) As Category
Return New Category With {.CategoryID = CInt(categoryElement.Element("CategoryID")), .CategoryName = categoryElement.Element("CategoryName").Value, .Description = categoryElement.Element("Description").Value, .Products = New List(Of Product)()}
End Function
'Product オブジェクトを作成し、XElement のデータに基づいてそのプロパティを設定します
Private Function CreateProduct(ByVal productElement As XElement) As Product
Return New Product With {.ProductID = CInt(productElement.Element("ProductID")), .CategoryID = CInt(productElement.Element("CategoryID")), .ProductName = productElement.Element("ProductName").Value, .QuantityPerUnit = productElement.Element("QuantityPerUnit").Value, .UnitPrice = CDec(productElement.Element("UnitPrice")), .UnitsInStock = CInt(productElement.Element("UnitsInStock")), .UnitsOnOrder = CInt(productElement.Element("UnitsOnOrder")), .ReorderLevel = CInt(productElement.Element("ReorderLevel")), .Discontinued = CBool(productElement.Element("Discontinued"))}
End Function
'Category オブジェクトと、そのカテゴリに含まれる Product オブジェクトを作成します。
Private Function CreateCategoryAndProducts(ByVal categoryElement As XElement) As Category
Dim category As Category = CreateCategory(categoryElement)
category.Products = rootElement.Descendants("Product").Where(Function(product) CInt(categoryElement.Element("CategoryID")) = CInt(Product.Element("CategoryID"))).Select(Of Product)(Function(product) CreateProduct(product)).ToList()
Return category
End Function
'Category 要素を新規作成して Categories ノードに追加します。
Private Function AddCategory() As XElement
'Categories XElement を取得します
Dim categoriesElement = rootElement.Descendants("Categories").Single()
'このカテゴリの XElement を新規作成します
Dim categoryElement As New XElement("Category", New XElement("CategoryID"), New XElement("CategoryName"), New XElement("Description"))
'CategoryID の最大値に基づいて CategoryID をインクリメントします。
Dim categoryIDIncrementCounter As Integer = Me.GetMaxCategoryID()
categoryElement.SetElementValue("CategoryID", categoryIDIncrementCounter + 1)
'カテゴリ XElement を Categories XElement の子として追加します
categoriesElement.Add(categoryElement)
Return categoryElement
End Function
'実装は AddCategory メソッドと同じです。
Private Function AddProduct() As XElement
Dim productsElement = rootElement.Descendants("Products").Single()
'Product の XElement を新規作成します。
Dim productElement As New XElement("Product", New XElement("ProductID"), New XElement("ProductName"), New XElement("CategoryID"), New XElement("QuantityPerUnit"), New XElement("UnitPrice"), _
New XElement("UnitsInStock"), New XElement("UnitsOnOrder"), New XElement("ReorderLevel"), New XElement("Discontinued"))
'ProductID の最大値に基づいて ProductID をインクリメントします。
Dim productIDIncrementCounter As Integer = Me.GetMaxProductID()
productElement.SetElementValue("ProductID", productIDIncrementCounter + 1)
'XElement を Products ノードに追加します。
productsElement.Add(productElement)
Return productElement
End Function
'XML ファイルから CategoryID の最大値を取得するヘルパー メソッド
Private Function GetMaxCategoryID() As Integer
Return rootElement.Descendants("Category").Max(Function(categoryElement) CInt(categoryElement.Element("CategoryID")))
End Function
'XML ファイルから ProductID の最大値を取得するヘルパー メソッド
Private Function GetMaxProductID() As Integer
Return rootElement.Descendants("Product").Max(Function(productElement) CType(productElement.Element("ProductID"), Integer))
End Function
'更新した Category オブジェクトに基づいてカテゴリ XElement 値を更新します。
Private Sub ReplayCategoryChanges(ByVal updatedCategory As Category, ByVal originalCategoryElement As XElement)
originalCategoryElement.SetElementValue("CategoryName", If(updatedCategory.CategoryName Is Nothing, String.Empty, updatedCategory.CategoryName))
originalCategoryElement.SetElementValue("Description", If(updatedCategory.Description Is Nothing, String.Empty, updatedCategory.Description))
End Sub
'更新した Category オブジェクトに基づいて製品 XElement 値を更新します。
Private Sub ReplayProductChanges(ByVal updatedProduct As Product, ByVal originalProductElement As XElement)
originalProductElement.SetElementValue("CategoryID", updatedProduct.CategoryID)
originalProductElement.SetElementValue("Discontinued", updatedProduct.Discontinued)
originalProductElement.SetElementValue("ProductName", If(updatedProduct.ProductName Is Nothing, String.Empty, updatedProduct.ProductName))
originalProductElement.SetElementValue("QuantityPerUnit", If(updatedProduct.QuantityPerUnit Is Nothing, String.Empty, updatedProduct.QuantityPerUnit))
originalProductElement.SetElementValue("ReorderLevel", updatedProduct.ReorderLevel)
originalProductElement.SetElementValue("UnitPrice", updatedProduct.UnitPrice)
originalProductElement.SetElementValue("UnitsInStock", updatedProduct.UnitsInStock)
originalProductElement.SetElementValue("UnitsOnOrder", updatedProduct.UnitsOnOrder)
End Sub
#End Region
End Class
#End Region
#Region "Category class"
<DataContract()> _
Public Class Category
Private _CategoryID As Integer
<DataMember()> _
Public Property CategoryID() As Integer
Get
Return _CategoryID
End Get
Set(ByVal value As Integer)
_CategoryID = value
End Set
End Property
Private _CategoryName As String
<DataMember()> _
Public Property CategoryName() As String
Get
Return _CategoryName
End Get
Set(ByVal value As String)
_CategoryName = value
End Set
End Property
Private _Description As String
<DataMember()> _
Public Property Description() As String
Get
Return _Description
End Get
Set(ByVal value As String)
_Description = value
End Set
End Property
Private _Products As IEnumerable(Of Product)
<DataMember()> _
Public Property Products() As IEnumerable(Of Product)
Get
Return _Products
End Get
Set(ByVal value As IEnumerable(Of Product))
_Products = value
End Set
End Property
End Class
#End Region
#Region "Product class"
<DataContract()> _
Public Class Product
Private _ProductID As Integer
<DataMember()> _
Public Property ProductID() As Integer
Get
Return _ProductID
End Get
Set(ByVal value As Integer)
_ProductID = value
End Set
End Property
Private _ProductName As String
<DataMember()> _
Public Property ProductName() As String
Get
Return _ProductName
End Get
Set(ByVal value As String)
_ProductName = value
End Set
End Property
Private _CategoryID As Integer
<DataMember()> _
Public Property CategoryID() As Integer
Get
Return _CategoryID
End Get
Set(ByVal value As Integer)
_CategoryID = value
End Set
End Property
Private _QuantityPerUnit As String
<DataMember()> _
Public Property QuantityPerUnit() As String
Get
Return _QuantityPerUnit
End Get
Set(ByVal value As String)
_QuantityPerUnit = value
End Set
End Property
Private _UnitPrice As Decimal
<DataMember()> _
Public Property UnitPrice() As Decimal
Get
Return _UnitPrice
End Get
Set(ByVal value As Decimal)
_UnitPrice = value
End Set
End Property
Private _UnitsInStock As Integer
<DataMember()> _
Public Property UnitsInStock() As Integer
Get
Return _UnitsInStock
End Get
Set(ByVal value As Integer)
_UnitsInStock = value
End Set
End Property
Private _UnitsOnOrder As Integer
<DataMember()> _
Public Property UnitsOnOrder() As Integer
Get
Return _UnitsOnOrder
End Get
Set(ByVal value As Integer)
_UnitsOnOrder = value
End Set
End Property
Private _ReorderLevel As Integer
<DataMember()> _
Public Property ReorderLevel() As Integer
Get
Return _ReorderLevel
End Get
Set(ByVal value As Integer)
_ReorderLevel = value
End Set
End Property
Private _Discontinued As Boolean
<DataMember()> _
Public Property Discontinued() As Boolean
Get
Return _Discontinued
End Get
Set(ByVal value As Boolean)
_Discontinued = value
End Set
End Property
End Class
#End Region