关键词:
WCF, Ado.net Entit, Ado.net Self-Trakcing Entity, silverlight, O/R mapping
概述
[例子代码在http://cid-56b433ad3d1871e3.office.live.com/self.aspx/.Public/OrderServices.zip]
Ado.net Entity是微软推出的O/R mapping框架,在Vs2008sp1时是1.0版本,现在和vs2010一起发布的是2.0版本,通过使用发现,2.0版本对于做分层的企业应用比较方便,下面具体的描述一下,供参考。
以一个C/S形式的例子说明,数据层直接使用Ado.net Entity建立的对象,在客户端和服务段共享这些结构,这样可以比较快速的构建起来系统,而且由于IDE支持可视化的修改,因此数据模型修改和变化非常的方便。
使用Ado.net Entity作为数据对象在客户端和服务端传递时,如果处理数据的修改和更新问题呢?如Order包含很多个对象Books,而客户端对于Books可以删除、增加、修改操作,这些操作完后,如何更新到服务端,此时可以使用Ado.net Self-Trakcing Entity Generator 自动生成模板, 这个是IDE提供的一个自动跟踪变化的框架,比较方便。
详细步骤如下:
1. 建立项目结构
Models:客户端和服务端共享的数据业务对象
Web:承载WCF的服务
Client: 测试WCF服务和各种接口
2. 添加模型:
OrdersModel.edmx
此处以一个简单的对象包含和继承关系为例说明
注意:
Ø 代码生成策略:注意此处设置为None,默认是自动有的,但这个自动生成的结构由于在客户端和服务端传递后处理更新比较麻烦,因此这样设置,在后续加一个自跟踪的对象处理
Ø 关联关系的设置:为了后续的独立查询方便,把主外键的关系设置一下
Ø 级联删除:如上图的Cascade,这样Order对象删除后,其关联的对象自动也自动删除[如果想自己删除,当然也是没有问题的]
3. 使模型能够自跟踪:
从模型生成数据库:建立数据库联接,执行生成出来的sql脚本即可
最终的模型工程如下:
4. 服务程序
首先引用Models项目和System.Datat.Entity.dll
Ø ObjectContext的生命期:由于默认的ObjectContext是延迟加载的,因此对于关联的对象,如果需要在客户端和服务器端传递,需要明确指定,否则序列化时会出现异常。至于这个的生命期可以参考blog.msdn上的文章。
public static void ContextLazy(Action<OrdersModelContainer> context)
{
using (OrdersModelContainer ctx = new OrdersModelContainer(WebConfigurationManager.ConnectionStrings["OrdersModelContainer"].ConnectionString))
{
ctx.ContextOptions.LazyLoadingEnabled = false;
context(ctx);
}
}
Ø 数据库的增删改查:可以使用linq和objectQuery的方法配合使用。
public class OrderService : IOrderService
{
private static readonly int PageSize = 50;
public void Create(Order order)
{
ObjectContextHelpers.Context(ctx =>
{
ctx.OrderSet.AddObject(order);
ctx.SaveChanges();
});
}
public List<Order> Read(string condition, int pageIndex, out int totals)
{
List<Order> ps = null;
int total = 0;
ObjectContextHelpers.ContextLazy(ctx =>
{
Func<Order, bool> f = (p) =>
{
return p.Name.Contains(condition);
};
total = ctx.OrderSet.Where(f).Count();
ps = ctx.OrderSet.Where(f).OrderByDescending(p => p.Id).Skip(pageIndex * PageSize).Take(PageSize).ToList<Order>();
});
totals = total;
return ps;
}
public Order ReadDetail(int Id)
{
Order order = null;
ObjectContextHelpers.ContextLazy(ctx =>
{
order = ctx.OrderSet.Include("Books").Where(o => o.Id == Id).First();
});
return order;
}
public void Update(Order order)
{
ObjectContextHelpers.Context(ctx =>
{
ctx.OrderSet.ApplyChanges(order);
ctx.SaveChanges();
});
}
public void Delete(int orderId)
{
Order ord = this.ReadDetail(orderId);
ObjectContextHelpers.Context(ctx =>
{
ord.StartTracking();
ord.MarkAsDeleted();
ctx.OrderSet.ApplyChanges(ord);
ctx.SaveChanges();
});
}
public void Clear()
{
ObjectContextHelpers.Context(ctx =>
{
string sql = @"delete from BookSet_IndustryBook;
delete from BookSet_ITBook;
delete from BookSet;
delete from OrderSet;";
ctx.ExecuteStoreCommand(sql);
ctx.SaveChanges();
});
}
}
5. 客户端程序
共享Models对象
private void button1_Click(object sender, EventArgs e)
{
using (OrderServiceClient svc = new OrderServiceClient())
{
Order o = new Order()
{
Name = "o1",
Books = new TrackableCollection<Book>()
{
new ITBook(){ Name="it", Index ="Computer"},
new IndustryBook(){ Name="it", Summary ="Industry Book"},
}
};
svc.Create(o);
}
}
private void button2_Click(object sender, EventArgs e)
{
using (OrderServiceClient svc = new OrderServiceClient())
{
int total;
List<Order> orders = svc.Read(out total, "o", 0);
Debug.WriteLine(orders.Count);
}
}
private void button3_Click(object sender, EventArgs e)
{
using (OrderServiceClient svc = new OrderServiceClient())
{
int total;
List<Order> orders = svc.Read(out total, "o", 0);
Debug.WriteLine(orders[0].Books.Count);
Order o = svc.ReadDetail(orders[0].Id);
Debug.WriteLine(o.Books.Count);
}
}
private void button4_Click(object sender, EventArgs e)
{
using (OrderServiceClient svc = new OrderServiceClient())
{
int total;
List<Order> orders = svc.Read(out total, "o", 0);
Debug.WriteLine(orders[0].Books.Count);
Order o = svc.ReadDetail(orders[0].Id);
o.Name += "--";
foreach (Book b in o.Books)
{
if (b is ITBook)
{
ITBook it = b as ITBook;
it.Index += "--";
}
else if (b is IndustryBook)
{
IndustryBook ind = b as IndustryBook;
ind.Summary += "--";
}
}
///此ä?处ä|也°2可¨¦以°?删¦?除yBooks的Ì?条¬?目?
o.Books.Add(new ITBook() { Name = "it2", Index = "Computer" });
svc.Update(o);
Order o1 = svc.ReadDetail(orders[0].Id);
Debug.Assert(o1.Name == o.Name && o.Books.Count == o.Books.Count);
}
}
private void button5_Click(object sender, EventArgs e)
{
using (OrderServiceClient svc = new OrderServiceClient())
{
int total;
List<Order> orders = svc.Read(out total, "o", 0);
Debug.WriteLine(orders[0].Books.Count);
svc.Delete(orders[0].Id);
}
}
private void SqlClr_Click(object sender, EventArgs e)
{
using (OrderServiceClient svc = new OrderServiceClient())
{
svc.Clear();
}
}
6. Silverlight项目的操作
如果在silverlight工程中使用以上的方法,由于Models引用的是.net4框架,那岂不是无法处理。通过Ado.net Self-Trakcing Entity的分析,由于这些类都是基于.net库独立实现的,通过试验确认:这些类可以在silverlight运行时环境下正常使用,具体步骤如下:
Ø 建立Models(sl)的silverlight项目,添加System.Runtime.Serialization.dll引用
Ø 在Models(sl)添加Models中的OrdersModel.tt下的所有文件(注意是Add Link,不要添加物理文件,这样Models更新后,在Models(sl)自动更新;如果模型新加入或删除了对象后,同样的方法处理一下即可)
SilverlightApplication应用引用Models(sl),添加WCF服务引用后,即可以享用实体框架和Ado.net Self-Trakcing Entity的便利之处