NoSQL用以MongoDB的WCF Data Services Provider的实现尝试

WCF Data
Services
先前称之为ADO.NET
Data 瑟维斯(Service)s,在.NET 4.0中发表了第二个本子。通过WCF Data
Service(Service)s可以揭穿符合OData标准的多寡接口,让各个各类的Client来花费这么些多少,而且也得以因而一些谓词来操纵数据。

有关WCF Data
Services的有关介绍,可以参考:http://www.cnblogs.com/shanyou/archive/2010/02/14/1668210.html

用作一个数码显露服务,当然可以支撑后端各样数据源的体现,WCF Data
瑟维斯(Service)(Service)s默认匡助实体框架(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 Service(Service)s Provider。

首先,我尝试了MongoDB的NoRM驱动,介绍见:http://www.cnblogs.com/shanyou/archive/2010/06/04/1751734.html

唯独出于这一个驱动,需要你在实体对象中显式添加一个ObjectId
_id的特性,才能便宜地开展封存,即方便调用Collection.Save方法;假如没有那个特性,就不得不通过Update方法来更新数据,不过Update方法在Data
瑟维斯(Service)(Service)s中会碰着莫名其妙的题目,尝试多种技能后,最后摒弃这些驱动。

接下来转而使用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
瑟维斯(Service)(Service)s的标准添加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;
}

NoSQL,留神在GetResource方法中,去行使了一点不太保险的小技巧。由于那么些驱动的LINQ不完美,query不起效能,只可以获取到表明式,并分析从规范值。更加自己面前的预约,那些标准值就是Document的_id。

全总Provider的源代码和测试客户端,大家可以到http://code.msdn.microsoft.com/DSPforNoSQL下载。

 

 

网站地图xml地图