设为首页 收藏本站
查看: 1519|回复: 0

[经验分享] 用于MongoDB的WCF Data Services Provider的实现尝试

[复制链接]

尚未签到

发表于 2015-7-6 06:05:02 | 显示全部楼层 |阅读模式
  WCF Data Services以前称之为ADO.NET Data Services,在.NET 4.0中发布了第二个版本。通过WCF Data Services可以发布符合OData标准的数据接口,让各种各样的Client来消费这些数据,而且也可以通过一些谓词来操纵数据。
  关于WCF Data Services的相关介绍,可以参看:http://www.iyunv.com/shanyou/archive/2010/02/14/1668210.html
  作为一个数据暴露服务,当然可以支持后端各种数据源的展示,WCF Data Services默认支持实体框架(Entity Framework),同时提供了一些扩展接口以支持大家实现自己的Provider。
  我们首先要确定,是否需要自己实现Provider:
  
       
  • 如果你的数据可以用EF来访问,那么就不用实现   
  • 如果你的数据通过LINQtoSQL来访问,那么也有一个现成的Provider使用:ADO.NET Data Services IUpdateable implementation for Linq to Sql   
  • 如果你的数据是一些自定义实体对象,和一些对象集合构成,那么使用Reflection Provider(反射提供者),如果需要编辑数据,就同时实现IUpdatable。实际上LINQtoSQL的Provider就是这样实现的。   
  • 实现诸多接口,来完全进行自定义。
  对于大部分情况,Reflection Provider已经足够,比如对于对象数据库(dbo4),对于现在火热的NoSQL,也即Document(-based) DataBase都可以轻易的实现。
  通过阅读http://www.iyunv.com/tansm/archive/2010/06/10/1755250.html的译文,大家可以自动如何自定义Provider。
  最近几天我就尝试实现了MongoDB的WCF Data Services Provider。
  首先,我尝试了MongoDB的NoRM驱动,介绍见:http://www.iyunv.com/shanyou/archive/2010/06/04/1751734.html
  但是由于这个驱动,需要你在实体对象中显式添加一个ObjectId _id的属性,才能方便地进行保存,即方便调用Collection.Save方法;如果没有这个属性,就只能通过Update方法来更新数据,但是Update方法在Data Services中会遇到莫名其妙的问题,尝试多种技巧后,最终放弃这个驱动。
  然后转而使用mongodb-csharp driver,这个驱动也有诸多不完美,尤其对LINQ的支持没有NoRM好,不过最终几经周转还是实现了基本功能。要使用这个驱动来实现Provider的最简单方法就是此文说的方法:http://blog.dynamicprogrammer.com/2009/11/10/UsingMongoDBFromC.aspx,在实体类中内含一个Document InternalDocument { get; set; },不过这样对于实体类有很大的入侵性。我没有采用这种方式。我的最终实现方法,可以对实体类影响做到最小。
  比如:
  
public class Team
{
public int ID { get; set; }//使用代码约定来设置Document Id
   public string Name { get; set; }
public string Odds { get; set; }
}
public class Group
{
[MongoId]
public string Name { get; set; }//使用特性标记来设置Document Id
   public List Teams { get; set; }
}  整个Provider就是一个实现了IUpdatable接口的类,这个类有个静态方法来获得对应的数据库,如:


public class MongoDBDataContext : IUpdatable
{
private static IMongoDatabase _db;
public IMongoDatabase DB
{
get
{
if (_db == null)
{
Mongo mongo = new Mongo();
mongo.Connect();
_db = mongo.GetDatabase(this.DataBaseName);
}
return _db;
}
}
public virtual string DataBaseName
{
get { return "temp"; }
}
}  要实现这个Provider,只需继承这个MongoDBDataContext,并重写DataBaseName属性,以设置自己的数据库名称,然后在安装Data Services的规范添加IQueryable的属性,如:


public class WorldCupData : MongoDBDataContext
{
public override string DataBaseName
{
get
{
return "WorldCup";
}
}
public IQueryable Teams
{
get
{
return DB.GetCollection("Team").Linq();
}
}
public IQueryable Groups
{
get
{
return DB.GetCollection("Group").Linq();
}
}
}  实现过程中,最难的就是如何正确获取到Document,并正确保存。我采取了一种折衷的方案,通过反射来动态对Document和Entity进行转换,并把Entity的ID(或标记为MongoId)的属性的ToString设置为Document的_id,而_id也总是为字符串。核心代码如下:


private static BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty;
private static Document GetDocumentFromEntity(object entity,Type entityType)
{
if (entity == null) return null;
Document doc = new Document();
var properties = entityType.GetProperties(flags);
foreach (var pro in properties)
{
object proGetValue = pro.GetValue(entity, null);
if (pro.Name.ToLower() == "id" || pro.Name.ToLower() == "_id")//使用代码约定来设置Document Id
          doc.Id = proGetValue.ToString();
else if (Attribute.IsDefined(pro, typeof(MongoIdAttribute)))//使用特性标记来设置Document Id
          doc.Id = proGetValue.ToString();
doc[pro.Name] = proGetValue;
}
return doc;
}
private static object GetEntityFromDocument(Document doc, string fullTypeName)
{
if (doc == null) return null;
Type t = Type.GetType(fullTypeName, true);
object entity = Activator.CreateInstance(t);
var properties = t.GetProperties(flags);
foreach (var pro in properties)
{
if (doc.ContainsKey(pro.Name))
pro.SetValue(entity, doc[pro.Name], null);
}
return entity;
}
public object GetResource(IQueryable query, string fullTypeName)
{
object resource = null;
//query没有返回任何结果,只好用下面的取巧方式,感觉不太保险
//foreach (var item in query)
//{
//    resource = item;
//}

var exp = query.Expression as MethodCallExpression;
var arg1 = exp.Arguments[1] as UnaryExpression;
var expStr = arg1.Operand.ToString();//element => (element.ID == 0)

string[] fullTypeNameSplit = fullTypeName.Split('.');
var col= DB.GetCollection(fullTypeNameSplit[fullTypeNameSplit.Length - 1]);
//like to translate (element.ID == 0) to { ID : 0 }
  string expStrSplit1 = expStr.Split('>')[1].Trim();//(element.ID == 0)
  var valueStr = expStrSplit1.Split(new string[] { "==" }, StringSplitOptions.RemoveEmptyEntries)[1].Trim();//0)
  valueStr = valueStr.TrimEnd(')');
var selector = new Document { Id = valueStr };
var doc = col.FindOne(selector);
resource = GetEntityFromDocument(doc, fullTypeName);

// fullTypeName can be null for deletes
  if (resource != null && fullTypeName != null && resource.GetType().FullName != fullTypeName)
throw new Exception("Unexpected type for resource");
return resource;
}  注意在GetResource方法中,去使用了一点不太保险的小技巧。由于这个驱动的LINQ不完善,query不起作用,只好获取到表达式,并解析从条件值。更加我前面的约定,这个条件值就是Document的_id。
  
整个Provider的源代码和测试客户端,大家可以到http://code.msdn.microsoft.com/DSPforNoSQL下载。
  
  

  
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-83486-1-1.html 上篇帖子: 使用MongoDB存储访问者信息 下篇帖子: MongoDB分片
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表