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