这里偷了个懒,直接让 DataModel 实现 IDataServiceUpdateProvider 接口,而 IDataServiceUpdateProvider 实际继承于 IUpdatable 接口。为了实现数据的增加,修改,删除,主要实现下面几个方法:
//增加
object CreateResource(string containerName, string fullTypeName);
//修改
void SetValue(object targetResource, string propertyName, object propertyValue);
//删除
void DeleteResource(object targetResource);
//保存
void SaveChanges();
//回滚
void ClearChanges();
PS:实体关联关系的一系列操作,比较复杂,之后讨论。
另外需要提到的是 IDataServiceUpdateProvider 接口设计中支持批量特性,这就允许在一个事务中一次更新很多的资源。换句话说可以在 SaveChangeds() 方法调用前,调用 IDataServiceUpdateProvider.SetValue() 或者 CreateResource() 多次。这似乎看起来比较简单,但是对接口的实现却影响很大。在某个方法实现上,不可以立即将请求反映到数据源上,而是记录所发生的事情并在最后一次性的提交所有操作。
如果数据存放在数据库中,那么数据库系统会自动的在事务中记录所有的命令操作(比如:Entity Framewok)。但在这个例子中使用了内存对象存放数据,所以需要记录在 SaveChanges() 前所发生的一切。
因此在 DataModel 中,增加了 List<Action> _actions 来记录 SaveChanges() 之前的操作(请求)。
然后在 CreateResource, SetValue, DeleteResource 时都只是将操作暂存在 _actions 里。
SaveChanges 时 ForEach 所有暂存的 Action,一口气调用。 ClearChanges 则简单的把 _actions 清空。
public class DataModel : IDataServiceUpdateProvider
{
#region Populate Service Data
static IList<Order> _orders;
static IList<Item> _items;
List<Action> _actions = new List<Action>();
static DataModel()
{
_orders = new List<Order> {
new Order(){ OrderId=1, Customer = "Wendy Wu", Items = new List<Item>()},
new Order(){ OrderId=2, Customer = "John Gu", Items = new List<Item>()},
new Order(){ OrderId=3, Customer = "Balance Yao", Items = new List<Item>()}
};
_items = new List<Item> {
new Item(){ Product="Chang", Quantity = 4 },
new Item(){ Product="Aniseed Syrup", Quantity=5 },
new Item(){ Product="Toy", Quantity=7 },
new Item(){ Product="Car", Quantity=1 },
new Item(){ Product="Ball", Quantity=6 }
};
_orders[0].Items.Add(_items[0]);
_orders[1].Items.Add(_items[1]);
_orders[1].Items.Add(_items[2]);
_orders[2].Items.Add(_items[3]);
_orders[2].Items.Add(_items[4]);
}
#endregion
public IQueryable<Order> Orders
{
get { return _orders.AsQueryable<Order>(); }
}
public IQueryable<Item> Items
{
get { return _items.AsQueryable<Item>(); }
}
#region NotImplementedMethods
public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues)
{
throw new NotImplementedException();
}
public void 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);
}
public void 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);
}
public void SetReference(object targetResource, string propertyName, object propertyValue)
{
//((IUpdatable)this).SetValue(targetResource, propertyName, propertyValue);
}
#endregion
public object CreateResource(string containerName, string fullTypeName)
{
Type t = Type.GetType(fullTypeName, true);
object resource = Activator.CreateInstance(t);
_actions.Add(() =>
{
if (containerName == "Orders")
_orders.Add(resource as Order);
if (containerName == "Items")
_items.Add(resource as Item);
});
return resource;
}
public object 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;
}
public void DeleteResource(object targetResource)
{
if (targetResource.GetType() == typeof(Order))
{
_orders.Remove(targetResource as Order);
return;
}
if (targetResource.GetType() == typeof(Item))
{
_items.Remove(targetResource as Item);
return;
}
throw new NotSupportedException("Type not found");
}
public void SetValue(object targetResource, string propertyName, object propertyValue)
{
_actions.Add(() =>
{
targetResource
.GetType()
.GetProperties()
.Single(p => p.Name == propertyName)
.GetSetMethod()
.Invoke(targetResource, new[] { propertyValue });
});
}
public object GetValue(object targetResource, string propertyName)
{
var value = targetResource
.GetType()
.GetProperties()
.Single(p => p.Name == propertyName)
.GetGetMethod()
.Invoke(targetResource, new object[] { });
return value;
}
public object ResetResource(object resource)
{
return resource;
}
public object ResolveResource(object resource)
{
return resource;
}
public void SaveChanges()
{
_actions.ForEach(a => a());
// Handler Primary Keys
var newOrders = _orders.Where(o => o.OrderId == 0).ToList();
var maxOrderId = _orders.Max(o => o.OrderId);
foreach (var order in newOrders)
order.OrderId = ++maxOrderId;
}
public void ClearChanges()
{
_actions.Clear();
}
}
客户端调用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Services.Client;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var svcUri = new Uri("http://localhost:50480/WcfDataService1.svc");
var ctx = new DataSvc.DataModel(svcUri);
Console.WriteLine("[Orders]");
ctx.Orders.ToList().ForEach(o => Console.WriteLine("\t{0}", o.Customer));
Console.WriteLine("---------------");
Console.WriteLine();
var order = ctx.Orders.Where(o => o.OrderId == 2).First();
Console.WriteLine("1.查询OrderId=2的Order.Customer:");
Console.WriteLine("\t{0}", order.Customer);
// 括号里的"2"是对应Order主键而不是Index
var items = ctx.Execute<DataSvc.Item>(new Uri("/Orders(2)/Items", UriKind.Relative)).ToList();
Console.WriteLine("2.查询OrderId=2的Order.Items:");
items.ForEach(it => Console.WriteLine("\t{0}", it.Product));
Console.WriteLine("3.增加Order:");
Console.WriteLine("\t保存前的Count:" + ctx.Orders.Count());
var addOrder1 = new DataSvc.Order { Customer = "Customer" + Guid.NewGuid().ToString("N") };
ctx.AddToOrders(addOrder1);
var addOrder2 = new DataSvc.Order { Customer = "Customer" + Guid.NewGuid().ToString("N") };
ctx.AddToOrders(addOrder2);
ctx.SaveChanges();
Console.WriteLine("\t保存后的Count:" + ctx.Orders.Count());
Console.WriteLine("4.修改OrderId=2的Order.Customer:");
Console.WriteLine("\t保存前的Customer:" + order.Customer);
order.Customer = "Clark Li";
ctx.SaveChanges();
var query = ctx.Orders.Where(o => o.OrderId == 2).First();
Console.WriteLine("\t保存后的Customer:" + query.Customer);
Console.WriteLine("5.删除Order:");
Console.WriteLine("\t保存前的Count:" + ctx.Orders.Count());
var delOrder1 = ctx.Orders.Where(o => o.OrderId == addOrder1.OrderId).First();
ctx.DeleteObject(delOrder1);
ctx.SaveChanges();
Console.WriteLine("\t保存后的Count:" + ctx.Orders.Count());
Console.WriteLine();
Console.WriteLine("[Orders]");
ctx.Orders.ToList().ForEach(o => Console.WriteLine("\t{0}", o.Customer));
Console.WriteLine("---------------");
Console.Read();
}
}
}
运行结果:
