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.
Related
I have the following xml file
<Questions>
<QuestionId=1>
Which is a reserved word in the Java programming language?
<Option1>
method
</Option1>
<Option2>
native
</Option2>
<Option3>
subclass
</Option3>
<Option4>
reference
</Option4>
<Answer>
reference
</Answer>
</Question>
</Questions>
I need to create a table in the database where the value of Question becomes the column name and the Answers come in the row using C# and ASP.NET
Any help would be appreciated.
This is the format expected.
|A_ID||Which is a reserved word in the Java programming language?||Q2 |
________________________________________________________________________
|01 ||Reference ||A2 |
If I understand correct, you need 2 tables. 1 for the questions, and 1 for the answers. For simplicity I'm thinking of:
public class Question
{
public int Id { get; set; }
public string Question { get; set; }
//relations
public List<Answer> Answers { get; set; }
}
public class Answer
{
public int Id { get; set; }
public string Answer { get; set; }
public bool Correct { get; set; }
public int QuestionId { get; set; }
//relations
public Question Question { get; set; }
}
Then with entityframework you can create migrations and your context and create your questions etc.
Makes any sense?
if your new to entity framework: check out: https://learn.microsoft.com/en-us/ef/core/get-started/aspnetcore/new-db
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
}
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.
I am learning how to use MVC right now and I just have a question on when I am creating and updating entries in the database. I was reading a post from this page: asp.mvc 4 EF ActionResult Edit with not all fields in view
The guy in it said to create a model that will be used, so is the efficient way to insert a new row and update an existing row by having two models with different properties?
So my models would look like this -
public class UserModelView
{
public string FirstName { get; set; }
public string Surname { get; set; }
public DateTime AccountCreated { get; set; }
public DateTime? LastLoggedIn { get; set; }
}
public class UserModelCreate
{
[Key]
public int UserId { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public DateTime AccountCreated { get; set; }
}
public class UserModelUpdate
{
public string FirstName { get; set; }
public string Surname { get; set; }
public DateTime? LastLoggedIn { get; set; }
}
Is this the best way to do what I need to do?
Im guessing you were previously using the entity class when binding your model back in.
You shouldn't do that!
The guy in the post is right, this is a much better way of controlling your entity and model information and provides a layer of seperation between the two.
After all you wouldnt want a user being able to directly manipulate an entity via a HTTP request.
I answered something similar here
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.