I am building an application that uses Mongo and the offical C# driver. I am having an odd problem when using interfaces and generic type constraints. Consider the following:
interface IMongoObject
{
BsonObjectId Id { get; set; }
string Name { get; set; }
}
class MongoObject : IMongoObject
{
public BsonObjectId Id { get; set; }
public string Name { get; set; }
}
class Program
{
MongoServer m_db;
string m_dbName;
protected void Save<T>(T entity) where T : IMongoObject
{
GetDatabase().GetCollection<T>(typeof(T).Name).Save(entity);
}
protected T Get<T>(string name) where T : IMongoObject
{
Type t = typeof(T);
// Throws
return GetDatabase().GetCollection<T>(typeof(T).Name).AsQueryable().Where(o => o.Name == name).FirstOrDefault();
}
protected MongoDatabase GetDatabase()
{
if (m_db == null)
{
var conString = "mongodb://localhost/MongoTest";
MongoUrl url = new MongoUrl(conString);
m_dbName = url.DatabaseName;
m_db = MongoServer.Create(url);
}
return m_db.GetDatabase(m_dbName);
}
void Run()
{
MongoObject o = new MongoObject();
o.Name = "Foo";
Save(o);
MongoObject b = Get<MongoObject>("Foo");
Console.ReadKey();
}
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
}
Everything works fine, apart from the call to Get(...). Using the offical driver, I get a null reference exception from the call var memberSerializationInfo = containingSerializationInfo.Serializer.GetMemberSerializationInfo(memberName);
in the function private BsonSerializationInfo GetSerializationInfoMember(IBsonSerializer serializer, MemberExpression memberExpression)
in SelectQuery.cs.
If I switch to using FluentMongo for LINQ, I get a message saying
"Discriminators can only be registered for classes, not for interface MyLib.Services.IRepoData."
This is basically the same as this guy:
FluentMongo throwing error all of a sudden
I understand that the Mongo driver is complaining because it doesnt know what IMongoObject is. What I dont understand is why the expression within the where call is seeing "o" as type IMongoObject not of type MongoObject (confirmed with debugger this is whats happening), which is what it is.
If I switch to using an abstract base class, it all works fine. I dont what to do this because not all "MongoObjects"
Thanks
So, the issue actually lies in how the C# compiler is putting together the expression tree for the object. Because of your type constraint, it is casting your MongoObject to an IMongoObject when accessing the Name property. Hence, IMongoObject is the type of the expression and we can't find any members on IMongoObject. I'll see if there is something we can do for this circumstance in the next version of the driver, but for now, I'd stick with abstract classes.
On a separate note, it might be best to not mix your "domain" objects with your DTOs. Instead, have 2, one for each responsibility and map between them.
--UPDATE-- Interesting note. Apparently, this has been fixed based on a different issue with how VB.NET creates expression trees differently that c#. So, in the near future, this will work correctly. You can see the test proving this here: https://github.com/craiggwilson/mongo-csharp-driver/commit/153b9862b122521eee681a86e56806d94fed8b21#diff-1.