ADO.NET Data Services 1.5(WCF Data Services) 的新增功能
1,支持服务端的 RowCount - 获取指定实体集合的成员数(只返回一个整型值,而不会返回实体集合)
2,支持服务端的分页 - 服务端可以返回分页后的数据,并且在其中还可以包含全部数据总数
3,支持服务端的 Select - 返回的结果只包括 Select 的字段
4,支持大数据传输 BLOB(binary large object)
5,支持自定义数据服务
1. RowCount (当然新玩意就要配置点新协议了 config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;)
rowCount只有在数据服务协议第二版本才能使用
$count - 返回 RowCount,即对应集合的成员数(只返回一个整型值,而不会返回实体集合)
http://localhost:12345/WcfDataService1.svc/EmployeeInfo/$count
$inlinecount=none - 只返回实体集合(分页后的数据)
http://localhost:12345/WcfDataService1.svc/EmployeeInfo/$inlinecount=none
$inlinecount=allpages - 在返回实体集合的基础上(分页后的数据),其中还会包括一个实体集合成员数(分页前的数据)的字段
http://localhost:9046/DataServices/Service/MyDataService.svc/Products?$inlinecount=allpages
2. 数据服务分页
// 设置服务器分页方式
config.SetEntitySetPageSize("EmployeeInfo", 5);
// 客户端则使用
http://localhost:12345/WcfDataService1.svc/EmployeeInfo?$skip=10
// 当然可以使用rowcount中的$inlinecount来获取数据
3. select字段及多参数
$select=[column1,column2,column3,...] - 返回的实体集合数据中只包括指定的字段
http://localhost:12345/WcfDataService1.svc/EmployeeInfo/?$select=EmployeeId,EmployeeName/&$inlinecount=allpages
4. BLOB大数据对象
ADO.NET Data Services 1.5 - 新增了对大数据传输 BLOB(binary large object)的支持
需要在概念模型(ConceptualModels)中的相关实体上增加属性“m:HasStream="true" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
// 注意 服务提供容器来自 using System.Data.Services.Providers 此命名空间之下
public class WcfDataService1 : DataService<HrmsDBEntities>,IServiceProvider
{
// 仅调用此方法一次以初始化涉及服务范围的策略。
public static void InitializeService(DataServiceConfiguration config)
{
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
// 设置规则以指明哪些实体集和服务操作是可见的、可更新的,等等。
// config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead); // 设置访问范围
// config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All); // 设置服务谓词访问
config.SetEntitySetAccessRule("EmployeeInfo", EntitySetRights.AllRead | EntitySetRights.AllWrite);
}
public object GetService(Type serviceType)
{
if (serviceType != typeof(IDataServiceStreamProvider))
{
return null;
}
// 调用服务的时候,如果指定了需要流式传输大数据对象,则通过我们自定义的流式文件传输对象去处理
return new StreamProvider();
}
}
// 数据服务流提供实现类,需要实现IDataServiceStreamProvider接口
public class StreamProvider : IDataServiceStreamProvider
{
public void DeleteStream(object entity, System.Data.Services.DataServiceOperationContext operationContext)
{
throw new NotImplementedException();
}
// 返回流
public Stream GetReadStream(object entity, string etag, bool? checkETagForEquality, DataServiceOperationContext operationContext)
{
if (entity as EmployeeInfo == null)
return null;
string employeeId = (entity as EmployeeInfo).EmployeeId;
using (var context = new HrmsDBEntities())
{
var employee = context.EmployeeInfo.First(p => p.EmployeeId == employeeId);
var stream = new MemoryStream(byte.Parse(employee.EmployeePicture));
return stream;
}
}
public Uri GetReadStreamUri(object entity, System.Data.Services.DataServiceOperationContext operationContext)
{
return null;
}
// 返回内容
public string GetStreamContentType(object entity, System.Data.Services.DataServiceOperationContext operationContext)
{
return "image/jpeg";
}
public string GetStreamETag(object entity, System.Data.Services.DataServiceOperationContext operationContext)
{
return null;
}
public System.IO.Stream GetWriteStream(object entity, string etag, bool? checkETagForEquality, System.Data.Services.DataServiceOperationContext operationContext)
{
throw new NotImplementedException();
}
public string ResolveType(string entitySetName, System.Data.Services.DataServiceOperationContext operationContext)
{
throw new NotImplementedException();
}
// 流缓冲大小
public int StreamBufferSize
{
get { return 64; }
}
}
// 获取EmployeeInfo表上主键为zx0000000007的值
调用获取 http://localhost:12345/WcfDataService1.svc/EmployeeInfo(zx0000000007)/$value
5. 自定义数据服务呢,就是将EF的实现过程我们手写一遍啦
要注意将数据上下文对象实现IQueryable接口,DataServiceKeyAttribute() 指定主键字段
EntityPropertyMapping() 实体属性到 ATOM 字段(可以理解为外键字段)的映射,以便生成一个友好格式的 Feed
// 设置规则以指明哪些实体集和服务操作是可见的、可更新的,等等。
// config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead); // 设置访问范围
// config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All); // 设置服务谓词访问
config.SetEntitySetAccessRule("EmployeeInfo", EntitySetRights.AllRead | EntitySetRights.AllWrite);
ps:就如开头说的,如何将CLR类型数据转换成具有平台互操互性数据,我们可以利用WCF DataService
WCF DataService巧妙利用了REST架构来实现对资源的访问,也利用自身SOAP特点将数据完美展现
到此为止我们的4.0新特性归结到此,其实4.0的东西远不止这些,这些只是一些常用的...
// 使用 WCF 数据服务 可以定义基于任意类的数据模型,前提是这些类作为实现 IQueryable
// 自定义数据展示
using System;
using System.Collections.Generic; using System.Data.Services; using System.Data.Services.Common; using System.Linq; namespace CustomDataServiceClient { [DataServiceKeyAttribute("OrderId")]
// LINQ TO SQL实体 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 { } // 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(); } }