• 用于MongoDB的WCF Data Services Provider的实现尝试


    WCF Data Services以前称之为ADO.NET Data Services,在.NET 4.0中发布了第二个版本。通过WCF Data Services可以发布符合OData标准的数据接口,让各种各样的Client来消费这些数据,而且也可以通过一些谓词来操纵数据。

    关于WCF Data Services的相关介绍,可以参看:http://www.cnblogs.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.cnblogs.com/tansm/archive/2010/06/10/1755250.html的译文,大家可以自动如何自定义Provider。

    最近几天我就尝试实现了MongoDB的WCF Data Services Provider。

    首先,我尝试了MongoDB的NoRM驱动,介绍见:http://www.cnblogs.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<Team> 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<Team> Teams
       {
           get
           {
               return DB.GetCollection<Team>("Team").Linq();
           }
       }
    
       public IQueryable<Group> Groups
       {
           get
           {
               return DB.GetCollection<Group>("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下载。

    分享到: 更多
  • 相关阅读:
    如何退出天擎
    git彻底删除或变更子模块
    湖北校园网PC端拨号算法逆向
    PPPoE中间人拦截以及校园网突破漫谈
    vscode打开django项目pylint提示has not "object" member
    从客户端取到浏览器返回的oauth凭证
    教程视频如何压制体积更小
    windows中的软链接硬链接等
    关于博客园和独立博客的一些打算
    拉勾抓职位简单小爬虫
  • 原文地址:https://www.cnblogs.com/redmoon/p/1770152.html
Copyright © 2020-2023  润新知