How to design database with user editable/versionable content using Code First? - c#

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.

Related

Deriving a database model for view model in ASP.MVC

I am creating some view models for my ASP MVC web app.
I created "code first" models for database. Is it a good way to derive view models from database models?
Example database model:
public class Project
{
[Key]
public int Id { get; set; }
public int? CustomerId { get; set; }
public int TypeId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
View model:
public class ViewModelProject : Project
{
[NotMapped]
public DateTime? Start { get; set; }
[NotMapped]
public DateTime? End { get; set; }
[NotMapped]
public string Manager { get; set; }
}
Is this the right way or is it completely false?
EDIT (subquestion):
I have some very simple database models like ProjectType, which only contains i.e. two properties. Should I also fragment those models in model view or can I make it that way:
Simple database model:
public class ProjectType
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int? Code { get; set; }
}
Can I use it like so:
public class ProjectVM
{
public string Name { get; set; }
public int Number { get; set; }
public ProjectType Type { get; set; }
}
Or does it have to be fragmented like so:
public class ProjectVM
{
public string Name { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int TypeCode { get; set; }
}
I would not recommend doing it this way. I (and many others) have tried it and it doesn't work well. You will inadvertedly run into troubles, since an MVC model has to be tailored to the view and what you get from the DB rarely fits. Sure, you can hammer it into place, but the code quickly gets messy and store-related and UI code starts to mangle together. This even shows in your example, since you have to put the NotMappedAttribute (which is related to data storage), to ViewModelProject (a class at UI level).
There are many other examples to show this problem, but an especially good one I find when you want to serialize a model object to JSON and send it to a JavaScript client. The JSON serializer takes the values of all public properties and adds them to the JSON. If you want to exclude a property, you have to mark it with a ScriptIgnoreAttribute, which you would also have to apply to the base class, which breaks separation between UI and store-related code.
The better way to go is to keep the staorage model and the MVC model separated and to map the data from one to the other (there are already pre-existing frameworks that help you with that, such as Automapper). This comes with additional advantages, for example better testability, since you are now not dependent on a specific data store to create model instances.

CRUD Operations on Grandchild using MVC

This is my first question on this community, I hope someone can help.
I have 3 models: Parent, child and grandchild. Resource, Parameter and Metric. I create the controlers with entity framework and it generated all CRUD operations. My problem its basically the grandchild. When I go to create a new Metric (grandchild), I have the dropdownlist of all the Parameters (child), but I want first to choose the Resource, then that will list all the parameters from that resource so I can create the metric. I'm new on asp MVC and this might be a lil bit basic question but I couldn't find anything like this. Thanks
Here are my classes just in case.
public class Resource
{
public int ResourceID { get; set; }
public string Name { get; set; }
public virtual ICollection<Parameter> Parameters { get; set; }
}
public class Parameter
{
public int ParameterID { get; set; }
public string Name { get; set; }
public int ResourceID { get; set; }
public virtual Resource Resource { get; set; }
public virtual ICollection<Metric> Metrics { get; set; }
}
public class Metric
{
public int MetricID { get; set; }
public string Name { get; set; }
public int ParameterID { get; set; }
public virtual Parameter Parameter { get; set; }
}
What you are trying to do sounds like either a linked or cascading drop down list. I did a quick search and found this link that may help you.
You will need some JavaScript that listens to the selection changed event for the first list (parent), and then updates the second list (child) with the available values. Depending on your situation, you may then need to link the third list (grandchild) to load when the second list has its selection changed as well.

Dynamic Create View with Multiple Nested IEnumerable Objects

Ok, I have 3 models. WorkoutViewModel has a one to many relationship with WorkoutExerciseViewModel. WorkoutExerciseViewModel has a one to many relationship with ExerciseSetViewModel. I need a dynamic “Create View”, that will allow me dynamically add Exercises to Workouts, and Sets to Exercises. I then want to save a Workout including all exercise and set records back to the database. I just need to validate that there is at least 1 exercise for the workout created and at least 1 set for the exercise created. Ultimately I just need to push a Workout View Model back to the controller with all of the populated nested IEnumberable objects present. Can anyone point me in the right direction?
public class WorkoutViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtal IEnumerable<WorkoutExerciseViewModel> WorkoutExercises { get; set;}
}
public class WorkoutExerciseViewModel
{
public int Id { get; set; }
public int WorkoutId { get; set; }
public int ExerciseId { get; set; }
public virtual ExerciseViewModel Exercise { get; set; }
public virtual IEnumerable<ExerciseSetViewModel> ExerciseSets { get; set; }
public string ExerciseFullname
{
get
{
return Exercise.Equipment.Name + " " + Exercise.Name;
}
}
}
public class ExerciseSetViewModel
{
public int Id { get; set; }
public int WorkoutExerciseId { get; set; }
public int Set { get; set; }
public int Reps { get; set; }
public int Weight { get; set; }
public string WeightValueType { get; set; }
}
There's really more to this than can reasonably be discussed in a StackOverflow answer, but I'll give you enough to start with.
As far as adding new exercises and sets within those exercises go, that's just JavaScript. You'll need to have some button that the user can click to add a new one, and tie the click event on that button to a handler that will add the appropriate HTML (form fields and such) to the page. There's many different ways to go about doing that, some more difficult than others. Most likely you want to look into some JavaScript templating library or a more full stack JS library like Knockout to make things easier. The only other thing to keep in mind is the conventions the modelbinder uses to wire everything from the post body to an instance of your model. For collections, it expects fields to have name attributes in the form of CollectionPropertyName[N].PropertyBeingEdited, where N is the position within the collection. So, the name attribute for ExerciseFullName for the first exercise would be WorkoutExercises[0].ExerciseFullName.
Your post action would simply take your same main view model:
[HttpPost]
public ActionResult Create(WorkoutViewModel model)
{
...
}
As long as you follow the property naming conventions for all the fields in your form, the modelbinder will happily wire everything from the post body onto your WorkoutViewModel instance.

Where did the overload of DbQuery.Include() go that takes a lambda?

I just declared some code-first models for a new project that uses EntityFramework.
public class BlogEntry
{
public long Id { get; set; }
public long AuthorId { get; set; }
public DateTime PublishedStamp { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public virtual User Author { get; set; }
}
public class User
{
public long Id { get; set; }
public string Email { get; set; }
// ...
}
class BlogDb : DbContext
{
public DbSet<BlogEntry> Entries { get; set; }
public DbSet<User> Users { get; set; }
}
Now suppose I want to retrieve the 10 most recent blog entries:
var entries = new BlogDb().Entries.OrderByDescending(...).Take(10).ToList();
The problem now is that accessing entry.Author will cause another database query. You wouldn’t want a separate such query for every blog entry. Now, it is my understanding that the purpose of Include is exactly this case, so I can say:
var entries = new BlogDb().Entries.Include(e => e.Author).(...).ToList();
However, that method doesn’t seem to exist. There is only an Include(string), like this:
var entries = new BlogDb().Entries.Include("Author").(...).ToList();
but this is annoying because it’s not compile-time checked and will be missed by the rename refactoring. Surely the version with the lambda is the “correct” approach.
Where did that method go? Is it no longer included in EntityFramework?
(I know that I can write an extension method for myself to achieve this, so you don’t have to. I’d just like to know whether I’m missing something.)
using System.Data.Entity;
It's in EF v4.1 and above, but you need a reference as it is an extension method.
Edit (thanks to #EastonJamesHarvey)
If using EF Core the import should be:
using Microsoft.EntityFrameworkCore;

Modelling a NoSQL Forum Application with C# / ASP.net MVC

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.

Categories