diff --git a/source/MongoDB.Tests/IntegrationTests/Inheritance/TestInheritanceWithAbstractBaseClass.cs b/source/MongoDB.Tests/IntegrationTests/Inheritance/TestInheritanceWithAbstractBaseClass.cs index ae088fb7..c3fd8b18 100644 --- a/source/MongoDB.Tests/IntegrationTests/Inheritance/TestInheritanceWithAbstractBaseClass.cs +++ b/source/MongoDB.Tests/IntegrationTests/Inheritance/TestInheritanceWithAbstractBaseClass.cs @@ -160,11 +160,45 @@ public void Should_support_projections_with_base_class_collections_with_linq() Assert.AreEqual(2, animals.Count); Assert.AreEqual(19, animals[0].Age); Assert.AreEqual("Bob", animals[0].Name); - Assert.IsNull(animals[0].Name); Assert.AreEqual(20, animals[1].Age); Assert.AreEqual("Jim", animals[1].Name); } + [Test] + public void Should_support_projections_with_concrete_class_collection() + { + var animalCollection = DB.GetCollection(); + animalCollection.Save(new Bear() { Age = 20, Name = "Jim" }); + animalCollection.Save(new Tiger() { Age = 19, Name = "Bob" }); + + var catCollection = DB.GetCollection(); + + var cats = catCollection.FindAll().Fields(new { Age = true }).Sort("Age", IndexOrder.Ascending).Documents.ToList(); + + Assert.AreEqual(1, cats.Count); + Assert.IsInstanceOfType(typeof(Tiger), cats[0]); + Assert.AreEqual(19, cats[0].Age); + Assert.IsNull(cats[0].Name); + } + + [Test] + public void Should_support_projections_with_concrete_class_collections_with_linq() + { + var animalCollection = DB.GetCollection(); + animalCollection.Save(new Bear() { Age = 20, Name = "Jim" }); + animalCollection.Save(new Tiger() { Age = 19, Name = "Bob" }); + + var catCollection = DB.GetCollection(); + + var cats = (from a in catCollection.Linq() + orderby a.Age ascending + select new { a.Name, a.Age }).ToList(); + + Assert.AreEqual(1, cats.Count); + Assert.AreEqual(19, cats[0].Age); + Assert.AreEqual("Bob", cats[0].Name); + } + [Test] public void Should_fetch_with_concrete_class_collection() { diff --git a/source/MongoDB.Tests/IntegrationTests/Inheritance/TestInheritanceWithConcreteBaseClass.cs b/source/MongoDB.Tests/IntegrationTests/Inheritance/TestInheritanceWithConcreteBaseClass.cs index 44f87f4e..953497df 100644 --- a/source/MongoDB.Tests/IntegrationTests/Inheritance/TestInheritanceWithConcreteBaseClass.cs +++ b/source/MongoDB.Tests/IntegrationTests/Inheritance/TestInheritanceWithConcreteBaseClass.cs @@ -14,6 +14,8 @@ class Animal public Oid Id { get; set; } public int Age { get; set; } + + public string Name { get; set; } } class Bear : Animal @@ -156,6 +158,77 @@ public void Should_fetch_with_inherited_class_collection_through_linq() Assert.AreEqual(19, animals[0].Age); } + [Test] + public void Should_support_projections_with_base_class_collection() + { + var animalCollection = DB.GetCollection(); + animalCollection.Save(new Animal() { Age = 20, Name = "Jim" }); + animalCollection.Save(new Tiger() { Age = 19, Name = "Bob" }); + + var animals = animalCollection.FindAll().Fields(new { Age = true }).Sort("Age", IndexOrder.Ascending).Documents.ToList(); + + Assert.AreEqual(2, animals.Count); + Assert.IsInstanceOfType(typeof(Tiger), animals[0]); + Assert.AreEqual(19, animals[0].Age); + Assert.IsNull(animals[0].Name); + Assert.IsInstanceOfType(typeof(Animal), animals[1]); + Assert.AreEqual(20, animals[1].Age); + Assert.IsNull(animals[1].Name); + } + + [Test] + public void Should_support_projections_with_base_class_collections_with_linq() + { + var animalCollection = DB.GetCollection(); + animalCollection.Save(new Animal() { Age = 20, Name = "Jim" }); + animalCollection.Save(new Tiger() { Age = 19, Name = "Bob" }); + + var animals = (from a in animalCollection.Linq() + orderby a.Age ascending + select new { a.Name, a.Age }).ToList(); + + Assert.AreEqual(2, animals.Count); + Assert.AreEqual(19, animals[0].Age); + Assert.AreEqual("Bob", animals[0].Name); + Assert.AreEqual(20, animals[1].Age); + Assert.AreEqual("Jim", animals[1].Name); + } + + [Test] + public void Should_support_projections_with_inherited_class_collection() + { + var animalCollection = DB.GetCollection(); + animalCollection.Save(new Animal() { Age = 20, Name = "Jim" }); + animalCollection.Save(new Tiger() { Age = 19, Name = "Bob" }); + + var catCollection = DB.GetCollection(); + + var cats = catCollection.FindAll().Fields(new { Age = true }).Sort("Age", IndexOrder.Ascending).Documents.ToList(); + + Assert.AreEqual(1, cats.Count); + Assert.IsInstanceOfType(typeof(Tiger), cats[0]); + Assert.AreEqual(19, cats[0].Age); + Assert.IsNull(cats[0].Name); + } + + [Test] + public void Should_support_projections_with_inherited_class_collections_with_linq() + { + var animalCollection = DB.GetCollection(); + animalCollection.Save(new Animal() { Age = 20, Name = "Jim" }); + animalCollection.Save(new Tiger() { Age = 19, Name = "Bob" }); + + var catCollection = DB.GetCollection(); + + var cats = (from a in catCollection.Linq() + orderby a.Age ascending + select new { a.Name, a.Age }).ToList(); + + Assert.AreEqual(1, cats.Count); + Assert.AreEqual(19, cats[0].Age); + Assert.AreEqual("Bob", cats[0].Name); + } + [Test] public void Should_get_correct_count_with_base_class_collection() { diff --git a/source/MongoDB/Connections/Connection.cs b/source/MongoDB/Connections/Connection.cs index 7ecfcd7a..c0d80a07 100644 --- a/source/MongoDB/Connections/Connection.cs +++ b/source/MongoDB/Connections/Connection.cs @@ -8,7 +8,7 @@ using MongoDB.Serialization; using MongoDB.Util; -namespace MongoDB.Connections +namespace MongoDB.Connections { /// /// Connection is a managment unit which uses a RawConnection from connection pool @@ -18,7 +18,7 @@ namespace MongoDB.Connections /// by a new fresh connection. /// /// - internal class Connection : IDisposable + internal class Connection : IDisposable { private readonly IConnectionFactory _factory; private RawConnection _connection; diff --git a/source/MongoDB/Cursor_1.cs b/source/MongoDB/Cursor_1.cs index d90ab3f4..a6459192 100644 --- a/source/MongoDB/Cursor_1.cs +++ b/source/MongoDB/Cursor_1.cs @@ -1,27 +1,27 @@ -using System; -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; -using MongoDB.Connections; -using MongoDB.Protocol; +using MongoDB.Connections; +using MongoDB.Protocol; using MongoDB.Serialization; using System.Linq; -namespace MongoDB +namespace MongoDB { /// /// /// /// - public class Cursor : ICursor where T : class + public class Cursor : ICursor where T : class { private readonly Connection _connection; private readonly string _databaseName; private readonly Document _specOpts = new Document(); - private object _spec; - private object _fields; - private int _limit; - private QueryOptions _options; - private ReplyMessage _reply; + private object _spec; + private object _fields; + private int _limit; + private QueryOptions _options; + private ReplyMessage _reply; private int _skip; private bool _keepCursor; private readonly ISerializationFactory _serializationFactory; @@ -33,14 +33,14 @@ public class Cursor : ICursor where T : class /// The conn. /// Name of the database. /// Name of the collection. - internal Cursor(ISerializationFactory serializationFactory, Connection connection, string databaseName, string collectionName) + internal Cursor(ISerializationFactory serializationFactory, Connection connection, string databaseName, string collectionName) { //Todo: add public constrcutor for users to call IsModifiable = true; _connection = connection; _databaseName = databaseName; FullCollectionName = databaseName + "." + collectionName; - _serializationFactory = serializationFactory; + _serializationFactory = serializationFactory; } /// @@ -55,15 +55,15 @@ internal Cursor(ISerializationFactory serializationFactory, Connection connectio /// The skip. /// The fields. internal Cursor(ISerializationFactory serializationFactory, Connection connection, string databaseName, string collectionName, object spec, int limit, int skip, object fields) - : this(serializationFactory, connection, databaseName, collectionName) + : this(serializationFactory, connection, databaseName, collectionName) { - //Todo: add public constrcutor for users to call - if (spec == null) - spec = new Document(); - _spec = spec; - _limit = limit; - _skip = skip; - _fields = fields; + //Todo: add public constrcutor for users to call + if (spec == null) + spec = new Document(); + _spec = spec; + _limit = limit; + _skip = skip; + _fields = fields; } /// @@ -72,103 +72,103 @@ internal Cursor(ISerializationFactory serializationFactory, Connection connectio /// ~Cursor(){ Dispose(false); - } - - /// - /// Gets or sets the full name of the collection. - /// - /// The full name of the collection. - public string FullCollectionName { get; private set; } - - /// - /// Gets or sets the id. - /// - /// The id. + } + + /// + /// Gets or sets the full name of the collection. + /// + /// The full name of the collection. + public string FullCollectionName { get; private set; } + + /// + /// Gets or sets the id. + /// + /// The id. public long Id { get; private set; } - /// - /// Specs the specified spec. - /// - /// The spec. - /// - public ICursor Spec(object spec){ - TryModify(); - _spec = spec; - return this; - } - - /// - /// Limits the specified limit. - /// - /// The limit. - /// - public ICursor Limit(int limit){ - TryModify(); - _limit = limit; - return this; - } - - /// - /// Skips the specified skip. - /// - /// The skip. - /// - public ICursor Skip(int skip){ - TryModify(); - _skip = skip; - return this; + /// + /// Specs the specified spec. + /// + /// The spec. + /// + public ICursor Spec(object spec){ + TryModify(); + _spec = spec; + return this; } - /// - /// Fieldses the specified fields. - /// - /// The fields. - /// - public ICursor Fields(object fields){ - TryModify(); - _fields = fields; - return this; - } - - /// - /// Sorts the specified field. - /// - /// The field. - /// - public ICursor Sort(string field){ - return Sort(field, IndexOrder.Ascending); - } - - /// - /// Sorts the specified field. - /// - /// The field. - /// The order. - /// - public ICursor Sort(string field, IndexOrder order){ - return Sort(new Document().Add(field, order)); + /// + /// Limits the specified limit. + /// + /// The limit. + /// + public ICursor Limit(int limit){ + TryModify(); + _limit = limit; + return this; } - /// - /// Sorts the specified fields. - /// - /// The fields. - /// - public ICursor Sort(object fields){ - TryModify(); - AddOrRemoveSpecOpt("$orderby", fields); - return this; + /// + /// Skips the specified skip. + /// + /// The skip. + /// + public ICursor Skip(int skip){ + TryModify(); + _skip = skip; + return this; + } + + /// + /// Fieldses the specified fields. + /// + /// The fields. + /// + public ICursor Fields(object fields){ + TryModify(); + _fields = fields; + return this; + } + + /// + /// Sorts the specified field. + /// + /// The field. + /// + public ICursor Sort(string field){ + return Sort(field, IndexOrder.Ascending); + } + + /// + /// Sorts the specified field. + /// + /// The field. + /// The order. + /// + public ICursor Sort(string field, IndexOrder order){ + return Sort(new Document().Add(field, order)); + } + + /// + /// Sorts the specified fields. + /// + /// The fields. + /// + public ICursor Sort(object fields){ + TryModify(); + AddOrRemoveSpecOpt("$orderby", fields); + return this; } - /// - /// Hints the specified index. - /// - /// The index. - /// - public ICursor Hint(object index){ - TryModify(); - AddOrRemoveSpecOpt("$hint", index); - return this; + /// + /// Hints the specified index. + /// + /// The index. + /// + public ICursor Hint(object index){ + TryModify(); + AddOrRemoveSpecOpt("$hint", index); + return this; } /// @@ -185,31 +185,31 @@ public ICursor KeepCursor(bool value) _keepCursor = value; return this; } - - /// - /// Snapshots the specified index. - /// - public ICursor Snapshot(){ - TryModify(); - AddOrRemoveSpecOpt("$snapshot", true); - return this; + + /// + /// Snapshots the specified index. + /// + public ICursor Snapshot(){ + TryModify(); + AddOrRemoveSpecOpt("$snapshot", true); + return this; } - - /// - /// Explains this instance. - /// - /// - public Document Explain(){ - TryModify(); + + /// + /// Explains this instance. + /// + /// + public Document Explain(){ + TryModify(); _specOpts["$explain"] = true; - var explainResult = RetrieveData(); + var explainResult = RetrieveData(); try { var explain = explainResult.Documents.FirstOrDefault(); if(explain==null) - throw new InvalidOperationException("Explain failed. No documents where returned."); + throw new InvalidOperationException("Explain failed. No documents where returned."); return explain; } @@ -217,20 +217,20 @@ public ICursor KeepCursor(bool value) { if(explainResult.CursorId > 0) KillCursor(explainResult.CursorId); - } + } } - /// - /// Gets a value indicating whether this is modifiable. - /// + /// + /// Gets a value indicating whether this is modifiable. + /// /// true if modifiable; otherwise, false. public bool IsModifiable { get; private set; } - /// - /// Gets the documents. - /// - /// The documents. - public IEnumerable Documents { + /// + /// Gets the documents. + /// + /// The documents. + public IEnumerable Documents { get { do { @@ -246,7 +246,7 @@ public ICursor KeepCursor(bool value) if(!_keepCursor) Dispose(true); - } + } } /// @@ -264,9 +264,9 @@ public int CursorPosition } } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// public void Dispose() { Dispose(true); @@ -285,15 +285,15 @@ protected virtual void Dispose(bool disposing) KillCursor(Id); } - /// - /// Optionses the specified options. - /// - /// The options. - /// - public ICursor Options(QueryOptions options){ - TryModify(); - _options = options; - return this; + /// + /// Optionses the specified options. + /// + /// The options. + /// + public ICursor Options(QueryOptions options){ + TryModify(); + _options = options; + return this; } /// @@ -301,13 +301,13 @@ protected virtual void Dispose(bool disposing) /// private void KillCursor(long cursorId) { - var killCursorsMessage = new KillCursorsMessage(cursorId); - - try { + var killCursorsMessage = new KillCursorsMessage(cursorId); + + try { _connection.SendMessage(killCursorsMessage,_databaseName); - Id = 0; - } catch (IOException exception) { - throw new MongoConnectionException("Could not read data, communication failure", _connection, exception); + Id = 0; + } catch (IOException exception) { + throw new MongoConnectionException("Could not read data, communication failure", _connection, exception); } } @@ -357,39 +357,39 @@ private void KillCursor(long cursorId) throw new MongoConnectionException("Could not read data, communication failure", _connection, exception); } } - - /// - /// Tries the modify. - /// + + /// + /// Tries the modify. + /// private void TryModify(){ if(!IsModifiable) - throw new InvalidOperationException("Cannot modify a cursor that has already returned documents."); - } - - /// - /// Adds the or remove spec opt. - /// - /// The key. - /// The doc. - private void AddOrRemoveSpecOpt(string key, object doc){ - if (doc == null) - _specOpts.Remove(key); - else - _specOpts[key] = doc; - } - - /// - /// Builds the spec. - /// - /// - private object BuildSpec(){ - if (_specOpts.Count == 0) - return _spec; - - var document = new Document(); - _specOpts.CopyTo(document); - document["$query"] = _spec; - return document; - } - } -} + throw new InvalidOperationException("Cannot modify a cursor that has already returned documents."); + } + + /// + /// Adds the or remove spec opt. + /// + /// The key. + /// The doc. + private void AddOrRemoveSpecOpt(string key, object doc){ + if (doc == null) + _specOpts.Remove(key); + else + _specOpts[key] = doc; + } + + /// + /// Builds the spec. + /// + /// + private object BuildSpec(){ + if (_specOpts.Count == 0) + return _spec; + + var document = new Document(); + _specOpts.CopyTo(document); + document["$query"] = _spec; + return document; + } + } +} diff --git a/source/MongoDB/MongoCollection_1.cs b/source/MongoDB/MongoCollection_1.cs index dfe05a81..986047b2 100644 --- a/source/MongoDB/MongoCollection_1.cs +++ b/source/MongoDB/MongoCollection_1.cs @@ -211,12 +211,17 @@ public T FindOne(string javascriptWhere) try { var command = new Document - { {"findandmodify", Name}, {"query", spec}, {"update", EnsureUpdateDocument(document)}, {"sort", sort}, {"new", returnNew} + { + {"findandmodify", Name}, + {"query", spec}, + {"update", EnsureUpdateDocument(document)}, + {"sort", sort}, + {"new", returnNew} }; - var response = _connection.SendCommand>(_configuration.SerializationFactory, - DatabaseName, - typeof(T), + var response = _connection.SendCommand>(_configuration.SerializationFactory, + DatabaseName, + typeof(T), command); return response.Value; diff --git a/source/MongoDB/Serialization/Descriptors/ClassMapPropertyDescriptor.cs b/source/MongoDB/Serialization/Descriptors/ClassMapPropertyDescriptor.cs index 620d1874..b2280308 100644 --- a/source/MongoDB/Serialization/Descriptors/ClassMapPropertyDescriptor.cs +++ b/source/MongoDB/Serialization/Descriptors/ClassMapPropertyDescriptor.cs @@ -37,7 +37,7 @@ public override IEnumerable GetProperties() if (ClassMap.HasId) yield return CreateProperty(ClassMap.IdMap.Alias, ClassMap.IdMap.MemberReturnType, ClassMap.GetId(_instance), false); - if (ShouldPersistDiscriminator()) + if (ShouldAddDiscriminator()) yield return CreateProperty(ClassMap.DiscriminatorAlias, ClassMap.Discriminator.GetType(), ClassMap.Discriminator, false); foreach (var memberMap in ClassMap.MemberMaps) @@ -62,7 +62,7 @@ public override IEnumerable GetProperties() /// private BsonPropertyValue GetValue(string name) { - if (ClassMap.DiscriminatorAlias == name && ShouldPersistDiscriminator()) + if (ClassMap.DiscriminatorAlias == name && ShouldAddDiscriminator()) return new BsonPropertyValue(ClassMap.Discriminator.GetType(), ClassMap.Discriminator, false); object value; diff --git a/source/MongoDB/Serialization/Descriptors/ClassMapPropertyDescriptorBase.cs b/source/MongoDB/Serialization/Descriptors/ClassMapPropertyDescriptorBase.cs index 21e619b5..5aa6123b 100644 --- a/source/MongoDB/Serialization/Descriptors/ClassMapPropertyDescriptorBase.cs +++ b/source/MongoDB/Serialization/Descriptors/ClassMapPropertyDescriptorBase.cs @@ -71,9 +71,9 @@ protected BsonProperty CreateProperty(string alias, BsonPropertyValue value) /// Shoulds the persist discriminator. /// /// - protected bool ShouldPersistDiscriminator() + protected bool ShouldAddDiscriminator() { - return (ClassMap.IsPolymorphic && ClassMap.HasDiscriminator) || ClassMap.IsSubClass; + return ClassMap.IsSubClass; } /// diff --git a/source/MongoDB/Serialization/Descriptors/DocumentClassMapPropertyDescriptor.cs b/source/MongoDB/Serialization/Descriptors/DocumentClassMapPropertyDescriptor.cs index 07c021b4..8a03a17b 100644 --- a/source/MongoDB/Serialization/Descriptors/DocumentClassMapPropertyDescriptor.cs +++ b/source/MongoDB/Serialization/Descriptors/DocumentClassMapPropertyDescriptor.cs @@ -31,7 +31,7 @@ public DocumentClassMapPropertyDescriptor(IMappingStore mappingStore, IClassMap /// public override IEnumerable GetProperties() { - if(ShouldPersistDiscriminator()) + if(ShouldAddDiscriminator()) { if (_document.ContainsKey("count")) //this is a special case { @@ -65,7 +65,7 @@ public override IEnumerable GetProperties() /// private BsonPropertyValue GetValue(string name) { - if (ClassMap.DiscriminatorAlias == name && ShouldPersistDiscriminator()) + if (ClassMap.DiscriminatorAlias == name && ShouldAddDiscriminator()) return new BsonPropertyValue(ClassMap.Discriminator.GetType(), ClassMap.Discriminator, false); var value = _document[name]; diff --git a/source/MongoDB/Serialization/Descriptors/ExampleClassMapPropertyDescriptor.cs b/source/MongoDB/Serialization/Descriptors/ExampleClassMapPropertyDescriptor.cs index 0f4816c8..df6c1bd4 100644 --- a/source/MongoDB/Serialization/Descriptors/ExampleClassMapPropertyDescriptor.cs +++ b/source/MongoDB/Serialization/Descriptors/ExampleClassMapPropertyDescriptor.cs @@ -34,7 +34,7 @@ public ExampleClassMapPropertyDescriptor(IMappingStore mappingStore, IClassMap c /// public override IEnumerable GetProperties() { - if (ShouldPersistDiscriminator()) + if (ShouldAddDiscriminator()) yield return CreateProperty(ClassMap.DiscriminatorAlias, ClassMap.Discriminator.GetType(), ClassMap.Discriminator, false); foreach (PropertyInfo propertyInfo in _exampleType.GetProperties())