I'm developing a simple question-answers forum. I want to use Azure DocumentDB.
How can i add an answer to existing question document in DocumentDB?
In my case i've done a list of answers of the same type as question:
public List<Announcement> { get; set; }
My document object looks as follows:
public class Announcement
{
[JsonProperty(PropertyName = "id")]
public string ID { get; set; }
[JsonProperty(PropertyName = "userid")]
public int UserID { get; set; }
[JsonProperty(PropertyName = "username")]
public string UserName { get; set; }
[JsonProperty(PropertyName = "useripaddress")]
public string UserIPAddress { get; set; }
[JsonProperty(PropertyName = "body")]
public string Body { get; set; }
[JsonProperty(PropertyName = "tags")]
public List<string> Tags { get; set; }
[JsonProperty(PropertyName = "datecreate")]
public DateTime DateCreate { get; set; }
[JsonProperty(PropertyName = "isedited")]
public bool IsEdited { get; set; }
[JsonProperty(PropertyName = "dateedit")]
public DateTime DateEdit { get; set; }
[JsonProperty(PropertyName = "imageaddonid")]
public string ImageAddonId { get; set; }
[JsonProperty(PropertyName = "imageaddonsource")]
public string ImageAddonSource { get; set; }
[JsonProperty(PropertyName = "answers")]
public List<Announcement> { get; set; }
}
I don't see any update methods in DocumentClient :/.
For me, you need to use the ReplaceDocumentAsync method in order to update your document.
As far as I can tell you need to be replacing the entire document; because when you change something you update the entire document not a part of it.
You can check the documentation about the method here
At the moment you can't update part of the document, you have to update the whole document. Updating part of the document is a frequently requested feature and you can vote for Microsoft to add it in the future. Do so on Azure's Uservoice: https://feedback.azure.com/forums/263030-documentdb/suggestions/6693091-be-able-to-do-partial-updates-on-document
In any case, you should probably rethink whether you want to embed the answers into the question itself.
According to the article Modeling data in DocumentDB, you should embed if
There are contains relationships between entities.
There are one-to-few relationships between entities.
There is embedded data that changes infrequently.
There is embedded data won't grow without bound.
There is embedded data that is integral to data in a document.
If you look at the bullet points in bold, it looks like having the answers inside the question might not be a good idea as you can potentially have thousands of answers, and the question document will change very frequently.
Instead you can either have
each answer defined as a document with a reference to the question, so you just add an answer document when someone adds an answer. That's the easiest option.
or you can define documents made up of 100 answers for example so it won't grow too much either. But then that takes a bit more planning to manage.
Related
I am using mongodb C# driver and trying to add "Goal" Entity in the "List", contained in Client class, but mongodb always returns an empty objectId og Goal.
Client.cs
public class Client
{
public string UserName { get; set; }
[BsonId]
public ObjectId ClientId { get; set; }
[BsonIgnore]
public string ClientIdString
{
get
{
return this.ClientId.ToString();
}
}
[BsonRequired]
public string ClientName { get; set; }
public string EmailAddress { get; set; }
public string CompanyName { get; set; }
public List<Goal> Goals { get; set; }
}
Goal.cs
public class Goal
{
public ObjectId GoalId { get; set; }
[BsonRequired]
public string Title { get; set; }
public string Description { get; set; }
[BsonDefaultValue(State.NotStarted)]
public State GoalState { get; set; }
[BsonIgnore]
public string StateString
{
get
{
return this.GoalState.ToString();
}
}
}
and i am trying to add Goal to client with following code:
IMongoQuery query = Query<Client>.EQ(eq => eq.ClientId, id);
IMongoUpdate update = Update<Client>.Push<Goal>(push => push.Goals, goal);
WriteConcernResult result = this.ClientCollection.Update(query, update);
But every time mongodb returns an empty objectId of newly added Goal in the List in client.
Kindly guide me what i am doing wrong?
Thanks in advance. :)
Kindly guide me what i am doing wrong?
Nothing really, but you're expecting the wrong behavior.
Embedded documents don't have an _id. Of course, you can force mongodb to create a field that happens to have that name, but unlike the root _id, such a field has no special semantics and there's no default index. The lack of these special semantics is also the reason why the driver doesn't bother generating a value for the field for you.
Generally speaking, having a unique index in an embedded document is often a sign of a malformed data structure. Make sure such an id is strictly required. If the id must be globally unique, it appears the embedded document might also make sense or be relevant if it had another parent, which indicates that it should be a first-level citizen, i.e. have a collection of its own.
Remember that this also a question of ownership - what happens with concurrent writes? Of course, you can go to great lengths and only use atomic modifiers to allow different writes to one document, but that is unnecessarily complicated IMHO.
Background I am using EF6 and is free to change the database.
I ran into this problem today. Say I have:
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Address
{
public int Id { get; set; }
public int CompanyId { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
}
The users are allowed to update Company and Address. Users are also allowed to update Invoice. But since it is a financial document, it must somehow save a snapshot of the address if the user marks IsCompleted to true.
Currently it is done in the following way:
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
//Auditing fields
public string CompanyName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
}
I think this is hard to follow. I was thinking:
Option 1: Save an audit to an audit table of its own:
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
//Null if IsCompleted = false.
public DateTime? CompletedTimeStamp { get; set; }
}
public class CompanyAudit
{
public int CompanyId { get; set; }
public string Name { get; set; }
public DateTime TimeStamp { get; set; }
}
public class AddressAudit
{
public int AddressId { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public DateTime TimeStamp { get; set; }
}
But then that seems like a lot of tables to create and a lot of work if we do change the schema for Company and Address. Also, it's not very robust. I can't reuse this for other documents without a bunch of wiring. However, this is what I found mostly on the internet. One audit table for each table.
Option 2: Save all the audit to the same table:
public class Audit
{
public int DocumentId { get; set; }
public string DocumentType { get; set; }
public string JsonData { get; set; }
public DateTime TimeStamp { get; set; }
}
But then, this seems like it's not very standard. I have never saved Json data to a SQL database before. Is this bad? If so What could go wrong?
Should I go with Option 1 or Option 2?
Short answer
Assuming you are using SQL Server, I would recommend creating some XML-serializable DTOs for that job and storing XML in a dedicated column, using the XML datatype.
Slightly longer answer
I have already gone that exact same path. We needed to save a snapshot of data at the point that it has been printed out, and there were many tables involved that would have been duplicated in the process.
Requirements and Evaluation
We didn't want to incorporate an additional technology (e.g. File System as proposed by Andreas or some NoSQL/document database), but store everything in SQL Server, as otherwise this would have complicated backup scenarios, deployment, maintenance and so on.
We wanted something easy to understand. New developers should be familiar with the technology used. Architecture shouldn't be influenced too much.
For serialization, there are several options: XML, JSON, BinaryFormatter, DataContractSerializer, Protocol Buffers... Our requirements: Easy versioning (for added properties or relationships), readability, conformance with SQL Server.
Easy versioning should be possible using all mentioned formats. Readability: XML and JSON win here. Conformance with SQL Server: XML is supported in SQL Server natively and was our choice.
Implementation
We did several things:
Create additional DTOs in our database project, side by side with the existing Entities, not for usage with EF but for XML serialization. They are annotated with XmlAttributes and resemble a complex, self-contained structure with everything that is needed to hold the document's data. The root InvoiceSnapshot class has Parse and ToXml methods to support serialization.
Update our Entities to include the snapshots, where required:
public string InvoiceXml { get; set; }
public InvoiceSnapshot Invoice
{
get
{
return this.InvoiceXml != null
? InvoiceSnapshot.Parse(this.InvoiceXml)
: null;
}
set { this.InvoiceXml = value != null ? value.ToXml() : null; }
}
Update the entity configuration to create an XML column and ignore the InvoiceSnapshot property:
public class InvoiceEntityConfig : EntityTypeConfiguration<InvoiceEntity>
{
public InvoiceEntityConfig()
{
this.Property(c => c.InvoiceXml).HasColumnType("xml");
this.Ignore(c => c.Invoice);
}
}
Modify our business objects so that they load themselves either from Entities (editable state) or from XML-DTOs (snapshot, readonly state). We use interfaces on both where they help streamlining the process.
Further steps
You should add metadata for common queries in separate scalar columns and index them. Retrieve the xml data only when you really want to show the invoice.
You can look into what SQL Server can do for you regarding XML, especially when you need to query based on attributes. It can index them, and you can use XPath in queries.
Sign or hash your xml to make sure that snapshot data won't be tampered with.
Option2
BUT
A better way is to store the genrated invoice (pdf)
and all reversions of it in normal files.
You can still use your Invoice table
but then u dont need to wory if some customer-data change
and an user reprints an older document.
To store the generated documents it's near the same
as to store the model in JsonData but u dont Need to
safe the Version of your template and template generator.
Option1 is just brut force and is maybe better released
with the "Event Store", "Event Sourcing" and "Query and Command" pattern.
public class Document //or DocumentFile
{
public int DocumentId { get; set; }
public string DocumentType { get; set; }
public string FilePath { get; set; }
[Index]
public String Owner { get; set;} //exp. "Customer:Id", "Contact:Id" maybe just int
[Index]
public String Reference { get; set; } //exp. "Invoice:Id", "Contract:Id" maybe just int
public DateTime TimeStamp { get; set; } //maybe int Reversion
}
My solution should work well with: ASP.NET MVC 4, EF5, Code First, Visual Studio 2012 Express, SQL Server 2012 Express.
I need to store many tags with a place object. In another question it was suggested I use a List to achieve this:
asp.net MVC 4, tagging places - best practice (eg Pub, Store, Restaurant)
ie
public List<String> Tags {get;set;}
If I just add that it doesn't get persisted in the database at all. How can I make it persist?
Thanks.
Update
This needs a many to many relationship - eg a place might be tagged Cafe and Cycle Shop. There are many cafe's and cycle shops.
public class Place
{
public Place()
{
// Set default value of dateAdded to now
DateAdded = DateTime.Now;
}
[ScaffoldColumn(false)]
public virtual int PlaceID { get; set; }
public virtual List<Tag> Tags { get; set; }
[ScaffoldColumn(false)]
[DisplayName("Date Added")]
public virtual DateTime DateAdded { get; set; }
[Required(ErrorMessage = "Place Name is required")]
[StringLength(100)]
public virtual string Name { get; set; }
public virtual string URL { get; set; }
[DisplayName("Contact Name")]
public virtual string ContactName { get; set; }
public virtual string Address { get; set; }
public virtual string City { get; set; }
[DisplayName("Post Code")]
public virtual string PostCode { get; set; }
public virtual string Website { get; set; }
public virtual string Phone { get; set; }
[EmailAddress(ErrorMessage = "Please enter valid email")]
public virtual string Email { get; set; }
public virtual string About { get; set; }
public virtual string Image { get; set; }
}
How do you expect the Tags to be represented in the database? There is no native SQL Server type that EF maps to a List (it's more typical mapping is rows of a table to lists of objects) so you have to do some manipulation. If you want that to be a single cell (ie one field) then I would make it an nvarchar in the db and a string in code.
Then I would keep the list since it's easier to deal with in code and use the following LINQ query to transform the list into a comma separated list which will be a string, from there it can go into an nvarchar like any other string.
string csl = myList.Aggregate((c, n) => c + "," + n));
To go back to a list you just do;
List<string> = csl.Split(',').ToList();
Regardless of how you decide to represent your data it's not going to be as simple as just adding a list to a class. You'll have to put some thought into what makes the most sense for your application, this is just an idea to get you started since your question is a bit too vague to give a complete solution.
As evanmcdonnal said, EF can't map a plain list of strings to a database. A simple solution would be to create a Tag entity:
public class Tag
{
public Place Place { get; set; }
public string Name { get; set; }
}
Then in your place entity, use a list of Tags:
public List<Tag> Tags { get; set; }
//just a helper, not required
public void AddTag(string tagName)
{
Tag tag = new Tag { Name = tagName };
Tags.Add(tag);
}
The result will be a new Tags table in your database that contains a foreign key back to the Places table.
I'm currently developing a Forum (Question / Answer) based application.
Using C# ASP.net MVC and MongoDB for data storage.
I'm currently looking at the model.
I was thinking of having separate classes like this: (simplified)
public class Question
{
public string ID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public List<string> Tags { get; set; }
public DateTime DateCreated { get; set; }
public string ForumID { get; set; }
}
Answer
public class Answer
{
public string ID { get; set; }
public string QuestionID { get; set; }
public string Body { get; set; }
public DateTime DateCreated { get; set; }
}
My questions is:
How to handle the "replies"
Am I best of having (as in the model above) two separate "entities"
Or should I have a List of Answer in my Question model?
Some requirements are that i'll need to be able to display a count of answers etc...
With this being stored in a NoSQL db, I'm aware I should denormalize things, but how can I insert an answer, without retrieving the entire post? Is that sort of operation possible using NoRM with MongoDB?
Normally in MongoDB, you would embed the answers inside the question. 99% of the time you're going to query by Question, so you might as well get the Answers at the same time.
Some requirements are that i'll need to be able to display a count of answer...
If you're bringing back the answers with the questions, this is really easy. You'll have an array/list/collection with answers. So you'll just grab the length.
but how can I insert an answer, without retrieving the entire post
MongoDB supports an atomic "$push" operation. That means that you can add an item to an array without actually loading the document from the client. From the javascript shell, it would look like this:
db.questions.update( {_id : your_id}, { $push : { answers : your_answer_object } } );
So MongoDB is capable of this. You'll have to check with the NoRM drivers to ensure that they actually allow for this type of behavior (they're really missing something if they don't support $push).
Answer should be part of the question.
public class Question
{
public string ID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public List<string> Tags { get; set; }
public DateTime DateCreated { get; set; }
public string ForumID { get; set; }
public List<Answers> Answers { get; set; }
}
Because of the lack of joins document databases encourage you to store instances of the entire graph in one document.
I an developing a page where users will be able to add and modify existing content, its not a wiki per sé but sort of, like SO's editing abilities.
I am working with EF4 and the new Code First approach in the latest CTP, so what would be the best class design for this?
my current guess is something like this:
public class VersionableText
{
public int Id { get; set; }
public DateTime Date{ get; set; }
public String Text{ get; set; }
public virtual User User{ get; set; }
}
and then use it in my other entities, in a SO context it could be something like this
public class Question
{
public int Id {get; set;}
public virtual VersionableText Title {get; set;}
public virtual VersionableText Content{get; set;}
...
}
But I'm not really convinced by it.. since I am also going to have tags, ability to delete/undelete posts, rollback, etc. Do you know how to properly design classes that help me version the content properly?
Aim for simplicity
The main question that you need to ask yourself is Are you going to show all versions all the time or the latest version most of the time and all of them on request? Similar to here. Most of the time you only see the latest version.
If this is the same with our case I wouldn't care so much about these versions. But when you'd want to show them all on one page class design more or less depends on the way that you'd like to show it. Is it going to be showing changes and things like that.
I'd rather have a class like:
public class Question
{
public int Id { get; set; }
public QuestionStatus Status { get; set; }
}
public class QuestionHistory
{
public Question Question { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public User Author { get; set; }
public DateTime Created { get; set; }
public IList<Tag> Tags { get; set; }
}
And when I'd display all of them I'd just return a list of these ordered by LastChange. I've added tags list but I didn't add any of the other process-related properties related to question state. It hugely depends on the process sequence.