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.
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'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);
Can anyone provide an easier more automatic way of doing this?
I have the following save method for a FilterComboTemplate model. The data has been converted from json to a c# model entity by the webapi.
So I don't create duplicate entries in the DeviceProperty table I have to go through each filter in turn and retrieve the assigned DeviceFilterProperty from the context and override the object in the filter. See the code below.
I have all the object Id's if they already exist so it seems like this should be handled automatically but perhaps that's just wishful thinking.
public void Save(FilterComboTemplate comboTemplate)
{
// Set the Device Properties so we don't create dupes
foreach (var filter in comboTemplate.Filters)
{
filter.DeviceProperty = context.DeviceFilterProperties.Find(filter.DeviceFilterProperty.DeviceFilterPropertyId);
}
context.FilterComboTemplates.Add(comboTemplate);
context.SaveChanges();
}
From here I'm going to have to check whether any of the filters exist too and then manually update them if they are different to what's in the database so as not to keep creating a whole new set after an edit of a FilterComboTemplate.
I'm finding myself writing a lot of this type of code. I've included the other model classes below for a bit of context.
public class FilterComboTemplate
{
public FilterComboTemplate()
{
Filters = new Collection<Filter>();
}
[Key]
public int FilterComboTemplateId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public ICollection<Filter> Filters { get; set; }
}
public class Filter
{
[Key]
public int FilterId { get; set; }
[Required]
public DeviceFilterProperty DeviceFilterProperty { get; set; }
[Required]
public bool Exclude { get; set; }
[Required]
public string Data1 { get; set; }
}
public class DeviceFilterProperty
{
[Key]
public int DeviceFilterPropertyId { get; set; }
[Required]
public string Name { get; set; }
}
Judging from some similar questions on SO, it does not seem something EF does automatically...
It's probably not a massive cut on code but you could do something like this, an extension method on DbContext (or on your particular dataContext):
public static bool Exists<TEntity>(this MyDataContext context, int id)
{
// your code here, something similar to
return context.Set<TEntity>().Any(x => x.Id == id);
// or with reflection:
return context.Set<TEntity>().Any(x => {
var props = typeof(TEntity).GetProperties();
var myProp = props.First(y => y.GetCustomAttributes(typeof(Key), true).length > 0)
var objectId = myProp.GetValue(x)
return objectId == id;
});
}
This will check if an object with that key exists in the DbContext. Naturally a similar method can be created to actually return that entity as well.
There are two "returns" in the code, just use the one you prefer. The former will force you to have all entities inherit from an "Entity" object with an Id Property (which is not necessarily a bad thing, but I can see the pain in this... you will also need to force the TEntity param: where TEntity : Entity or similar).
Take the "reflection" solution with a pinch of salt, first of all the performance may be a problem, second of all I don't have VS running up now, so I don't even know if it compiles ok, let alone work!
Let me know if that works :)
It seems that you have some common operations for parameters after it's bound from request.
You may consider to write custom parameter bindings to reuse the code. HongMei's blog is a good start point: http://blogs.msdn.com/b/hongmeig1/archive/2012/09/28/how-to-customize-parameter-binding.aspx
You may use the code in Scenario 2 to get the formatter binding to deserialize the model from body and perform the operations your want after that.
See the final step in the blog to specify the parameter type you want customize.
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
I am get confused about voting system implementation. I want to know vote up and vote down counts of a Post and also save voters who voted up or down. I use below model for it.
public class Post : Entity
{
public string Title { get; set; }
public virtual User Owner { get; set; }
public virtual List<User> UpVoters { get; set; }
public virtual List<User> DownVoters { get; set; }
}
I feel a bit of problem in this design because if i have to represent vote up and vote down count of a post so i think i have to process 2 query.Using navigation properties counts would cause performance problems if i just need counts. Am i right?
Second aproach below get confuse me too because VoteUpCount and VoteDownCount should be handled always manualy by developer. And always reupdate values when voter changed his vote and this looks bit of code smell.
public class Post : Entity
{
public string Title { get; set; }
public virtual User Owner { get; set; }
public int VoteUpCount { get; set; }
public int VoteDownCount { get; set; }
public virtual List<User> UpVoters { get; set; }
public virtual List<User> DownVoters { get; set; }
}
Can you suggest me which is better ? And why better ? Do i have another alternatives ?
Lastyly a post hold 3 User navigation properties and this makes me feel that something can be wrong. Is there anything wrong in that relationship ??
Instinctively, I would create a Vote entity, since you want save information on votes :
public class Vote : Entity
{
public User User {get;set;}
public int VoteDirection {get;set;} // 1 or -1
// any other info...
}
Then add the Vote field in the Post class :
public class Post : Entity
{
public string Title { get; set; }
public virtual User Owner { get; set; }
public virtual List<Vote> Votes { get; set; }
}
Then you count the sum of VoteDirection for each Votes...
I don't think anything is wrong here.
Your first model looks pretty OK to me. I wouldn't go for the second one unless you can prove that there is a performance hit for getting a count of related entities. Premature optimisation is the root of all evil ;-).
If you need to store the count for performance reasons than the second model is fine, too. Just make sure that adding the vote updates the count field.
Your first model is the right one, you should not compromise your model for the short-comings of EF, also the second version is not very maintainable - there is too much thinking and manual effort involved.
There is actually a workaround for EF materializing all related entities when you want to retrieve the count, the syntax is not very intuitive but does result in the right SQL query:
var myPost = context.Posts.First();
int upvotersCount = context.Entry(myPost)
.Collection(p => p.UpVoters)
.Query()
.Count();
This approach is detailed here.
Also as a general rule of thumb you should use ICollection<User> instead of the concrete List<User> in your model.
Using navigation properties counts would cause performance problems if i just need counts. Am i right? - what kind of performance problems do you have in mind?
Generally, adding the count fields will introduce redundancy in your database, but is an obvious performance optimization step. If I were you I would rather ask myself if I need to improve performance in this case. How many posts and votes are there going to be in the database? How many users are going to access this information simultaneously? Etc. In case your application needs to be really scalable, you can add the extra counts and make sure they are updated correctly. Otherwise, you can just keep this possibility in mind but not be in a hurry to implement it.