MongoDb BsonClassMap - c#

I'm new to MongoDb and I'm currently using the CSharp drivers (version 1.2). My problems occur when using BsonClass map. Below is the code I'm tring to execute. I've simply defined a custom type I'd like to map to a BsonDocument.
In order to use this I'm taking advantage of BsonClassMap.RegisterClassMap(). When I hit the foreach statement (trying to access the first entry in the FinAs() results) I get the following error:
'Cannot deserialize Guid from BsonType ObjectId.'
From what I understand BsonClassMap uses GuidGenerator for objects of type Guid. Why am I getting this error?
Please note that the insertion is performed without any errors...and after performing the insert, newEmployee has an EmployeeId that's been automatically generated for it.
Here's the code I'm trying to run:
class Program{
static void Main(string[] args){
MongoServer server = MongoServer.Create();
MongoDatabase dataBase = server.GetDatabase("test");
MongoCollection<Employee>employees = dataBase.GetCollection<Employee>("employees");
BsonClassMap.RegisterClassMap<Employee>(cm =>
{
cm.MapProperty(c => c.Name);
cm.MapProperty(c => c.Email);
cm.MapIdProperty(c => c.EmployeeId);
});
var newEmployee = new Employee{ Name="Test", Email="test#test.com"};
employees.Insert(newEmployee);
foreach(Employee e in employees.FindAs<Employee>(Query.EQ("Name","Test")){
Console.Writeline(e.Name);
}
}
}
public class Employee
{
public Guid EmployeeId {get;set;}
public string Name {get;set;}
public string Email {get;set;}
}

I suspect you need to use SetIdMember to identify your id field. Any particular reason you aren't using ObjectId values instead of Guids?

Turns out I had some additional documents stored in my collection before I started using BsonClassMap. Their ObjectId was stored in a format native to MongoDB....unfortunately this format couldn't be parsed to a Guid. To fix the error I ended up wiping out all old entries.

Related

SQL query to multiple objects using Entity Framework 6

I'm taking the time to learn EF (specifically version 6).
I created two tables in MSSQL and linked up EF6 to the database creating the EF model framework.
Then I created the classes in code. My desire is to have one row pulled with a list of items for "UserDatas" (yes I know it's misspelled).
Consider this code:
public class user
{
[Key]
public int pkID;
public string ForeignCode;
public string UserName;
public virtual List<UserData> UserDatas { get; set; }
}
public class UserData
{
[Key]
public int pkID;
public int fkUserID;
public string ColumnName;
public string ColumnValue;
}
class Program
{
static TestData db = new TestData();
static void Main(string[] args)
{
var record = db.tblUsers.Select(x => new { x.UserName, x.pkID }).FirstOrDefault();
var record2 = db.tblUsers.Include(x => x.tblUserDatas).ToList();
Console.ReadLine();
}
}
The first query is just a test to pull the primary record in the tblUsers table.
The second query is where I am attempting to pull all fields related to that user which include things like first name, last name, address, etc...
What happens when I set a break point on the Console.Readline(), I see 5 rows listed for record2. The "user" class is duplicated in each of those rows. I was expecting to see that only listed once with a list of items for "UserDatas".
How can I get this query to come through as I expect with one row containing a list of "UserDatas"?
Again, this is only for learning purposes so don't worry about data and if this is the best way to store it.
It should be as simple as the following (if you don't need the projection/anonymous object) and assuming your entities are configured correctly
var user = db.tblUsers
.Include(x => x.UserDatas) // include user data
.FirstOrDefault(); // select the first user
Some notes,
There is no need to prefix tables with tbl
There is no need to prefix fields with pk, fk
If you used Id, you don't need to specify [key]

Read object from c# database into classes directly

What I am trying to do is read a database, row by row, and use the data from each row to initialize an object of the type that data represents. In this case I am reading rows of the Device table and trying to create Device objects with that data. I saw this SO link:
and I tried this snippet:
using(var dc = new DataContext(connectionString))
{
List<Person> people = dc.ExecuteQuery(#"
SELECT Id, Name Address
FROM [People]
WHERE [Name] = {0}", name).ToList(); // some LINQ too
}
But it is telling me
The type arguments for this usage cannot be inferred from the usage
Is this in principal correct or should I be using the BondIO serializer/deserializer? as mentioned here
Also the order of the members in the object may not be the same as the order of the columns in the database, is this relevant?
Later that same day....
I now have a DBContext with all my database objects defined like this:
public class MyContext : DBContext
{
public dbSet<Device>{ get; set;}
etc...
}
And I now try to get object using this snippet:
using (var db = new MyContext(ConnectionString))
{
var res = db.Device.Find(ID);
}
However this gives an exception message
Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.AttributeToColumnAnnotationConvention`2
I have checked the database and it should return 1 value based on the PrimaryKey ID that I am passing. Anybody have any hints what I'm still doing wrong.
You cannot, because ExecuteQuery is for executing statements, not for querying database. You should use SqlQuery instead
What you can do is, to create a new class with the properties you want to set in your query, means a simplified version of your query. In your case
public class Device
{
public int Id {get;set}
public string Name {get;set}
public string Address {get;set}
}
then use it as
var people = dc.ExecuteQuery<Device>(#"
SELECT Id, Name Address
FROM [People]
WHERE [Name] = {0}", name).ToList();

Get a document by LUUID

I got an .net core application that is pretty straight forward it is using REST to add and download objects to and from mongo db. Adding items works really well. Getting a list that contains all items aswell, but when I try to access one using id then everytime I get null. What should i change to make this piece of code work. It means get a Tool object from database using it unique ID when there's one matching in database.
Here's a object in database
Here's my repository class
private IMongoCollection<Tool> Tools => _database.GetCollection<Tool>("Tools");
public async Task<Tool> GetAsync(Guid id) =>
await Tools.AsQueryable().FirstOrDefaultAsync(tool => tool.Id == id);
Argument looks like that when I check it out in debugger "{ee1aa9fa-5d17-464c-a8ba-f685203b911f}"
Edit
Tool Class Properties
public Guid Id { get; protected set; }
public string Model { get; protected set; }
public string Brand { get; protected set; }
public string Type { get; protected set; }
public uint Box { get; protected set; }
Fixed check comments
Project on github
The easiest way to do this in C# MongoDB Driver is to set a global GuidRepresentation setting which can be found on the BsonDefaults object. This is a global setting and will effect all serialization/deserialization of GUIDs in to Bson Binary Objects.
BsonDefaults.GuidRepresentation = GuidRepresentation.PythonLegacy;
var collection = new MongoClient().GetDatabase("test").GetCollection<ClassA>("test");
var item = collection.Find(x => x.MyId == new Guid("ee1aa9fa-5d17-464c-a8ba-f685203b911f"))
.FirstOrDefault();
The second option is to convert the GUID manually from a LUUID to a CSUUID, for this there is a helper class within the MongoDB driver of GuidConverter, with this it converts the GUID in to byte[] which is normally used for storage but we can use it for our query.
BsonDefaults.GuidRepresentation = GuidRepresentation.CSharpLegacy;
var collection = new MongoClient().GetDatabase("test").GetCollection<ClassA>("test");
var luuid = new Guid("0798f048-d8bb-7048-bb92-7518ea4272cb");
var bytes = GuidConverter.ToBytes(luuid, GuidRepresentation.PythonLegacy);
var csuuid = new Guid(bytes);
var item = collection.Find(x => x.MyId == csuuid)
.FirstOrDefault();
I also noticed that you're using Robo 3T (formerly Robomongo), within this application you can set how GUIDs are displayed by going to Options -> Legacy UUID Encodings

Azure Document DB UpdateDoc

I am starting off with azure document db. I was trying to update an existing document. When I use the following query everything works:
dynamic Team2Doc = client.CreateDocumentQuery<Document>(documentCollection.DocumentsLink).Where(d => d.Id == "t002").AsEnumerable().FirstOrDefault();
Team2Doc.TeamName = "UPDATED_TEAM_2";
await client.ReplaceDocumentAsync(Team2Doc);
but if use the below code:
dynamic Team2Doc = client.CreateDocumentQuery<Document>(documentCollection.DocumentsLink).Where(d => d.TeamName== "team1").AsEnumerable().FirstOrDefault();
Team2Doc.TeamName = "UPDATED_TEAM_2";
await client.ReplaceDocumentAsync(Team2Doc);
I get this error:
"The best overloaded method match for
'Microsoft.Azure.Documents.Client.DocumentClient.ReplaceDocumentAsync(Microsoft.Azure.Documents.Document,
Microsoft.Azure.Documents.Client.RequestOptions)' has some invalid
arguments"
Is there anyway to retrieve a document by one of the properties and update the document?
The where clause is trying to query the property TeamName which does not exist in Document class.
Changing the type of the queryable to your data model should fix it.
For example, say you have the following data model:
public class EmployeeDocument : Document
{
// Other properties that you may have similarly defined ....
public class string TeamName
{
get
{
return this.GetValue<string>("TeamName");
}
set
{
this.SetValue("TeamName", value);
}
}
}
Then you can modify your query like this:
var team2Doc = client.CreateDocumentQuery<EmployeeDocument>(documentCollection.DocumentsLink).Where(d => d.TeamName== "team1").AsEnumerable().FirstOrDefault();
team2Doc.TeamName = "UPDATED_TEAM_2";
await client.ReplaceDocumentAsync(team2Doc);
Note that you have to use the EmployeeDocument, instead of the Document class, while creating the document queryable. That will let you query on EmployeeDocument properties.
SQL Version
Creating a document model for each of your existing data models may not be feasible if you have a large number of data models. In that case you may want to try out the SQL query syntax.
Refer to Aravind's answer in this post. The example he uses is for deleting documents, but it can be easily modified to update them too.
You can also create a model with Id:
public class Employee
{
[JsonPropery("id")]
public class string Id { get; set; }
public class string TeamName { get; set; }
}
And then replace the document using it's Id:
var employee= client
.CreateDocumentQuery<Employee>(documentCollection.DocumentsLink)
.Where(d => d.TeamName== "team1")
.AsEnumerable()
.FirstOrDefault();
employee.TeamName = "team2";
var documentUri = UriFactory.CreateDocumentUri(databaseName, collectionName, employee.Id);
await client.ReplaceDocumentAsync(employee);

Id field mapping is broken after ensureIndex using MongoDB C# driver

I have the following entity with custom ID field:
public class User : IEntity {
public string Id { get; set; }
}
if (!BsonClassMap.IsClassMapRegistered(typeof(User))) {
BsonClassMap.RegisterClassMap<User>(map => map.AutoMap());
}
It works pretty good, when I add new record to collection, ID value is assigned.
However, when I'm trying to index any other field within this collection:
UserCollection.EnsureIndex(IndexKeys<User>.Ascending(p => p.Email),IndexOptions.SetUnique(true));
Id mapping becames broken, ID field value is null after insert and in collection I see default '_id' field generated (insead of my 'ID').
Any ideas what's the problem? (I'm using driver 1.8.3.9, mongo win32 2.4.5, NET 4.5). Thanks.
Found solution! UserCollection.EnsureIndex was called before the class mapping, causing the problem.

Categories

Resources