I have a Task entity with various navigation properties, including a Comment entity:
public Comment {
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastEditedDate { get; set; }
public virtual User User { get; set; }
}
A comment can be edited, and I want to keep a history of all such changes. Think of the way Stackoverflow allows you to edit your questions/comments, but keeps a history of changes.
Keeping a history means more complexity, and it becomes harder to maintain. My options:
add properties such as public virtual ICollection<string> DescriptionHistory { get; set; }, and a similar one for Title, User, EditDate, etc. It gets out of hand very quickly.
keep the Title, Description, etc. properties as strings, but make them CSV strings and stuff all those changes into a fewer set of properties. This means more processing, but the entity is simpler. The problem is that it becomes tricky to associate one CSV fragment with the corresponding one from a different property, eg a historical title must match its historical description and date.
do both, have the current set of properties, and another single set of nullables like TitleHistory, DescriptionHistory, etc., which are CSV strings of older versions, and so it only gets complicated when you are dealing with the historical stuff.
Also there are problems around the storage of the user, unless I use a CSV of IDs rather than the entities.
What is the best approach to this problem? There are various techniques such as sprocs and "insert only" tables - but I am using Entity Framework 5, and so prefer a solution which leverages the technology I am already using.
I've settled on using an "insert only" table. Pity it's not easy to do with EF5.
Related
I'm working on a side project to create a Forum built on top of RavenDB. I am currently trying to work out the relationship between Authors of Topics, and the "Last Reply" user on a topic. In a typical relational model I would simply store the FK to the User who posted the topic, and have a join off to the replies table to get the most recent replies author. This is obviously not the use case for Raven or any Document store for that matter.
What would be the most "optimal" way of pulling this off? Currently I'm tossing around a couple ideas.
Idea 1:
Save the FK of the Author on the Topic model, add a JsonIgnored User object that I will populate on the Topic load by using an Include in my session load (so one request so far from the client side, just makes the Load itself and model a bit complicated). Then possibly using a map-reduce index to get the most recent replies author (or even the same method as getting the Topic Author, so 1 or 2 queries depending).
Idea 2:
Saving both the Author and the most recent reply User on the model. Main "problem" here is the potential for stale data (say if a username changes). However that could potentially be alleviated with a background task (or simply keeping that in mind when updating a user document and going back over all posts from a user).
Example of the models in question.
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
public string PasswordHash { get; set; }
}
public class Topic
{
public string Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
// Idea 1 Relationships
public string AuthorId { get; set; }
[JsonIgnore]
public User Author { get; set; } // Would need to be populated on loads from an Include on AuthorId
public string MostRecentReplyUserId { get; set; }
[JsonIgnore]
public User MostRecentReplyUser { get; set; } // Same as Author
// Idea 2 Relationships
public User Author { get; set; }
public User MostRecentReplyUser { get; set; }
}
Note: I would likely add a method to the User model to return a "clean" version where I scrub out things like the PasswordHash and use that on the Save for Idea 2.
Depending on your needs in case of update and query performance both ways may be the better choice.
I personally would recommend the first idea because you don't need to update existing documents when some data changes on the user records. Using include on query/load time is quite a nice feature of ravendb which can help you when retrieving nested records from the database.
Just make sure that you don't forget to include all nested documents - otherwise you may get many roundtrips.
Embedding documents (like Idea 1 but with a stored value of the users) may be better if your data processing is seperated from the data retrieval and you don't have access to the database session when converting data to be handed out to the frontend. We're using such a system that heavily relies on that (getting one input and mapping out a json pendant of the value) - this seperates data retrieval logic completely from the output (like mapping to json) logic.
Downside here: You've to make sure that existing (embedded) data get's updated whenever a user changes and the data that is transferred over the wire is more than on idea 1.
I would like to get some ideas about how retrieve data from a MSSQL database with the constraints for all the columns. I'm listing all the databases on a server, and let the user choose the database, and after that, let them choose a table to CRUD against. This is going to be shown in a javascript grid (Slickgrid) for inline editing.
It's going to be very close to what you get when you rightclick a table in MSSQL Management Studio and select Edit top 200
Chalenges:
The application should access a bunch of different databases that often change, so generating POCOs is out of the question. The databases are also very often porly made and does not contain FKs as they should.
I do need to have some server side validation, and preferably client side as well for all the columns, so I do need to get the information about datatype, nvarchar length, nullable or not and so on from the DB.
I'm used to program in C# with EF/ADO.NET, but i would not mind trying some other languages like Node.js, if there are any good support for what I'm after (Not interested in PHP though).
I was thinking about using ASP.NET MVC with ADO.NET and read the data into some models like this:
public class GridVM
{
public IEnumerable<Column> ColumnDefinitions { get; set; }
public IEnumerable<dynamic> Rows { get; set; }
}
public class Column
{
public string Name { get; set; }
public string Type { get; set; } //perhaps public Type Type?
public bool Nullable { get; set; }
public int MaxLength { get; set; }
}
And then creating a list of dynamics with the number of properties corresponding with the number of entries in the ColumnDefinitions.
dynamic
{
public object column1 { get; set; }
public object column2 { get; set; }
public object column3 { get; set; }
//etc, so that I get properties for all the columns
}
I do have some code for binding retrieved data from a DataReader to a data model, ignoring the property names not corresponding with the column names, but I need to do it without having a known data model.
The questions:
Is this a good approach or should i reconsider using some other technique or method? Are there any pitfalls with this approach that I'm not seeing right now?
I'm trying to make a discussion forum in ASP.NET MVC 5 (mostly as a test as I'm pretty new to C#/MVC/any coding).
I have two classes, Discussion and Message.
public class Discussion
{
public int DiscussionID { get; set; }
[Required]
[Display(Name="Discussion Title")]
public string DiscussionTitle { get; set; }
//[Required]
//ForumUser UserCreatedThread { get; set; }
[Required]
DateTime CreatedTime { get; set; }
public ICollection<Message> Messages { get; set; }
}
and
public class Message
{
public int MessageID { get; set; }
public int MessageNumber { get; set; }
[Required]
[Display(Name="Message Content")]
[DataType(DataType.MultilineText), AllowHtml]
public string Content { get; set; }
[Required]
public DateTime MessageTime { get; set; }
public virtual int DiscussionID { get; set; }
}
Discussion has a list of Messages which I would like the MessageID, or another property to be ordered 1,2,3,etc for each list. Currently if more than one discussion is created the MessageID can be 1,4,5,8,etc.
I started using a static method in the GetMessage() and GetDiscussion() methods to fix the IDs as the messages were returned to the controller by ordering the threads by the MessageID then iterating over them to change the values to 1,2,3,etc.
Now I've changed that to add a MessageNumber property, save the message to the database when a new message is created (using EF6), retrieve the message, get the count of messages, update MessageNumber to the count and save the changes.
The latter works but I assume could cause problems if a new message is created between saving the message then retrieving the count. Two messages would have the same MessageNumber.
Is there a better way to do this or a should I use a different way to access the messages that doesn't need the ID?
The id is just the key for the table; it's not really intended to be part of the UI, even though you commonly see ids floating around in URLs across the web. It's far better to expose and use something like a slug for user-facing scenarios.
Regardless, though, what you're trying to do is really not possible. The id is typically set as an identity column, and is auto-incremented for each row in the table. Even if you don't rely on auto-increment and set it manually, you still need to ensure a unique value for each one (i.e., you can't repeat id 1 for multiple rows. The only way around this would be to create a composite key utilizing a manually set id and something like the foreign key to the Discussions table, but that's really, really, not a good thing to do. Please, don't do that. Not only would any good DBA smack you for using a foreign key as part of a composite key for another table, but then you have a ton manual work to do each time you want to save a new message.
My best advice is to just not worry about the id. If you want a consecutive number, you can create another property much like the MessageNumber property you have already and put anything you want in that as long as it's not a key or index for the table. That means you can't (or at least shouldn't) actually retrieve anything using that field. You would still need to lookup by the actual row id, or something like a slug, as mentioned earlier.
I'm not sure why you want to do what you want to do, but if your implementation of Messages is a List, then you can use an index and add one to it. An indexes are by nature consecutive numbers. You would do that something like this:
int index = Messages.FindIndex(message => message.MessageID = theID);
If you want something a little more flexible:
Messages.Select((m, index) => new { index, Message = m })
.Single(message => message.MessageID == theID);
I need to track a change history of some database objects in a MVC .NET application using the code first approach.
Here is what is meant by history table:
http://database-programmer.blogspot.de/2008/07/history-tables.html
I would use a history table for it, if I would write the SQL queries myself. But in the code first approach the SQL is generated... and I would like to stick to this paradigm.
The goal is a structure that holds all "old" revisions of changed/deleted entries together with some additional information (e.g. timestamp, user who changed it, ...)
Any ideas?
Regards,
Stefan
To be more specific - here is some code example:
public class Node {
public int NodeID { get; set; }
public string? data { get; set; } // sample data
}
public class NodeHistory {
public int NodeID { get; set; }
public string? data { get; set; }
public int UserID { get; set; }
public DataTime timestamp { get; set; }
}
What I need is some "framework" assistance to be able to add an entry to NodeHistory whenever a change is -persisted- to table the Node structure.
That means: Just overriding the set-method isn't a solution, as it would also create an entry, if the change to a "Node" is not persisted at the end (e.g. roleback).
I think the best approach for me would be to use a repository pattern and do the insertion into the NodeHistory table on every operation on the Node object that you see fit to keep a history of.
EDIT: Some code
public class NodeRepository{
public Node EditNode(Node toEdit, int userId){
using(new TransactionScope())
{
//Edit Node in NodeContext like you would anyway without repository
NodeContext.NodeHistories.Add(new NodeHistory(){//initialise NodeHistory stuff here)
NodeContext.SaveChagnes();
}
}
}
public class NodeContext:DbContext{
public DbSet<Node> Nodes{get;set;}
public DbSet<NodeHistory> NodeHistories{get;set;}
}
If you are looking for something simpler than this, then I have no idea what it might be.
This is really something you should do with a trigger. Yes, you have to write some sql for it, but then history is updated no matter how the update occurs, either manually, or through some other means.
I have something that looks like the following document structure:
public class Document {
public int Id { get; set; }
public string Name { get; set; }
public List<Property> Properties { get; set; }
}
public class Property {
public int Id { get; set; }
public string Name { get; set; }
}
Now, querying and modifying Documents is easy. But I need to access specific Property-instances in my app, and it seems that they won't automatically get an ID like the root document does. And it seems this is by design in RavenDB.
I might be me stuck in the relational world, but what I'd like to do is basically retrieve the correct document, then get the right property, modify it and save the document again.
from property in document.Properties
where property.Id == someId
select property
...which will obviously not work as long as
RavenDB does not auto-set the Id field or
I don't make any ID-generating mechanism myself
Am I heading completely the wrong way, or does what I'm trying to do mak sense? Should I move the Properties out to being a root node and make some sort of reference to them in Document? Or should I just do something like this when inserting properties:
Retrieve the document with the list of properties
Get Properties[last]'s ID
Add 1 and insert new ID myself in new properties
?
This would, however, require at least two requests (one to get existing properties, one to save the changes) to the database, which just seems dirty and unnecessarsy for such a seemingly simple task.
I've found a lot of sortof similar posts, but none of them really answers this AFAIK.
Check to see how we do that in RaccoonBlog:
https://github.com/ayende/RaccoonBlog/blob/master/RaccoonBlog.Web/Infrastructure/Tasks/AddCommentTask.cs