Как создать службу данных с помощью источника данных LINQ to SQL (службы WCF Data Services)
Службы WCF Data Services предоставляет данные сущности в виде службы данных. Поставщик отражения позволяет определить модель данных на основе любого класса, предоставляющего элементы, возвращаемые реализацией интерфейса IQueryable. Для выполнения обновлений данных в источнике данных эти классы должны также реализовать интерфейс IUpdatable. Дополнительные сведения см. в разделе Поставщики служб данных (службы WCF Data Services). В этом разделе показано, как создать классы LINQ to SQL, обращающиеся к образцу базы данных Northwind с помощью поставщика, а также как создать службу данных на основе этих классов.
Добавление в проект объектов классов LINQ to SQL
Из приложения на Visual Basic или C# в меню Проект щелкните пункт Добавить новый элемент.
Выберите шаблон LINQ to SQL Classes.
Измените имя на Northwind.dbml.
Нажмите кнопку Добавить.
Файл Northwind.dbml добавляется в проект и открывается реляционный конструктор объектов.
В обозревателе базы данных/сервера в окне Northwind разверните узел Таблицы и перетащите таблицу Customers в реляционный конструктор объектов.
При этом в области конструктора создается и отображается класс сущностей Customer.
Повторите шаг 6 для таблиц Orders, Order_Details и Products.
Щелкните правой кнопкой мыши DBML-файл, представляющий классы запросов LINQ to SQL, и выберите команду Просмотр кода.
При этом создается новая страница с выделенным кодом Northwind.cs, содержащая разделяемый класс, производный от класса DataContext, представляющего в данном случае контекст NorthwindDataContext.
Замените содержимое файла Northwind.cs следующим кодом. Этот код реализует поставщик отражения, расширяя контекст DataContext и классы данных, сформированные LINQ to SQL.
Imports System.ComponentModel Imports System.Collections Imports System.Linq Imports System.Reflection Imports System.Data.Linq Imports System.Data.Linq.Mapping Imports System.Data.Services Imports System.Data.Services.Common ' Define the key properties for the LINQ to SQL data classes. <DataServiceKeyAttribute("CustomerID")> _ Partial Public Class Customer End Class <DataServiceKeyAttribute("ProductID")> _ Partial Public Class Product End Class <DataServiceKeyAttribute("OrderID")> _ Partial Public Class Order End Class <DataServiceKeyAttribute("OrderID", "ProductID")> _ Partial Public Class Order_Detail End Class #Region "IUpdatable implementation" ' Define the IUpdatable implementation for LINQ to SQL. Partial Public Class NorthwindDataContext Implements IUpdatable ' Creates an object in the container. Function CreateResource(ByVal containerName As String, ByVal fullTypeName As String) _ As Object Implements IUpdatable.CreateResource Dim t = Type.GetType(fullTypeName, True) Dim table = GetTable(t) Dim resource = Activator.CreateInstance(t) table.InsertOnSubmit(resource) Return resource End Function ' Gets the object referenced by the resource. Function GetResource(ByVal query As IQueryable, ByVal fullTypeName As String) As Object _ Implements IUpdatable.GetResource Dim resource = query.Cast(Of Object)().SingleOrDefault() ' fullTypeName can be null for deletes If fullTypeName IsNot Nothing AndAlso resource.GetType().FullName <> fullTypeName Then Throw New ApplicationException("Unexpected type for this resource.") End If Return resource End Function ' Resets the value of the object to its default value. Function ResetResource(ByVal resource As Object) As Object _ Implements IUpdatable.ResetResource Dim t = resource.GetType() Dim table = Mapping.GetTable(t) Dim dummyResource = Activator.CreateInstance(t) For Each member In table.RowType.DataMembers If Not member.IsPrimaryKey AndAlso Not member.IsDeferred AndAlso _ Not member.IsAssociation AndAlso Not member.IsDbGenerated Then Dim defaultValue = member.MemberAccessor.GetBoxedValue(dummyResource) member.MemberAccessor.SetBoxedValue(resource, defaultValue) End If Next Return resource End Function ' Sets the value of the given property on the object. Sub SetValue(ByVal targetResource As Object, ByVal propertyName As String, _ ByVal propertyValue As Object) Implements IUpdatable.SetValue Dim table = Mapping.GetTable(targetResource.GetType()) Dim member = table.RowType.DataMembers.Single(Function(x) x.Name = propertyName) member.MemberAccessor.SetBoxedValue(targetResource, propertyValue) End Sub ' Gets the value of a property on an object. Function GetValue(ByVal targetResource As Object, ByVal propertyName As String) _ As Object Implements IUpdatable.GetValue Dim table = Mapping.GetTable(targetResource.GetType()) Dim member = _ table.RowType.DataMembers.Single(Function(x) x.Name = propertyName) Return member.MemberAccessor.GetBoxedValue(targetResource) End Function ' Sets the related object for a reference. Sub SetReference(ByVal targetResource As Object, ByVal propertyName As String, _ ByVal propertyValue As Object) Implements IUpdatable.SetReference CType(Me, IUpdatable).SetValue(targetResource, propertyName, propertyValue) End Sub ' Adds the object to the related objects collection. Sub AddReferenceToCollection(ByVal targetResource As Object, ByVal propertyName As String, _ ByVal resourceToBeAdded As Object) _ Implements IUpdatable.AddReferenceToCollection Dim pi = targetResource.GetType().GetProperty(propertyName) If pi Is Nothing Then Throw New Exception("Can't find property") End If Dim collection = CType(pi.GetValue(targetResource, Nothing), IList) collection.Add(resourceToBeAdded) End Sub ' Removes the object from the related objects collection. Sub RemoveReferenceFromCollection(ByVal targetResource As Object, ByVal propertyName As String, _ ByVal resourceToBeRemoved As Object) _ Implements IUpdatable.RemoveReferenceFromCollection Dim pi = targetResource.GetType().GetProperty(propertyName) If pi Is Nothing Then Throw New Exception("Can't find property") End If Dim collection = CType(pi.GetValue(targetResource, Nothing), IList) collection.Remove(resourceToBeRemoved) End Sub ' Deletes the resource. Sub DeleteResource(ByVal targetResource As Object) _ Implements IUpdatable.DeleteResource Dim table = GetTable(targetResource.GetType()) table.DeleteOnSubmit(targetResource) End Sub ' Saves all the pending changes. Sub SaveChanges() Implements IUpdatable.SaveChanges SubmitChanges() End Sub ' Returns the actual instance of the resource represented ' by the resource object. Function ResolveResource(ByVal resource As Object) As Object Implements IUpdatable.ResolveResource Return resource End Function ' Reverts all the pending changes. Sub ClearChanges() Implements IUpdatable.ClearChanges ' Raise an exception as there is no real way to do this with LINQ to SQL. ' Comment out the following line if you'd prefer a silent failure Throw New NotSupportedException() End Sub End Class #End Region
using System; using System.ComponentModel; using System.Collections; using System.Linq; using System.Reflection; using System.Data.Linq; using System.Data.Linq.Mapping; using System.Data.Services; using System.Data.Services.Common; namespace NorthwindService { // Define the key properties for the LINQ to SQL data classes. [DataServiceKeyAttribute("CustomerID")] public partial class Customer { } [DataServiceKeyAttribute("ProductID")] public partial class Product { } [DataServiceKeyAttribute("OrderID")] public partial class Order { } [DataServiceKeyAttribute("OrderID", "ProductID")] public partial class Order_Detail { } #region IUpdatable implementation // Define the IUpdatable implementation for LINQ to SQL. public partial class NorthwindDataContext : IUpdatable { // Creates an object in the container. object IUpdatable.CreateResource(string containerName, string fullTypeName) { Type t = Type.GetType(fullTypeName, true); ITable table = GetTable(t); object resource = Activator.CreateInstance(t); table.InsertOnSubmit(resource); return resource; } // Gets the object referenced by the resource. object IUpdatable.GetResource(IQueryable query, string fullTypeName) { object resource = query.Cast<object>().SingleOrDefault(); // fullTypeName can be null for deletes if (fullTypeName != null && resource.GetType().FullName != fullTypeName) throw new ApplicationException("Unexpected type for this resource."); return resource; } // Resets the value of the object to its default value. object IUpdatable.ResetResource(object resource) { Type t = resource.GetType(); MetaTable table = Mapping.GetTable(t); object dummyResource = Activator.CreateInstance(t); foreach (var member in table.RowType.DataMembers) { if (!member.IsPrimaryKey && !member.IsDeferred && !member.IsAssociation && !member.IsDbGenerated) { object defaultValue = member.MemberAccessor.GetBoxedValue(dummyResource); member.MemberAccessor.SetBoxedValue(ref resource, defaultValue); } } return resource; } // Sets the value of the given property on the object. void IUpdatable.SetValue(object targetResource, string propertyName, object propertyValue) { MetaTable table = Mapping.GetTable(targetResource.GetType()); MetaDataMember member = table.RowType.DataMembers.Single(x => x.Name == propertyName); member.MemberAccessor.SetBoxedValue(ref targetResource, propertyValue); } // Gets the value of a property on an object. object IUpdatable.GetValue(object targetResource, string propertyName) { MetaTable table = Mapping.GetTable(targetResource.GetType()); MetaDataMember member = table.RowType.DataMembers.Single(x => x.Name == propertyName); return member.MemberAccessor.GetBoxedValue(targetResource); } // Sets the related object for a reference. void IUpdatable.SetReference( object targetResource, string propertyName, object propertyValue) { ((IUpdatable)this).SetValue(targetResource, propertyName, propertyValue); } // Adds the object to the related objects collection. void IUpdatable.AddReferenceToCollection( object targetResource, string propertyName, object resourceToBeAdded) { PropertyInfo pi = targetResource.GetType().GetProperty(propertyName); if (pi == null) throw new Exception("Can't find property"); IList collection = (IList)pi.GetValue(targetResource, null); collection.Add(resourceToBeAdded); } // Removes the object from the related objects collection. void IUpdatable.RemoveReferenceFromCollection( object targetResource, string propertyName, object resourceToBeRemoved) { PropertyInfo pi = targetResource.GetType().GetProperty(propertyName); if (pi == null) throw new Exception("Can't find property"); IList collection = (IList)pi.GetValue(targetResource, null); collection.Remove(resourceToBeRemoved); } // Deletes the resource. void IUpdatable.DeleteResource(object targetResource) { ITable table = GetTable(targetResource.GetType()); table.DeleteOnSubmit(targetResource); } // Saves all the pending changes. void IUpdatable.SaveChanges() { SubmitChanges(); } // Returns the actual instance of the resource represented // by the resource object. object IUpdatable.ResolveResource(object resource) { return resource; } // Reverts all the pending changes. void IUpdatable.ClearChanges() { // Raise an exception as there is no real way to do this with LINQ to SQL. // Comment out the following line if you'd prefer a silent failure throw new NotSupportedException(); } #endregion } }
Создание службы данных с использованием модели данных на основе LINQ to SQL
В обозревателе решений щелкните правой кнопкой мыши имя проекта ASP.NET и выберите команду Добавить новый элемент.
В диалоговом окне Добавление нового элемента выберите Служба данных WCF.
Введите имя службы и нажмите кнопку ОК.
В Visual Studio для новой службы создаются файлы разметки и кодов XML. По умолчанию открывается окно редактора кода.
В коде службы данных замените комментарий
/* TODO: put your data source class name here */
в определении класса, задающего службу данных, типом контейнера сущностей модели данных, который в данном случае равен NorthwindDataContext.В коде службы данных замените код местозаполнителя в функции
InitializeService
следующим текстом.config.SetEntitySetAccessRule("Customers", EntitySetRights.ReadMultiple) config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead _ Or EntitySetRights.WriteMerge) config.SetEntitySetAccessRule("Order_Details", EntitySetRights.AllRead _ Or EntitySetRights.AllWrite) config.SetEntitySetAccessRule("Products", EntitySetRights.ReadMultiple)
config.SetEntitySetAccessRule("Customers", EntitySetRights.ReadMultiple); config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead | EntitySetRights.WriteMerge); config.SetEntitySetAccessRule("Order_Details", EntitySetRights.AllRead | EntitySetRights.AllWrite); config.SetEntitySetAccessRule("Products", EntitySetRights.ReadMultiple);
Это позволяет авторизованным клиентам осуществлять доступ к ресурсам трех указанных наборов сущностей.
Для проверки службы данных Northwind.svc с помощью веб-обозревателя следуйте инструкциям в разделе Доступ к службе из веб-обозревателя (краткое руководство по службам WCF Data Services).
См. также
Задачи
Как создать службу данных с использованием источника данных ADO.NET Entity Framework (службы WCF Data Services)
Как создать службу данных с помощью поставщика отражения (службы WCF Data Services)