I am using mongodb with the official c# driver.
I am using Guids as Id field for my objects. I don't want to introduce a dependency on the mongodb bson classes so I am not using ObjectId in my domain layer.
Is it possible to instruct mongodb to insert a creation timestamp into objects that I insert into the datastore?
Example:
public class Foo
{
public Guid Id {get;set;}
public DateTime CreatedOn {get;set;}
}
Using mongodb idGenerators I can get the Guids generated upon insert. I know ObjectId has the timestamp included but as mentioned I wouldn't want my class to look like this
public class Foo
{
public ObjectId Id { get; set; }
public DateTime CreatedOn {get { return Id.CreationTime;}}
}
Is it important that it's the inserted timestamp and not the object's created timestamp? If not then do it in the constructor of the class. Or even better, a base class for your class(es).
public abstract class BaseClass
{
[BsonId]
public Guid Id {get;set;}
public DateTime CreatedOn {get;set;}
protected BaseClass()
{
Guid = new Guid();
CreatedOn = new DateTime.UtcNow;
}
}
public class Foo : BaseClass
{
}
Is this something you can use for it?
You can have an _id which is itself a document :
in json : { _id : {guid : ...., createdOn : ....} , field1 : ..., field2:....}
You just have to modify your idGenerator to have this behavior.
I recommend, however, that you really re-consider to use ObjectId.
If you're trying to make your domain layer pure and free of persistence concerns, then it makes sense to populate the creation date yourself in the domain layer, when deciding to create an entity, instead of relying on the database technology to put in a server timestamp.
This makes "creation date" a logical domain concept rather than the DB's concept of "timestamp when first stored in DB". The two can differ e.g. in cases of migrating data (but keeping the timestamp), deferring execution (e.g. in jobs), etc.
It also creates a healthy separation between "physical timestamp" and "logical timestamp" which you can further exploit during testing/mocking (e.g. you could have a test that says "do X, then change the logical time to 2 days in the future, then assert Y").
Finally, it forces you to think of what the creation date means in your domain layer instead of blindly assuming that it will be correct.
All this being said, if you insist on having it in MongoDB you can have a mapping that creates an ObjectID into some kind of hidden field (e.g. an explicitly-implemented interface) at insert time, and extracts its timestamp into the CreationDate field at read time.
Related
Following the guide lines from Domain Driven Design, I try to avoid having one aggregate referencing a different aggregate. Instead, an aggregate should reference another aggregate using the other aggregate's id, for example:
public class Addiction
{
private Addiction(){} //Needed for EF to populate non-simple types
//DrugType belongs to the aggregate,
//inflate when retrieving the Addiction from the db
//EF does not need DrugId for navigation
Drug Drug{get;set;}
//The supplier is not part of the aggregate,
//aggregates only reference eachother using Ids
int SupplierId{get;set;}
//Other properties
}
public class AddictionConfiguration : IEntityTypeConfiguration<Addiction>
{
builder.HasOne(addiction => addiction.Drug); //Works
builder.HasOne("SupplierId") //Does not work.
}
In this (not very realistic) example, Drug is part of the Addiction's aggregate. When loading this entity from the database using EF, it will also inflate the Drug property without me having to specify the DrugId as the foreign key.
However, now I need to get a list of all Addictions and their suppliers by mapping the relevant properties to a Dto. I try to achieve this by using AutoMapper's ProjectTo functionality, e.g.
_mapper.ProjectTo<AddictionDto>(_dbContext.Addictions.Where(x => x.Id > 1));
where AddictionDto is defined as
public class AddictionDto
{
DrugDto Drug {get;set;}
SupplierDto Supplier {get;set;}
//other properties
}
And
public class SupplierDto
{
public int Id {get;set;}
public string Name {get;set;}
}
Automapper correctly loads the Addiction and also the Drug, but I cannot get it to load the Supplier. I've tried all the options of the IEntityTypeConfiguration to tell EF that there is a navigation property, but I cannot get it to work. Does anyone know if is even possible to do what I described above?
Suppose I have a .NET Entity Framework model class:
public class Foo
{
public int FooId { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public DateTime LastUpdated { get; set; }
}
The Created and LastUpdated columns in my SQL Server table, both of type DATETIME2, have a DEFAULT constraint (SYSUTCDATETIME()). An AFTER UPDATE trigger sets LastUpdated to SYSUTCDATETIME whenever the Description is changed.
In my code, when I'm reading from the Foo table, I want Created and LastUpdated included, because I want to use their values. But when I'm adding a row to the table, I don't want them included in the Add because I want SQL Server to use the default value I've configured it to use. I thought it would just have been a matter of having
Foo foo = new Foo
{
Description = "This is my latest foo."
}
but C# is giving the two date properties their own default value of 0001-01-01T00:00:00.000000, which isn't null, and this is what's getting recorded in the table.
Isn't there an attribute that tells the framework not to write a property back to the database? It isn't NotMapped because that would prevent the values from being read.
`Don't you hate when you find the answer right after posting your question?
[DatabaseGenerated(DatabaseGeneratedOption.Computed)] omits the property from inserts and updates.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] omits the property from inserts.
The latter will take care of EndDate, which I didn't illustrate in my post. I have the database set a default value of 9999-12-31T23:59:59 on insert, but my application will change its value later when Foo is meant to expire.
What kills me is they based the naming on specific use cases that wouldn't come to mind in a different scenario. They ought to have gone with [SkipOnInsert] and [SkipOnUpdate] (which could then be combined as needed).
I'm using GORM for MongoDB in my Grails 3 web-app to manage read/writes from DB.
I have the following 2 domain classes:
class Company {
String id
}
class Team {
String id
Company company
}
For teams, their company is saved on DB as String, and with GORM I can simply use team.company to get an instance of Company domain class.
However, I need to override the getter for company, and I need the raw value for company id (as stored on DB), without GORM getting in the way and performing its magic.
Is there a way to get the raw String value?
Any help is welcome! Thanks in advance
Update (May 27)
Investigating #TaiwaneseDavidCheng suggestion, I updated my code to
class Company {
String id
}
class Team {
String id
Company company
String companyId
static mapping = {
company attr: "company" // optional
companyId attr: "company", insertable: false, updateable: false
}
}
Please note that I'm using GORM for MongoDB, which (citing the manual) tries to be as compatible as possible with GORM for Hibernate, but requires a slightly different implementation.
However I found out (by trial&error) that GORM for MongoDB doesn't support a similar solution, as it seems only one property at a time can be mapped to a MongoDB document property.
In particular the last property in alphabetical order wins, e.g. companyId in my example.
I figured out a way to make the whole thing work, I'm posting my own answer below.
given a non-insertable non-updateable column "companyId" in domain class
class Company {
String id
}
class Team {
String id
Company company
Long companyId
static mapping = {
company column:"companyId"
companyId column:"companyId",insertable: false,updateable: false
}
}
(Follows the edit to my question above)
I defined a custom mapping, and made use of Grails transients by also defining custom getter and setter for team's company.
class Company {
String id
}
class Team {
String id
Company company
String companyId
static mapping = {
companyId attr: "company" // match against MongoDB property
}
static transients = [ 'company' ] // non-persistent property
Company getCompany() {
return Company.get(companyId)
}
void setCompany(Company company) {
companyId = company.id
}
}
Say I have a User class which has a manual reference to a customer document:
public class User(){
#Id
public String id;
public String name;
public String customerId;
}
I want both the id & customerId to be stored as an ObjectId in mongo.
When saving a User document, the "id" gets converted to an ObjectId, however, the customerId gets saved as a string. I could have customerId of type ObjectId, but I would rather have the POJO as a string and have the customerId automatically convert to ObjectId when saving/querying. There does not seem to be any built in annotation which behaves like #Id, but can be used for manual references. How would I go about creating one, or is there a better solution? I have read a bit above converters, but I do not want to re-map the whole POJO to a DBObject.
Any advice would be appreciated.
when you get your customer data you have to create the objectId yourself.
Db.Customer.find({"_id" : new ObjectId("$valueFromUserTable")});
so in Spring Java you would:
ObjectId objId = new ObjectId("$valueFromUserTable");
Query query = new Query(Criteria.where("_id").is(objId));
Customer customer = super.mongoOps.find(query, Customer.class);
An application persists Guid field in Mongo and it ends up being stored as BinData:
"_id" : new BinData(3, "WBAc3FDBDU+Zh/cBQFPc3Q==")
The advantage in this case is compactness, the disadvantage shows up when one needs to troubleshoot the application. Guids are passed via URLs, and constantly transforming them to BinData when going to Mongo console is a bit painful.
What are drawbacks of storing Guid as string in addition to increase in size? One advantage is ease of troubleshooting:
"_id" : "3c901cac-5b90-4a09-896c-00e4779a9199"
Here is a prototype of a persistent entity in C#:
class Thing
{
[BsonIgnore]
public Guid Id { get; set; }
[BsonId]
public string DontUseInAppMongoId
{
get { return Id.ToString(); }
set { Id = Guid.Parse(value); }
}
}
In addition to gregor's answer, using Guids will currently prevent the use of the new Aggregation Framework as it is represented as a binary type. Regardless, you can do what you are wanting in an easier way. This will let the mongodb bson library handle doing the conversions for you.
public class MyClass
{
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set;}
}
The drawbacks are that mongodb is optimised to use BSON ObjectID's so it will be slightly less efficient to use strings as ObjectID's. Also if you want to use range based queries on string ObjectIDs then a lexicographic compare will be used which may give different results than you expect. Other than that you can use strings as ObjectIDs.
See Optimizing ObjectIDs
http://www.mongodb.org/display/DOCS/Optimizing+Object+IDs