Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' of git://github.com/Amorano/NoRM

  • Loading branch information...
commit 4d3a274751565187ffa2e79ac717eb50f812c1bd 2 parents 01d6569 + ef444e6
@schotime schotime authored
View
26 NoRM - VS2010.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoRM", "NoRM\NoRM.csproj", "{297A83DB-DACE-4264-B28E-769CF165D4E4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoRM.Tests", "NoRM.Tests\NoRM.Tests.csproj", "{505E617D-1F40-40AF-BDF0-730EFBC5BA95}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {297A83DB-DACE-4264-B28E-769CF165D4E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {297A83DB-DACE-4264-B28E-769CF165D4E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {297A83DB-DACE-4264-B28E-769CF165D4E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {297A83DB-DACE-4264-B28E-769CF165D4E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {505E617D-1F40-40AF-BDF0-730EFBC5BA95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {505E617D-1F40-40AF-BDF0-730EFBC5BA95}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {505E617D-1F40-40AF-BDF0-730EFBC5BA95}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {505E617D-1F40-40AF-BDF0-730EFBC5BA95}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
View
43 NoRM.Tests/BSONSerializerTest.cs
@@ -9,7 +9,7 @@
namespace Norm.Tests
{
public class BSONSerializerTest
- {
+ {
[Fact]
public void DoesntSerializeIgnoredProperties()
{
@@ -200,11 +200,39 @@ public void SerializationOfGuidIsNotLossy()
[Fact]
public void SerializationOfInheritenceIsNotLossy()
{
- var obj1 = new ChildGeneralDTO {Pi = 3.14, IsOver9000 = true};
- var hydratedObj1 = BsonDeserializer.Deserialize<ChildGeneralDTO>(BsonSerializer.Serialize(obj1));
- Assert.Equal(obj1.Pi, hydratedObj1.Pi);
- Assert.Equal(obj1.IsOver9000, hydratedObj1.IsOver9000);
+ var obj1 = new SubClassedObject {Title = "Subclassed", ABool = true};
+ var hydratedObj1 = BsonDeserializer.Deserialize<SubClassedObject>(BsonSerializer.Serialize(obj1));
+ Assert.Equal(obj1.Title, hydratedObj1.Title);
+ Assert.Equal(obj1.ABool, hydratedObj1.ABool);
}
+
+ [Fact]
+ public void SerializationOfInheritenceIsNotLossy_EvenWhenWeAskForTheBaseType()
+ {
+ var obj1 = new SubClassedObject { Title = "The Title", ABool = true };
+ var hydratedObj1 = (SubClassedObject)BsonDeserializer.Deserialize<SuperClassObject>(BsonSerializer.Serialize(obj1));
+ Assert.Equal(obj1.Title, hydratedObj1.Title);
+ Assert.Equal(obj1.ABool, hydratedObj1.ABool);
+ }
+
+ [Fact]
+ public void SerializationOfInheritenceIsNotLossy_EvenWhenDiscriminatorIsOnAnInterface()
+ {
+ var obj1 = new InterfaceDiscriminatedClass();
+ var hydratedObj1 = BsonDeserializer.Deserialize<InterfaceDiscriminatedClass>(BsonSerializer.Serialize(obj1));
+
+ Assert.Equal(obj1.Id, hydratedObj1.Id);
+ }
+
+ [Fact]
+ public void SerializationOfInheritenceIsNotLossy_EvenWhenDiscriminatorIsOnAnInterfaceAndWeTryToDeserializeUsingTheInterface()
+ {
+ var obj1 = new InterfaceDiscriminatedClass();
+ var hydratedObj1 = (InterfaceDiscriminatedClass)BsonDeserializer.Deserialize<IDiscriminated>(BsonSerializer.Serialize(obj1));
+
+ Assert.Equal(obj1.Id, hydratedObj1.Id);
+ }
+
[Fact]
public void SerializationOfRegexIsNotLossy()
{
@@ -398,5 +426,10 @@ public void WillDeserializeObjectWithPrivateConstructor()
Assert.Equal(start.Name, end.Name);
}
+ [Fact]
+ public void WillSerializeObjectWithDiscriminator()
+ {
+
+ }
}
}
View
67 NoRM.Tests/DBTypeTests/DBRefTests.cs
@@ -1,39 +1,26 @@
-using System.Linq;
+using System.Collections.Generic;
+using System.Linq;
using Norm.BSON.DbTypes;
-using Norm.Configuration;
using Xunit;
-using Norm.Collections;
-
namespace Norm.Tests
{
- public class DBRefTests
+ public class DbRefTests
{
- public DBRefTests()
- {
- using (var session = new Session())
- {
- session.Drop<Product>();
- session.Drop<ProductReference>();
- }
- }
-
[Fact]
- public void DBRefMapsToOtherDocumentsByOid()
+ public void DbRefMapsToOtherDocumentsByOid()
{
- string databaseName = "NormTests";
+ const string databaseName = "NormTests";
var id = ObjectId.NewObjectId();
using (var session = new Session())
{
+ session.Drop<Product>();
+ session.Drop<ProductReference>();
+
session.Add(new Product { _id = id, Name = "RefProduct" });
- var productReference = new DBReference<Product>
- {
- Collection = MongoConfiguration.GetCollectionName(typeof(Product)),
- DatabaseName = databaseName,
- ID = id,
- };
+ var productReference = new DbReference<Product>(id);
session.Add(new ProductReference
{
@@ -49,5 +36,39 @@ public void DBRefMapsToOtherDocumentsByOid()
Assert.Equal(id.Value, product._id.Value);
}
+
+ [Fact]
+ public void DbRefMapsToOtherDocumentsByCustomId()
+ {
+ const string databaseName = "NormTests";
+ const string userId = "Tim Berners-Lee";
+ const string roleName = "Administrator";
+
+ using (var session = new Session())
+ {
+ session.Drop<User>();
+ session.Drop<Role>();
+
+ session.Add(new User
+ {
+ Id = userId,
+ EmailAddress = "user@domain.com"
+ });
+ session.Add(new Role
+ {
+ Id = roleName,
+ Users = new List<DbReference<User, string>>
+ {
+ new DbReference<User, string>(userId)
+ }
+ });
+ }
+
+ var server = Mongo.Create("mongodb://localhost/" + databaseName);
+ var role = server.GetCollection<Role>().Find().First();
+ var user = role.Users[0].Fetch(() => server);
+
+ Assert.Equal(userId, user.Id);
+ }
}
-}
+}
View
73 NoRM.Tests/LinqTests/LinqTests.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Norm.Linq;
using Xunit;
using Norm.Configuration;
@@ -642,8 +643,80 @@ public void CanQueryWithinEmbeddedArray()
session.Add(post1);
session.Add(post2);
var found = session.Posts.Where(p => p.Comments.Any(a => a.Text == "commentA")).SingleOrDefault();
+
Assert.Equal("Second", found.Title);
+ }
+ }
+
+ [Fact]
+ public void CanQueryAndReturnSubClassedObjects()
+ {
+ using (var session = new Session())
+ {
+ session.Drop<SuperClassObject>();
+
+ session.Add<SuperClassObject>(new SubClassedObject { Title = "Find This", ABool = true });
+ session.Add<SuperClassObject>(new SubClassedObject { Title = "Don't Find This", ABool = false });
+
+ var query = new MongoQuery<SuperClassObject>(session.Provider);
+
+ var dtos = query.Where(dto => dto.Title == "Find This").ToList();
+
+ Assert.Equal(1, dtos.Count);
+ Assert.Equal("Find This", dtos[0].Title);
+ }
+ }
+
+ [Fact]
+ public void CanQueryAndReturnSubClassedObjects_EvenWhenAddedBySubClass()
+ {
+ using (var session = new Session())
+ {
+ session.Drop<SuperClassObject>();
+
+ session.Add(new SubClassedObject());
+
+ var query = new MongoQuery<SuperClassObject>(session.Provider);
+
+ var dtos = query.ToList();
+
+ Assert.Equal(1, dtos.Count);
+ }
+ }
+
+ [Fact]
+ public void CanQueryAndReturnSubClassedObjects_EvenWhenQueriedBySubClass()
+ {
+ using (var session = new Session())
+ {
+ session.Drop<SuperClassObject>();
+
+ session.Add(new SubClassedObject());
+
+ var query = new MongoQuery<SubClassedObject>(session.Provider);
+
+ var dtos = query.ToList();
+
+ Assert.Equal(1, dtos.Count);
+ }
+ }
+
+ [Fact]
+ public void CanQueryAndReturnSubClassedObjects_EvenWhenQueriedByInterface()
+ {
+ using (var session = new Session())
+ {
+ session.Drop<IDiscriminated>();
+
+ var obj = new InterfaceDiscriminatedClass();
+ session.Add(obj);
+
+ var query = new MongoQuery<IDiscriminated>(session.Provider);
+
+ var dtos = query.ToList();
+ Assert.Equal(1, dtos.Count);
+ Assert.Equal(obj.Id, dtos.Single().Id);
}
}
}
View
27 NoRM.Tests/MongoConfigurationTests.cs
@@ -208,7 +208,32 @@ public void Are_Queries_Fully_Linqified()
}
}
+ [Fact]
+ public void Can_correctly_determine_collection_name()
+ {
+ var collectionName = MongoConfiguration.GetCollectionName(typeof(SuperClassObject));
+
+ Assert.Equal("SuperClassObject", collectionName);
+ }
+
+ [Fact]
+ public void Can_correctly_determine_collection_name_from_discriminated_sub_class()
+ {
+ var collectionName = MongoConfiguration.GetCollectionName(typeof(SubClassedObject));
+
+ Assert.Equal("SuperClassObject", collectionName);
+ }
+
+ [Fact]
+ public void Can_correctly_determine_collection_name_when_discriminator_is_on_an_interface()
+ {
+ var collectionName = MongoConfiguration.GetCollectionName(typeof(InterfaceDiscriminatedClass));
+
+ Assert.Equal("IDiscriminated", collectionName);
+ }
+
#region Test classes
+
internal class Shoppers : MongoQuery<Shopper>, IDisposable
{
private readonly MongoQueryProvider _provider;
@@ -310,6 +335,7 @@ public User2()
public string FirstName { get; set; }
public string LastName { get; set; }
}
+
public class ShopperMap : MongoConfigurationMap
{
public ShopperMap()
@@ -381,6 +407,7 @@ public CustomMap()
this.For<Product>(cfg => cfg.ForProperty(p => p.Name).UseAlias("productname"));
}
}
+
#endregion
}
}
View
1  NoRM.Tests/NoRM.Tests.csproj
@@ -102,6 +102,7 @@
<Compile Include="MongoOptimizationTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestClasses.cs" />
+ <Compile Include="TypeHelperTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NoRM\NoRM.csproj">
View
79 NoRM.Tests/TestClasses.cs
@@ -4,6 +4,7 @@
using System.Linq;
using System.Text.RegularExpressions;
using Norm.Attributes;
+using Norm.Configuration;
using Norm.Linq;
using Norm.Responses;
using Norm.BSON.DbTypes;
@@ -132,7 +133,12 @@ public T MapReduce<T>(string map, string reduce)
T result = default(T);
using (MapReduce mr = _provider.Server.CreateMapReduce())
{
- MapReduceResponse response = mr.Execute(new MapReduceOptions(typeof(T).Name) { Map = map, Reduce = reduce });
+ MapReduceResponse response =
+ mr.Execute(new MapReduceOptions(MongoConfiguration.GetCollectionName(typeof (T)))
+ {
+ Map = map,
+ Reduce = reduce
+ });
MongoCollection<MapReduceResult<T>> coll = response.GetCollection<MapReduceResult<T>>();
MapReduceResult<T> r = coll.Find().FirstOrDefault();
result = r.Value;
@@ -152,7 +158,7 @@ public void Update<T>(T item) where T : class, new()
public void Drop<T>()
{
- _provider.DB.DropCollection(typeof(T).Name);
+ _provider.DB.DropCollection(MongoConfiguration.GetCollectionName(typeof(T)));
}
public void CreateCappedCollection(string name)
@@ -218,7 +224,19 @@ public ProductReference()
public ObjectId Id { get; set; }
public string Name { get; set; }
- public DBReference<Product>[] ProductsOrdered { get; set; }
+ public DbReference<Product>[] ProductsOrdered { get; set; }
+ }
+
+ internal class User
+ {
+ public string Id{ get; set; }
+ public string EmailAddress{ get; set; }
+ }
+
+ internal class Role
+ {
+ public string Id{ get; set; }
+ public List<DbReference<User,string>> Users{ get; set; }
}
internal class Person
@@ -423,11 +441,61 @@ public class GeneralDTO
public int IgnoredProperty { get; set; }
}
-
public class ChildGeneralDTO : GeneralDTO
{
public bool IsOver9000 { get; set; }
}
+
+ [MongoDiscriminated]
+ public class SuperClassObject
+ {
+ public SuperClassObject()
+ {
+ Id = Guid.NewGuid();
+ }
+
+ [MongoIdentifier]
+ public Guid Id { get; protected set; }
+ public string Title { get; set; }
+ }
+
+ public class SubClassedObject : SuperClassObject
+ {
+ public bool ABool { get; set; }
+ }
+
+ [MongoDiscriminated]
+ internal interface IDiscriminated
+ {
+ [MongoIdentifier]
+ Guid Id { get; }
+ }
+
+ internal class InterfaceDiscriminatedClass : IDiscriminated
+ {
+ public InterfaceDiscriminatedClass()
+ {
+ Id = Guid.NewGuid();
+ }
+
+ public Guid Id { get; protected set; }
+ }
+
+ internal interface IDTOWithNonDefaultId
+ {
+ [MongoIdentifier]
+ Guid MyId { get; }
+ }
+
+ internal class DtoWithNonDefaultIdClass : IDTOWithNonDefaultId
+ {
+ public DtoWithNonDefaultIdClass()
+ {
+ MyId = Guid.NewGuid();
+ }
+
+ public Guid MyId { get; protected set; }
+ }
public class PrivateConstructor
{
@@ -444,8 +512,9 @@ public class Forum
{
public ObjectId Id{ get; set;}
}
+
public class Thread
{
- public ObjectId ForumId{ get; set;}
+ public ObjectId ForumId{ get; set; }
}
}
View
48 NoRM.Tests/TypeHelperTests.cs
@@ -0,0 +1,48 @@
+using Norm.BSON;
+using Xunit;
+
+namespace Norm.Tests
+{
+ public class TypeHelperTests
+ {
+ [Fact]
+ public void Can_get_discriminator_for_type()
+ {
+ var helper = TypeHelper.GetHelperForType(typeof(SuperClassObject));
+
+ Assert.Equal("Norm.Tests.SuperClassObject, NoRM.Tests", helper.GetTypeDiscriminator());
+ }
+
+ [Fact]
+ public void Can_get_discriminator_for_sub_type()
+ {
+ var helper = TypeHelper.GetHelperForType(typeof(SubClassedObject));
+
+ Assert.Equal("Norm.Tests.SubClassedObject, NoRM.Tests", helper.GetTypeDiscriminator());
+ }
+
+ [Fact]
+ public void Can_get_discriminator_when_discriminator_is_on_an_interface()
+ {
+ var helper = TypeHelper.GetHelperForType(typeof(InterfaceDiscriminatedClass));
+
+ Assert.Equal("Norm.Tests.InterfaceDiscriminatedClass, NoRM.Tests", helper.GetTypeDiscriminator());
+ }
+
+ [Fact]
+ public void Can_get_id_property_for_type()
+ {
+ var helper = TypeHelper.GetHelperForType(typeof(SuperClassObject));
+
+ Assert.NotNull(helper.FindIdProperty());
+ }
+
+ [Fact]
+ public void Can_get_id_property_for_type_when_the_identifier_attribute_is_declared_on_an_interface_and_property_does_not_have_default_name()
+ {
+ var helper = TypeHelper.GetHelperForType(typeof(DtoWithNonDefaultIdClass));
+
+ Assert.NotNull(helper.FindIdProperty());
+ }
+ }
+}
View
53 NoRM/Attributes/MongoDiscriminatedAttribute.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Linq;
+using Norm.BSON;
+
+namespace Norm
+{
+ /// <summary>
+ /// Flags a type as having a discriminator. Apply to a base type to enable multiple-inheritance.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
+ public class MongoDiscriminatedAttribute : Attribute
+ {
+ /// <summary>
+ /// Finds the sub-type or interface from the given type that declares itself as a discriminating base class
+ /// </summary>
+ /// <param name="type"></param>
+ /// <returns></returns>
+ public static Type GetDiscriminatingTypeFor(Type type)
+ {
+ var usingType = type;
+
+ while (usingType != typeof(object))
+ {
+ var discriminator = GetDiscriminatedAttribute(usingType);
+ if (discriminator != null)
+ return usingType;
+
+ usingType = usingType.BaseType;
+ }
+
+ foreach (var iface in type.GetInterfaces())
+ {
+ var discriminator = GetDiscriminatedAttribute(iface);
+
+ if (discriminator != null)
+ return iface;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Determines whether the type given directly declares itself as saving sub-types with a discriminator
+ /// </summary>
+ /// <param name="usingType"></param>
+ /// <returns></returns>
+ private static MongoDiscriminatedAttribute GetDiscriminatedAttribute(Type usingType)
+ {
+ return usingType.GetCustomAttributes(BsonHelper.MongoDiscriminatedAttribute, false)
+ .OfType<MongoDiscriminatedAttribute>()
+ .FirstOrDefault();
+ }
+ }
+}
View
23 NoRM/BSON/BSONSerializer.cs
@@ -4,6 +4,7 @@
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
+using Norm.BSON.DbTypes;
using Norm.Configuration;
namespace Norm.BSON
@@ -136,6 +137,20 @@ private void WriteFlyweight(Flyweight document)
}
/// <summary>
+ /// Checks to see if the object is a DbReference. If it is, we won't want to override $id to _id.
+ /// </summary>
+ /// <param name="type">The type of the object being serialized.</param>
+ /// <returns>True if the object is a DbReference, false otherwise.</returns>
+ private static bool IsDbReference(Type type)
+ {
+ return type.IsGenericType &&
+ (
+ type.GetGenericTypeDefinition() == typeof (DbReference<>) ||
+ type.GetGenericTypeDefinition() == typeof (DbReference<,>)
+ );
+ }
+
+ /// <summary>
/// Actually write the property bytes.
/// </summary>
/// <param name="document">The document.</param>
@@ -144,10 +159,16 @@ private void WriteObject(object document)
var typeHelper = TypeHelper.GetHelperForType(document.GetType());
var idProperty = typeHelper.FindIdProperty();
var documentType = document.GetType();
+ var discriminator = typeHelper.GetTypeDiscriminator();
+
+ if (String.IsNullOrEmpty(discriminator) == false)
+ {
+ SerializeMember("__type", discriminator);
+ }
foreach (var property in typeHelper.GetProperties())
{
- var name = property == idProperty
+ var name = property == idProperty && !IsDbReference(property.DeclaringType)
? "_id"
: MongoConfiguration.GetPropertyAlias(documentType, property.Name);
View
5 NoRM/BSON/BsonDeserializer.cs
@@ -233,8 +233,9 @@ private object ReadObject(Type type)
HandleError((string)DeserializeValue(typeof(string), BSONTypes.String));
}
- var property = (name == "_id") ? typeHelper.FindIdProperty() :
- typeHelper.FindProperty(name);
+ var property = (name == "_id" || name == "$id")
+ ? typeHelper.FindIdProperty()
+ : typeHelper.FindProperty(name);
if (property == null)
{
View
1  NoRM/BSON/BsonHelper.cs
@@ -12,6 +12,7 @@ internal class BsonHelper
public const int KEY_TERMINATOR_LENGTH = 1;
public static readonly DateTime EPOCH = new DateTime(1970, 1, 1).ToUniversalTime();
public static readonly Type MongoIdentifierAttribute = typeof(MongoIdentifierAttribute);
+ public static readonly Type MongoDiscriminatedAttribute = typeof(MongoDiscriminatedAttribute);
public static readonly HashSet<Type> ProhibittedTypes = new HashSet<Type>();
}
}
View
100 NoRM/BSON/DbTypes/DBReference.cs
@@ -7,22 +7,76 @@ namespace Norm.BSON.DbTypes
/// <summary>
/// A DB-pointer to another document.
/// </summary>
- public class DBReference<T> : ObjectId where T : class, new()
+ /// <typeparam name="T">The type of document being referenced.</typeparam>
+ public class DbReference<T> : DbReference<T, ObjectId> where T : class, new()
{
/// <summary>
- /// Initializes static members of the <see cref="DBReference"/> class.
+ /// Initializes static members of the <see cref="DbReference{T,TId}"/> class.
/// </summary>
- static DBReference()
+ static DbReference()
{
- MongoConfiguration.Initialize(c => c.For<DBReference<T>>(dbr =>
+ MongoConfiguration.Initialize(c => c.For<DbReference<T>>(dbr =>
+ {
+ dbr.ForProperty(d => d.Collection).UseAlias("$ref");
+ dbr.ForProperty(d => d.DatabaseName).UseAlias("$db");
+ dbr.ForProperty(d => d.Id).UseAlias("$id");
+ }));
+ }
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ public DbReference()
+ {
+ }
+
+ /// <summary>
+ /// Constructor for easier instantiation of db references.
+ /// </summary>
+ /// <param name="id">The id of the referenced document.</param>
+ public DbReference(ObjectId id) : base(id)
+ {
+ }
+ }
+
+ /// <summary>
+ /// A DB-pointer to another document.
+ /// </summary>
+ /// <typeparam name="T">The type of document being referenced.</typeparam>
+ /// <typeparam name="TId">The type of ID used by the document being referenced.</typeparam>
+ public class DbReference<T,TId> : ObjectId where T : class, new()
+ {
+ /// <summary>
+ /// Initializes static members of the <see cref="DbReference{T,TId}"/> class.
+ /// </summary>
+ static DbReference()
+ {
+ MongoConfiguration.Initialize(c => c.For<DbReference<T,TId>>(dbr =>
{
dbr.ForProperty(d => d.Collection).UseAlias("$ref");
dbr.ForProperty(d => d.DatabaseName).UseAlias("$db");
- dbr.ForProperty(d => d.ID).UseAlias("$id");
+ dbr.ForProperty(d => d.Id).UseAlias("$id");
}));
}
/// <summary>
+ /// Default constructor.
+ /// </summary>
+ public DbReference()
+ {
+ }
+
+ /// <summary>
+ /// Constructor for easier instantiation of db references.
+ /// </summary>
+ /// <param name="id">The id of the referenced document.</param>
+ public DbReference(TId id)
+ {
+ Id = id;
+ Collection = MongoConfiguration.GetCollectionName(typeof(T));
+ }
+
+ /// <summary>
/// The collection in while the referenced value lives.
/// </summary>
public string Collection { get; set; }
@@ -30,7 +84,7 @@ static DBReference()
/// <summary>
/// The ID of the referenced object.
/// </summary>
- public ObjectId ID { get; set; }
+ public TId Id { get; set; }
/// <summary>
/// The name of the db where the reference is stored.
@@ -51,24 +105,24 @@ static DBReference()
/// </returns>
public T Fetch(Func<MongoCollection<T>> referenceCollection)
{
- return referenceCollection().FindOne(new { _id = this.ID });
+ return referenceCollection().FindOne(new { _id = Id });
}
- /// <summary>
- /// Fetches the instance of type T in the collection referenced by the DBRef $id
- /// </summary>
- /// <typeparam name="T">
- /// Type referenced by DBRef
- /// </typeparam>
- /// <param name="server">
- /// A function that returns an instance of the Mongo server connection.
- /// </param>
- /// <returns>
- /// Referenced type T
- /// </returns>
- public T Fetch(Func<Mongo> server)
- {
- return Fetch(() => server().GetCollection<T>());
- }
+ /// <summary>
+ /// Fetches the instance of type T in the collection referenced by the DBRef $id
+ /// </summary>
+ /// <typeparam name="T">
+ /// Type referenced by DBRef
+ /// </typeparam>
+ /// <param name="server">
+ /// A function that returns an instance of the Mongo server connection.
+ /// </param>
+ /// <returns>
+ /// Referenced type T
+ /// </returns>
+ public T Fetch(Func<Mongo> server)
+ {
+ return Fetch(() => server().GetCollection<T>());
+ }
}
}
View
8 NoRM/BSON/MagicProperty.cs
@@ -18,15 +18,21 @@ public class MagicProperty
/// Initializes a new instance of the <see cref="MagicProperty"/> class.
/// </summary>
/// <param name="property">The property.</param>
- public MagicProperty(PropertyInfo property)
+ public MagicProperty(PropertyInfo property, Type declaringType)
{
_property = property;
_ignoreIfNull = property.GetCustomAttributes(_ignoredIfNullType, true).Length > 0;
Getter = CreateGetterMethod(property);
Setter = CreateSetterMethod(property);
+ DeclaringType = declaringType;
}
/// <summary>
+ /// The object that declared this property.
+ /// </summary>
+ public Type DeclaringType { get; private set; }
+
+ /// <summary>
/// Gets the property's underlying type.
/// </summary>
/// <value>The type.</value>
View
32 NoRM/BSON/TypeHelper.cs
@@ -18,7 +18,7 @@ public class TypeHelper
private static readonly Type _ignoredType = typeof(MongoIgnoreAttribute);
private readonly IDictionary<string, MagicProperty> _properties;
- //private readonly Type _type;
+ private readonly Type _type;
static TypeHelper()
{
@@ -41,10 +41,9 @@ static void TypeConfigurationChanged(Type theType)
/// <param name="type">The type.</param>
public TypeHelper(Type type)
{
- //_type = type;
- var properties =
- type.GetProperties(BindingFlags.Instance | BindingFlags.Public |
- BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
+ _type = type;
+ var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public |
+ BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
_properties = LoadMagicProperties(properties, IdProperty(properties));
}
@@ -147,7 +146,11 @@ public MagicProperty FindProperty(string name)
/// <returns></returns>
public MagicProperty FindIdProperty()
{
- return _properties.ContainsKey("$_id") ? _properties["$_id"] : null;
+ return _properties.ContainsKey("$_id")
+ ? _properties["$_id"]
+ : _properties.ContainsKey("$id")
+ ? _properties["$id"]
+ : null;
}
/// <summary>
@@ -201,10 +204,25 @@ private static PropertyInfo IdProperty(IEnumerable<PropertyInfo> properties)
var name = (property == idProperty && alias != "$id")
? "$_id"
: alias;
- magic.Add(name, new MagicProperty(property));
+ magic.Add(name, new MagicProperty(property, property.DeclaringType));
}
return magic;
}
+
+ /// <summary>
+ /// Determines the discriminator to use when serialising the type
+ /// </summary>
+ /// <returns></returns>
+ public string GetTypeDiscriminator()
+ {
+ var discriminatingType = MongoDiscriminatedAttribute.GetDiscriminatingTypeFor(_type);
+ if (discriminatingType != null)
+ {
+ return String.Join(",", _type.AssemblyQualifiedName.Split(','), 0, 2);
+ }
+
+ return null;
+ }
}
}
View
3  NoRM/Collections/MongoCollectionGeneric.cs
@@ -11,6 +11,7 @@
using Norm.Responses;
using TypeHelper=Norm.BSON.TypeHelper;
using Norm.Commands.Modifiers;
+
namespace Norm.Collections
{
/// <summary>
@@ -380,7 +381,7 @@ public IEnumerable<T> Find<U>(U template, int limit, int skip, string fullyQuali
/// <param name="orderBy">Passing null for this means it will be ignored.</param>
/// <param name="limit">The maximum number of documents to return.</param>
/// <param name="skip">The number to skip before returning any.</param>
- /// <param name="fullyqualifiedName">The collection from which to pull the documents.</param>
+ /// <param name="fullyQualifiedName">The collection from which to pull the documents.</param>
/// <returns></returns>
public IEnumerable<T> Find<U, S>(U template, S orderBy, int limit, int skip, string fullyQualifiedName)
{
View
2  NoRM/Configuration/IMongoConfigurationMap.cs
@@ -14,14 +14,12 @@ public interface IMongoConfigurationMap : IHideObjectMembers
/// <param name="typeConfiguration">The type configuration.</param>
void For<T>(Action<ITypeConfiguration<T>> typeConfiguration);
-
/// <summary>
/// Remove all configuration for the specified type.
/// </summary>
/// <remarks>Supports unit testing, use at your own risk!</remarks>
/// <typeparam name="T">The type for which to remove fluent mappings.</typeparam>
void RemoveFor<T>();
-
/// <summary>
/// Gets the name of the type's collection.
View
10 NoRM/Configuration/MongoConfiguration.cs
@@ -1,4 +1,6 @@
using System;
+using System.Linq;
+using Norm.BSON;
namespace Norm.Configuration
{
@@ -96,7 +98,13 @@ internal static string GetPropertyAlias(Type type, string propertyName)
/// <returns>Type's Collection name</returns>
internal static string GetCollectionName(Type type)
{
- return _configuration != null ? _configuration.GetConfigurationMap().GetCollectionName(type) : type.Name;
+ var discriminatingType = MongoDiscriminatedAttribute.GetDiscriminatingTypeFor(type);
+ if (discriminatingType != null)
+ return discriminatingType.Name;
+
+ return _configuration != null
+ ? _configuration.GetConfigurationMap().GetCollectionName(type)
+ : type.Name;
}
/// <summary>
View
17 NoRM/Configuration/MongoConfigurationMap.cs
@@ -2,6 +2,7 @@
using Norm.BSON;
using System.Collections.Generic;
using System.Reflection;
+using Norm.BSON.DbTypes;
namespace Norm.Configuration
{
@@ -62,6 +63,20 @@ private bool IsIdPropertyForType(Type type, String propertyName)
}
/// <summary>
+ /// Checks to see if the object is a DbReference. If it is, we won't want to override $id to _id.
+ /// </summary>
+ /// <param name="type">The type of the object being serialized.</param>
+ /// <returns>True if the object is a DbReference, false otherwise.</returns>
+ private static bool IsDbReference(Type type)
+ {
+ return type.IsGenericType &&
+ (
+ type.GetGenericTypeDefinition() == typeof(DbReference<>) ||
+ type.GetGenericTypeDefinition() == typeof(DbReference<,>)
+ );
+ }
+
+ /// <summary>
/// Gets the property alias for a type.
/// </summary>
/// <remarks>
@@ -79,7 +94,7 @@ public string GetPropertyAlias(Type type, string propertyName)
var map = MongoTypeConfiguration.PropertyMaps;
var retval = propertyName;//default to the original.
- if (this.IsIdPropertyForType(type, propertyName))
+ if (IsIdPropertyForType(type, propertyName) && !IsDbReference(type))
{
retval = "_id";
}
View
3  NoRM/Linq/MongoQuery.cs
@@ -53,7 +53,8 @@ public MongoQuery(MongoQueryProvider provider, string collectionName)
/// <param name="provider">
/// The provider.
/// </param>
- public MongoQuery(MongoQueryProvider provider) : this(provider, typeof(T).Name)
+ public MongoQuery(MongoQueryProvider provider)
+ : this(provider, MongoConfiguration.GetCollectionName(typeof(T)))
{
}
View
2  NoRM/Linq/MongoQueryProvider.cs
@@ -188,7 +188,7 @@ public IEnumerable<T> Execute<T>(Expression expression)
tranny.SortFly.ReverseKitchen();
result = collection.Find(fly, tranny.SortFly, tranny.Take, tranny.Skip, collection.FullyQualifiedName);
} else {
- result=collection.Find(fly, tranny.Take, tranny.Skip);
+ result = collection.Find(fly, tranny.Take, tranny.Skip);
}
return result;
View
4 NoRM/MapReduce/MapReduceOptionsGeneric.cs
@@ -1,4 +1,6 @@
+using Norm.Configuration;
+
namespace Norm
{
/// <summary>
@@ -12,7 +14,7 @@ public class MapReduceOptions<T> : MapReduceOptions
/// </summary>
public MapReduceOptions()
{
- CollectionName = typeof(T).Name;
+ CollectionName = MongoConfiguration.GetCollectionName(typeof(T));
}
}
}
View
3  NoRM/NoRM.csproj
@@ -72,6 +72,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Attributes\MongoDiscriminatedAttribute.cs" />
<Compile Include="Attributes\MongoIdentifierAttribute.cs" />
<Compile Include="Attributes\MongoIgnoreAttribute.cs" />
<Compile Include="Attributes\MongoIgnoreIfNullAttribute.cs" />
@@ -80,7 +81,7 @@
<Compile Include="BSON\Command.cs" />
<Compile Include="BSON\DbTypes\GridFile.cs" />
<Compile Include="BSON\DbTypes\ObjectId.cs" />
- <Compile Include="BSON\DbTypes\DBReference.cs" />
+ <Compile Include="BSON\DbTypes\DbReference.cs" />
<Compile Include="BSON\DbTypes\ObjectIdTypeConverter.cs" />
<Compile Include="BSON\DbTypes\ScopedCode.cs" />
<Compile Include="BSON\BsonSerializer.cs" />
View
4 NoRM/Protocol/Messages/QueryMessage.cs
@@ -1,4 +1,6 @@
+using Norm.Configuration;
+
namespace Norm.Protocol.Messages
{
/// <summary>
@@ -15,7 +17,7 @@ public class QueryMessage<T> : QueryMessage<T, T> where T : class, new()
/// <param name="connection">
/// The connection.
/// </param>
- public QueryMessage(IConnection connection) : base(connection, typeof(T).Name)
+ public QueryMessage(IConnection connection) : base(connection, MongoConfiguration.GetCollectionName(typeof(T)))
{
}
Please sign in to comment.
Something went wrong with that request. Please try again.