WCF实例 —— 自定义DataService数据模型(2)

在前一篇  DataService数据模型(1)  里介绍了如何实现一个内存数据集合的 DataService,接下来研究一下如何实现更新操作。

这里偷了个懒,直接让 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();
        }
    }
}

运行结果:
WCF实例 —— 自定义DataService数据模型(2)_第1张图片

你可能感兴趣的:(WCF实例 —— 自定义DataService数据模型(2))