如何:使用 LINQ to SQL 数据源创建数据服务(WCF 数据服务)

WCF 数据服务 将实体数据作为数据服务公开。 使用反射提供程序可以定义基于以下任何类的数据模型:公开返回 IQueryable 实现的成员。 为了能够更新数据源中的数据,这些类还必须实现 IUpdatable 接口。 有关更多信息,请参见数据服务提供程序(WCF 数据服务)。 本主题演示如何创建通过使用反射提供程序来访问 Northwind 示例数据库的 LINQ to SQL 类,以及如何创建基于这些数据类的数据服务。

向项目中添加 LINQ to SQL 类

  1. 在 Visual Basic 或 C# 应用程序中,从**“项目”菜单中单击“添加新项”**。

  2. 单击**“LINQ to SQL 类”**模板。

  3. 将名称更改为 Northwind.dbml。

  4. 单击**“添加”**。

    此时,Northwind.dbml 文件将随即添加到项目中且对象关系设计器(O/R 设计器)将打开。

  5. 在**“服务器”/“数据库资源管理器”中,在“Northwind”下展开“表”**,再将 Customers 表拖到对象关系设计器(O/R 设计器)上。

    此时将随即创建一个 Customer 实体类并显示在设计图面上。

  6. OrdersOrder_DetailsProducts 表重复步骤 6。

  7. 右击表示该 LINQ to SQL 类的新 .dbml 文件,再单击**“查看代码”**。

    这将创建一个名为 Northwind.cs 的新代码隐藏页,其中包含从 DataContext 类(在此示例中为 NorthwindDataContext)继承的类的分部类定义。

  8. 用下列代码替换 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 的数据模型创建数据服务

  1. 在**“解决方案资源管理器”中,右击 ASP.NET 项目的名称,然后单击“添加新项”**。

  2. 在**“添加新项”对话框中,选择“WCF 数据服务”**。

  3. 为该服务提供一个名称,然后单击**“确定”**。

    Visual Studio 将为该新服务创建 XML 标记和代码文件。 默认情况下,代码编辑器窗口将打开。

  4. 在数据服务的代码中,用数据模型的实体容器的类型(在此示例中为 NorthwindDataContext)替换定义数据服务的类定义中的注释 /* TODO: put your data source class name here */

  5. 在数据服务的代码中,用下列代码替换 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);
    

    这使得授权客户端能够访问指定的三个实体集的资源。

  6. 若要使用 Web 浏览器测试 Northwind.svc 数据服务,请按照从 Web 浏览器访问服务(WCF 数据服务快速入门)主题中的说明操作。

另请参见

任务

如何:使用 ADO.NET 实体框架数据源创建数据服务(WCF 数据服务)
如何:使用反射提供程序创建数据服务(WCF 数据服务)

概念

数据服务提供程序(WCF 数据服务)