Audit trail for specific controls in presentation layer - c#

I have a project using ASP.NET with a MS SQL DB, using LINQ classes and require change tracking / audit trail for specific fields. Since the project is quite big, adding the audit-trail functionality manually would be cumbersome. Also, tracking ALL changes on the data-layer would not be perfect (since I also have to handle external data, coming from JSON files sent by HTTP POST, and more ...).
My idea is to implement the usual audit-trail functionality (which is discussed and shown in various other questions/tutorials and should not be the issue here) by extending the existing ASP.NET controls (such as asp:TextBox, asp:CheckBox, asp:DropDownList, etc) with a simple property "bool auditTrail", which can be set to TRUE in my .aspx-file and then tracks the changes in code-behind. This functionality would simply speaking just store the origin page, element #ID, old + new value, logged user and timestamp.
Can this be done - more or less easily? To be honest I am quite firm in ASP.NET but not-so-firm when it comes to extending existing classes. Or maybe the whole idea has a big flaw and you can point me to the right direction? I found solutions using NHibernate, but, if possible, I would prefer to avoid external dependencies.
Thanks for your input!
To clarify what I would like, some (pseudo)code. Of course the tagprefix of the controls needs to be set accordingly etc (since I don't think that I can override the asp:WebControl classes themselves ... can I?)
page.aspx
<asp:TextBox runat="server" ID="tb1" audit="true"></asp:TextBox>
<asp:DropDownList runat="server" ID="ddl1" audit="true"></asp:DropDownList>
page.aspx.cs
no further code regarding the 2 controls...
AuditControls.cs
public class TextBox : System.Web.UI.WebControls.TextBox
{
public bool Audit { get; set; }
}
public class DropDownList : System.Web.UI.WebControls.DropDownList
{
public bool Audit { get; set; }
}
So long, that's the raw outline of what I would like to have in my application. What's missing is how I could - in a solution as generic as possible - add the functionality for the audit trail itself. Storing the value when the Control is rendered (/filled with data), and also retrieving the value history of the Control in order to maybe show it in another (popup-) element. Maybe it's even a good idea to load the data in a seperate element:
page.aspx
<asp:TextBox runat="server" ID="tb1" audit="true"></asp:TextBox>
<asp:MyHistoryViewer runat="server" ID="history_tb1" TargetID="tb1" />
AuditControls.cs
public class MyHistoryViewer
{
public string TargetID {get;set;}
// program logic to retrieve the value history of the control with ID=TargetID from DB
// render it as a, let's say HTML table...
}
And finally, in order to store all changed values, some sort of "invisible" ChangeTracker could be sitting on the .aspx page, having his eye on all my Controls with property audit set to true. This Changetracker will fire once the page gets a Postback, checking if the current WebControl field values (be it TextBox/DropDownList/...) is different to the latest value. If not, it writes the data to the DB...?
Thinking out loud - any suggestions or hints, do you have experience with this? Any better way to do things?

Also "Thinking out loud"... I'm thinking that the controls you suggested in your question are server controls. this means that on selecting/clicking they will produce a postback. #konrad_pe can I suggest that perhaps a HttpModule would be a good idea to tackle your problem? since HttpModules are hit for every request you could query the form data for the control that caused the request and take it from there.
I read similar here:
Get which control...
I hope this gives you another point of view and you find it interesting enough to explore. Good luck!

Related

How to get Label inner text from Codebehind

I have html label contol without runat="server"
Does it possible to get inner text from code behind c#?
Label:
<label id="lblClanName">Text Here</label>
Thanks
Every time an ASP.Net page is posted back to the server it is recreated from scratch using the custom code contained in the page (such as calls to a database), the HTTP post/get collections (which include ViewState), any custom data in Application, Cache, Session, static objects, etc.
If the value does not exist in any of those locations, the server doesn't have access to it. A common trick to pass data from the client is to simply use a hidden field. If you want something more elegant, you can use asynchronous AJAX to send/receive data from the server.
Or in this case, you could just add runat="server" to an asp:Label. ViewState will maintain the value between postbacks, though it will not reflect changes made client-side unless (once again) the data is somehow passed back to the server.
Note that ViewState is typically a bad thing because it essentially doubles the size of your data (or more) and (in my opinion) encourages sloppy design.
i don't think you can do it.either you can use js get the lable,and call js method from code behind
Short answer: no.
To access this from your code-behind, you will minimally need to add runat="server" to your label. This will allow you to access it using Page.FindControl(String).
The preferred approach, if you are able to modify the front-end code, would be to use an <asp:Label />. This will allow you easy access by just using the control's ID in the code-behind, specifically its Text property.
Do you want to know how to parse a string value for the inner html, or do you expect your web page do have text written to the label at runtime?
string labelHtml = "<label id="lblClanName">Text Here</label>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(labelHtml);
string innerText = doc.DocumentElement.InnerText;
Why do you need the text between a label, is this for a live web page? This sound like a bad design more than a requirement.

Pop Up Window Data Variable Passing

I am creating a web application for my company that needs to deal with form processing and database manipulation. The application is implemented using .NET Framework 3.5 using C# and Visual Studio 2008 as the IDE and Microsoft SQL Server 2005 as the database.
Here is my problem:
I have a lot of forms
But my boss only want me to create a single page for the database
processing (easier to extend in the future)
I figured out the only way to solve this problem is by having only 1 .aspx file (that contains everything about the database) and having it invoked as a pop up window everytime a forms need to deal with the database.
Here is another problem of mine:
Due to stateless nature of HTTP, I am unable to process and pass
variable between 2 different windows.
I managed to create certain Javascript functions and have the
variable transferred on display, however it can only pass a variable
that is the primary key in the table. To process other columns in
the table is possible but as the consequence I have to write a very
long inline script in my .aspx page and after it is compiled people
can easily view how to access my company database easily. Hence, I
don't favor this (beside to deal with 1 form, I need to create a
long code already, imagine if i have more than 1000 forms!)
So there are two ways you guys can help me:
Suggest another way other than popping up a new window for my
problem, maybe even advise on how it's implemented.
If you think popping up is the solution, you mind to share some
snippets that can help me figure out the variable passing between
two different windows. I can use some advise especially from some
Javascript expert on this :).
Note: Solution must be workable in ASP.NET Framework 3.5 and tested using IE browser version: 8.
P.S: This is a short explanation about my application flow
Let's say I entered data about a product (it has few properties id, name, price, etc) into the database
Later on somehow I want to edit one or few properties of that product, so I have to launch a form which called "editor.aspx"
Instead of entering the product id (which is the primary key) into the form (and edit the data based on the entered product id) and risking to miscalculately edit the correct data, I provide a small button in the form (let's name it btSearch), that will launch a new popup window which contains the gridview of the database of all product (with selection enabled)
Now I just need to browse through the gridview, select a particular row, it will close the popup and I expect to see few data from that row appeared on my original page (in the textboxes/labels)
I hope my explanation above clears the air, thank you.
I recently wrote something like that: A database handler as aspx file. But i invoked it by using ajax / jquery.
When my aspx file is done, i write something to the response stream, some code, a json string, what ever.
Example:
$.post("yourdatabasehandler.aspx", { name: "John", lastname: "Smith" }, function(data) {
alert("Response from page: " + data);
});
In that example, name and lastname are values that are posted to your site. You can access them like that:
string name = Request.Params["name"]
// Do your database , validation and whatever logic here
Response.Write("Cool dude");
The above javascript will alert "Cool dude" after your databasehandler is done. Inside your javascript you can react to the response how ever you want - For example reload a page.
Hope that helps? Regards
"1. Due to stateless nature of HTTP, I am unable to process and pass
variable between 2 different windows."
You very wrong with this comment to start with, trying MSDN and ASP.Net "How to pass values between ASP.Net Web pages". Passing between Windows only requires a little bit more thought and possibly a little Javascript to refresh a parent windows or cause a postback on a shild window etc.
If you're using a popup window, you can always use QueryStrings to pass a value going to your popup.
window.open("popup_page.aspx?id=" + id + "&name=" + name)
to access it in popup_page.aspx
string sID = Request.QueryString("id");
string sName = Request.QueryString("name");
Update: if you're using IE the this might help you.
function ShowPopup(strMessage)
{
var returnValue= window.showModalDialog("popup_page.aspx");
}
popup_page.aspx
<asp:Button ID="btnReturnValue" runat="server" Text="Proceed" OnClientClick="window.returnValue='some message';window.close();" />
Note: Please note this only works in IE, so I suggest consider using the followings instead:
jQuery
AjaxControlToolkit ModalPopup
I personally suggest the use of jQuery. :)
Thank's for all answers, comments. rates, and feedback. Just now I found a very helpful link here. Basically the answer is based on that particular code. I just need to alter some part.
<script language="javascript">
function GetRowValue(val)
{
window.opener.document.getElementById("ctl00_ContentPlaceHolder1_TextBox2").value = val;
// make sure you change the TextBoxId as respective to your creation
window.close();
}
</script>
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1">
<Columns>
<!-- Reserve the code below, as after you configure data source you -->
<!-- will alter this code drastically therefore-->
<!--you have to make sure to paste this code -->
<!-- again inside this Gridview element once you configure your data source -->
<asp:TemplateField>
<AlternatingItemTemplate>
<asp:Button ID="btnSelect" runat="server" Text="Select" />
</AlternatingItemTemplate>
<ItemTemplate>
<asp:Button ID="btnSelect" runat="server" Text="Select" />
</ItemTemplate>
</asp:TemplateField>
<!-- This part must be reserved -->
</Columns>
Also remember to specify the connection string and the sql command in the datasource.
The rest just follow that tutorial and copy paste the code entirely.

ASP.Net: User control with content area, it's clearly possible but I need some details

I have seen two suggestions for my original question about whether it is possible to define a content area inside a user control and there are some helpful suggestions i.e.
Passing in content to ASP.NET user control
and
ASP.NET User Control inner content
Now, I like the theory of the latter better than the former just for aesthetic reasons. It seems to make more sense to me but the example given uses two variables content and templateContent that the answerer has not defined in their example code. Without these details I have found that the example does not work. I guess they are properties of the control? Or some such?
EDIT - DETAILS: What I am trying to do
I have need of an ASP.Net user control that conceals some content in a panel inside a placeholder and asks for the input of a code in a visible panel.
Essentially the user will put their code into the provided textbox in Panel A and submit it, it will be checked and, if it is valid, panel B and the locked content will be displayed.
I have done a test where the content was hard coded into panel B but as soon as I need to make the content a generic input it fails. If it were just text or somesuch then I could make it a property of the control, but as it is, in fact, another User Control I am having some difficulty getting this into the "hidden" panel.
Any other workable solutions are also welcome.
EDIT NOTE: The solution I'm trying to implement this in 2.0 I did find a 3.5 solution which I cannot use.
The former example seems workable but I'd prefer to go with the latter if someone could fill in the blanks for me.
Thanks.
Okay, so this is disturbingly easy but many of the tutorials on the web that talk about this kind of thing push to do extravagant things that require the control to parse ListItems or such.
So this solution is purely so that you can build a control that, for whatever reason, has a placeholder in it that could have anything inside it (kind of like a content area on a Master page). In this instance it happens to be because the Panel containing the placeholder is hidden until appropriate input actions have taken place in another panel.
First, you need to add this:
[ParseChildren(true,"Content")]
[PersistChildren(false)]
just above the part of the control which looks like this:
public partial class MyControl : System.Web.UI.UserControl
then in the control scoped declarations at the head of the control you want to declare thus:
private Control _content;
[PersistenceMode(PersistenceMode.InnerProperty)]
public Control Content { get { return _content; } set { _content = value; } }
Finally you need to place the content into the placeholder like this:
phContent.Controls.Add((Control)_content);
That last line goes into the Page_Init event. For reference "phContent" is the name of the place holder where you want the content to appear, like this:
<asp:Panel ID="pnlLockable" runat="server" Visible="False">
<asp:Placeholder runat="server" ID="phContent" />
</asp:Panel>
On the front end the resulting implementation looks like this:
<uc:MyControl runat="server" ID="lockit1">
<Content>
//...your stuff goes here...
</Content>
<uc:MyControl>
Note that I presume that what is inbetween the Content Tags is a root control. This is because I nested another user control in there. I imagine if you put whatever content you want within a panel or placeholder it should be fine.
Also you can read "How to: Create Templated ASP.NET User Controls". Really helpful.

MultiViewControl issues

I'm presently in the process of reworking a MultiViewControl based wizard process for our web application. I am having an rough time trying to make sense of the order that events are happening (Page_Load, Init, prerender, etc). Does anyone out there on the interwebs have details on dealing with one of these controls? Please don't just say 'google' it. I've done that and have yet to find a good, comprehensive site yet.
Admittedly, I haven't really elaborated on the problems I'm having with this control, so I'll try to do that:
Primary problem is the initialization of UserControls that live in different Views. In the existing codebase, the programmer was using a combination of multiviewcontrol.ActiveViewIndex = WHATEVER and Response.Redirect("PageWithMultiView.aspx?nextstep") and it made it all very convoluted. My task is to attempt to remove the Response.Redirect calls and use only the setting of the ActiveViewIndex. Is this even possible? Also, there are some cases where I need to initialize a control in a particular view only on the initial load and not on subsequent postbacks. I can use something like the IsPostBack flag but this is only ever set to false on the initial load. Subsequent reloads IsPostBack == true. I basically want to have IsPostBack set to false for the initial load of each View. Can this be done without doing a Response.Redirect to itself?
Hopefully this will make some sense to someone out there.
Thanks.
Greg.
I am having an rough time trying to
make sense of the order that events
are happening (Page_Load, Init,
prerender, etc).
Here you have all details about ASP.NET page lifecycle and events: http://msdn.microsoft.com/en-us/library/ms178472.aspx .
In terms of MultiView - you should NEVER use Response.Redirect when you work with MultiView.
If user can not switch to previous view then you can check previous ActiveViewIndex value before setting it to the new value, e.g.
if (mv.ActiveViewIndex != newIndex)
{
// this view is displayed for the first time
}
If user can switch to the previous views, I suggest to place an information about already used views in session or by placing hidden field on the form with ids of the views that have already been displayed and to use that information instead of IsPostBack.

Building Dynamic UI with C#?

I need to expose some input fields based on what properties I find for particular types in an assembly.
I'm not sure how common an approach like that is. Perhaps there are easier ways. Maybe on the client side instead of server.
If anyone knows of a good way of doing something like this, I would appreciate the help.
Create input controls accordingly and simple add control to some div container? I'm not sure if it would be more complex than that.
I'll need to somehow add css classes to the controls as I build them so they get placed nicely; that might get tricky.
This all sounds like standard asp.net development. Any good tutorial should be able to help you. For the asp server controls, you use the CssClass property to set the class for the control.
Here is the asp.net tutorial from the W3C Schools.
I assume you will use reflection to figure out what properties entity has, then you would based on the type of the property create an input field. You would have to dynamically create control to handle input in code behind. Make sure you give that control and id. You will have to recreate these controls on the post back. This looks to me like dynamic property editor. There might be some free ones, google for it.
If the UI doesn't have to be completely dynamic you could include all the controls in the markup with any optional ones set to Visible="false". Then, selectively enable the appropriate controls in your code-behind. For example:
Default.aspx
<asp:Button ID="EvenButton" runat="server" Text="Even" Visible="false" />
<asp:Button ID="OddButton" runat="server" Text="Odd" Visible="false" />
Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
String msg = "A message to count";
if (msg.Length % 2 == 0)
{
// Enable the Even Button
EvenButton.Visible = true;
}
else
{
OddButton.Visible = true;
}
}
The advantage of this method is that you can lay things out with the appropriate CSS easily in the markup. If, on the other hand, your UI is much more dynamic than this, you'll probably have to resort to dynamically creating controls in the code-behind and adding them to the page via calls to Controls.Add(). This way, however, is harder to layout. And you have to deal with things like re-wiring any event handlers on each postback.
Hope that helps.
I ended up leveraging jQuery.
I laid out a simple markup with the basic layout I would need.
For creating controls dynamically, I did it all in javascript using jQuery methods.
This of course requires that you return some data set to the UI intelligently enough to render it.

Categories

Resources