For about a few months i'm programming ASP C#. I always program a lot code in the events and in the load event i check the querystring for valid data. This is some sample code i have in one of my projects:
protected void Page_Load(object sender, EventArgs e)
{
if (Controller.Manual == null)
{
Response.Redirect("login.aspx");
}
lblLocation.Text = "<a href='viewdocument.aspx'>" + Controller.Manual.Title + "</a>";
if (Request.QueryString["gchap"] != null)
{
if (Controller.IsNumeric(Request.QueryString["gchap"].ToString()))
{
genchap = Convert.ToInt32(Request.QueryString["gchap"]);
FillGeneralList();
SetChapterTitle();
}
}
if (Request.QueryString["qchap"] != null)
{
if (Controller.IsNumeric(Request.QueryString["qchap"].ToString()))
{
qualchap = Convert.ToInt32(Request.QueryString["qchap"]);
FillQualityList();
SetChapterTitle();
}
}
// Check document Id is set (did)
if (Request.QueryString["did"] != null)
{
if (Controller.IsNumeric(Request.QueryString["did"].ToString()))
{
docId = Convert.ToInt32(Request.QueryString["did"]);
DetermineView();
}
}
}
I know there must be a way to accomplish this on a more neat way. And this is just the load event. On other events, like click and onchange events i have similar code. I think this is spaghetti code and not well-arranged. So can you tell me how i can arrange my code?
EDIT:
What i want to know is, is there a more neat way to, let's say, fill a listbox? And where do i check whether a querystring value has valid data? Where do i check whether the (input/querystring) data is a number? And where should you put the code that validates the Querystring? Also in the load event?
I feel your pain with some of the organization issues with ASP.NET websites. I've had similar code to yours on several projects.
If you have the choice of your frameworks you might look into ASP.NET MVC. This allows you to have clear separation between the View (Html), the Controllers (All actions and business logic) and the Model (database). That way you have zero code in your codebehind files, it all stays nice and neat in controllers.
Try using TryParse (for example) and you can simplify all the code that looks like
xx.IsNumeric(Request.QueryString["qchap"].ToString())
and
Convert.ToInt32(Request.QueryString["gchap"]);
and reduce the number of calls to Request.QueryString variables
You could try something like
Original code
if (Request.QueryString["gchap"] != null)
{
if (Controller.IsNumeric(Request.QueryString["gchap"].ToString()))
{
gchap = Convert.ToInt32(Request.QueryString["gchap"]);
FillGeneralList();
SetChapterTitle();
}
}
Suggestion
int? gchap; //nullable types thanks Richard :D
if (!int.TryParse(Request.QueryString["gchap"], out id)) {gchap = null};
if (gchap != null) {
FillGeneralList();
SetChapterTitle();
}
// you could make this neater with your own little method
Have a look at this post How do you test your Request.QueryString[] variables?
Try capturing the repetitive code in a separate function. (qchap / gchap)
e.g.:
qualchap = ConvertFillAndSet(Request.Querystring["qchap"]);
genchap = ConvertFillAndSet(Request.QueryString["gchap"]);
private int ConvertFillAndSet(string qrystring)
{
int numberToReturn = 0;
//if the conversion was ok -> true, else false
if (Int32.TryParse(qrystring,numberToReturn))
{
FillQualityList();
SetChapterTitle();
}
//returns 0 if tryparse didn't work
return numberToReturn;
}
Where to start. Unfortunately despite other comments, you're not really writing anything that is 'web forms' specific. So moving to MVC isn't going to magically make your code better.
1 Don't roll your own authentication: Use forms authentication unless you have a compelling reason not to. When using forms authentication, you don't need to write code on every page to check that you're logged in. The framework handles that for you.
2 Learn to use the server controls:
Also as others write you shouldn't be writing html in code, especially for something so trivial. Web forms doesn't make you do this either.
<!-- this is in MyPage.aspx -->
<asp:HyperLink id="viewLink" runat="server" />
// in the code-behind file MyPage.aspx.cs
viewLink.NavigateUrl = "~/viewdocument.aspx";
viewLink.Text = Controller.Title;
If you're going to stick with web forms, you need to get familiar with the ASP.Net Page life-cycle
3 Your code is in need of refactoring. No matter if it's web forms, php, or MVC. Here are some basic refactorings, and none of this is really .net specific. I'll walk through these in small steps.
// this may be a good candidate for an extension method
int? ConvertNullable(string nullableInt) {
if( string.IsNullOrEmpty(nullableInt) )
return null;
int value;
if( Int32.TryParse(nullableInt, out value) )
return value;
return null;
}
which then allows you to write.
int genchap? = ConvertNullable(Request.QueryString["gchap"]);
int qualchap? = ConvertNullable(Request.QueryString["qualchap"]);
int docId? = ConvertNullable(Request.QueryString["did"]);
FillQualityList(genchap,qualchap);
SetChapterTitle(genchap,qualchap);
DetermineView(docId);
but passing a lot of primitives around is a hassle and prone to errors, so sometimes we make a small class to encapsulate the data, in this case the page initialization information.
class ChapterView
{
public int? GenChapter {get; set;}
public int? QualChapter {get; set;}
public int? DocumentId {get; set;}
}
private ChapterView GetChapterView()
{
return new ChapterView
{
GenChapter = ConvertNullable(Request.QueryString["gchap"]),
QualChapter = ConvertNullable(Request.QueryString["qualchap"]),
DocumentId = ConvertNullable(Request.QueryString["did"])
}
}
Note that I've no idea what GenChap and QualChap are, but they're a bit terse and you could complete the refactoring to make them more readable in code. But even without better names, we now have more readable code.
ChapterView chapterView = GetChapterView();
FillQualityList(chapterView);
SetChapterTitle(chapterView);
DetermineView(chapterView);
And finally you may determine that you don't really need to call this every time the page executes to read from the query string. If you've read up on the Asp.Net Page LifeCycle you know that events may change GenChapter or something else that affects how the page is rendered. You may find it better to set up the view in the PreRender instead of calling FillQualityList over and over again.
ChapterView chapterView;
Page_Load()
{
if( !IsPostback )
{
ChapterView chapterView = GetChapterView();
}
else
{
chapterView = (ChapterView) ViewState["chapterview"];
}
}
NextChapter_Click()
{
chaperView.NextChapter();
}
Page_PreRender()
{
FillQualityList(chapterView);
SetChapterTitle(chapterView);
DetermineView(chapterView);}
// make sure class is marked [Serializable]
ViewState["chapterview"] = chapterView;
}
you should follow layered approach. ie: put all your data access code in data access layer, put all your business logic (which also includes validations) in your business layer, put all your model code in your business object layer
and finally for ui - try to never generate html mark up from within the code as far as possible. also, always create a root class for your aspx pages where it has common methods already implemented. then subclass this root class for every other aspx pages
if you are going to hardcode html markup within your c# code - i can assure you this would always result in a lot of chaos (based on my own experience)
but there are situations where you simply cant avoid it. for such cases - this is what i do - i get rid of the code behind and simply put that code in my aspx / ascx file itself. that way when i have to change my ui based on never ending client requests, i dont have to recompile my code - i simply replace my aspx / ascx files on the staging / production server.
you know how clients are : hmmm can u make the black strip look a bit like gray, can u increase the spacing between lines, can u change the text of this hyper link... requests like these never seem to end :-)
Related
I'm migrating a web app to .NET Core, and it uses a few Razor editor templates for specific input types (e.g. outputting a date input for any DateTime model properties).
A couple of the templates use the following method to get the ID attribute value, for use elsewhere within the HTML:
ViewData.TemplateInfo.GetFullHtmlFieldId("PropertyName")
However, this method no longer seems to exist in ASP.NET Core.
The GetFullHtmlFieldName method still exists, so it's possible to get the same result (at least for everything I've tested) by doing:
Regex.Replace(ViewData.TemplateInfo.GetFullHtmlFieldName("PropertyName"), #"[\.\[\]]", "_")
But this seems a little untidy to me, not to mention that there could be edge cases which the old method deals with that I don't know about.
Half an hour of googling, reading the .NET Core docs and searching SO hasn't turned up anything useful. The only thing I could find which was remotely relevant was this answer (which just confirms the method is gone).
Does anyone know why GetFullHtmlFieldId no longer exists? Is it just an accidental omission, or is there a newer, better way of getting this now?
I had a look through the ASP.NET Core repository, and it looks like TemplateInfo.GetFullHtmlFieldId() was originally removed due to a static reference to HtmlHelper.
After a lot of digging around (the .NET Core source browser is an amazingly useful tool), I think the solution for my problem is to use IHtmlHelper.GenerateIdFromName. So, this code:
ViewData.TemplateInfo.GetFullHtmlFieldId("PropertyName")
should now be written as:
Html.GenerateIdFromName(ViewData.TemplateInfo.GetFullHtmlFieldName("PropertyName"))
Still not all that tidy (or consistent), but at least this way we're re-using the same internal logic that the framework uses to construct its IDs.
It shouldn't be hard to simulate the old behavior for your needs. Everything leads back to TagBuilder.CreateSanitizedId which exists in .NET Core too, but it changed and the logic down to the path it changed.
TemplateInfo class had the following in System.Web.Mvc:
private string _htmlFieldPrefix;
public string HtmlFieldPrefix
{
get { return _htmlFieldPrefix ?? String.Empty; }
set { _htmlFieldPrefix = value; }
}
public string GetFullHtmlFieldId(string partialFieldName)
{
return HtmlHelper.GenerateIdFromName(GetFullHtmlFieldName(partialFieldName));
}
public string GetFullHtmlFieldName(string partialFieldName)
{
// This uses "combine and trim" because either or both of these values might be empty
return (HtmlFieldPrefix + "." + (partialFieldName ?? String.Empty)).Trim('.');
}
Afterwards it uses HtmlHelper to generate the id from the name:
public static string IdAttributeDotReplacement
{
get { return WebPages.Html.HtmlHelper.IdAttributeDotReplacement; }
set { WebPages.Html.HtmlHelper.IdAttributeDotReplacement = value; }
}
public static string GenerateIdFromName(string name, string idAttributeDotReplacement)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if (idAttributeDotReplacement == null)
{
throw new ArgumentNullException("idAttributeDotReplacement");
}
// TagBuilder.CreateSanitizedId returns null for empty strings, return String.Empty instead to avoid breaking change
if (name.Length == 0)
{
return String.Empty;
}
return TagBuilder.CreateSanitizedId(name, idAttributeDotReplacement);
}
What needs to be researched and adjusted as per your needs:
IdAttributeDotReplacement property (gets or sets the character that is used to replace the dot (.) in the id attribute of rendered form controls) which also exists in .NET Core in Microsoft.AspNetCore.Mvc.Rendering
TagBuilder.CreateSanitizedId(name, idAttributeDotReplacement) which I can see got modified to TagBuilder.CreateSanitizedId(string name, string invalidCharReplacement);
I don't really know about the method getting to be something else in .NET Core, but I'm looking forward to finding out.
In an old WPF project I have a class with Properties like this:
private string _name = "";
public string Name
{
get { return _name; }
set
{
string cleanName = clsStringManip.CleanText(value, true);
if (cleanName != _name)
{
_name = cleanName;
}
}
}
Where every time the name changes, I ensure that the value is "cleaned". Putting it in the property ensures I never forget to clean the string before setting the property on the object.
Now I am recreating this system using MVC5 and EntityFramework6.1 using DatabaseFirst.
So all the properties are autogenerated by EF. How then can I add the equivalent CleanText function to my properties without editing the autogen code? - as I'll lose these changes next time I change my database and resync.
All I can find via Google is a way add data annotations via MetadataType and partial classes but this doesn't answer my question.
I tried to add the above code into a partial class but get the error:
The type XXX already contains a definition for Name
The only way I can think is to create a bunch of SetProperty() functions but this is dirty and you can never ensure other developers (or myself) will remember to use them.
Disclaimer: I haven't used EF 6 yet.
Let me answer this in two parts. First, I will tell you how to do this. Then I will tell you why I don't think you should do this. :-)
HOW:
As you discovered, you cannot create another Name property. You need to modify the way the EF generates the code, so that it gives you a place to insert your new code. Depending on how you are using the EF, it often generates Validate() method calls or OnPropertyChanged() calls. You may be able to do what you want inside of those methods.
If you can't do this in Validate() or OnPropertyChanged(), you could change the T4 template to generate something like this:
private string _name = "";
public string Name
{
get { return _name; }
set
{
string cleanName = value;
Cleanup_Name(ref cleanName);
if (cleanName != _name)
{
_name = cleanName;
}
}
}
private partial void Cleanup_Name(ref string);
This gives you a partial method that you can then implement as you see fit. So for any property you want to customize, you can now add another file to your project that does this:
public partial class MyEntity {
void Cleanup_Name(ref string name)
{
// Put your logic in here to fixup the name
}
}
If you do not write the above code block, then the partial method is simply a no-op. (Partial methods must return void, hence the use of a ref parameter).
WHY NOT?
The advantage of this method is that it is totally transparent to the developer. The property is just magically changed. But there are several disadvantages:
Some controls expect that if they call name = "123" that if they get the name back, it is "123" and will fail if this happens. Values are changing but no PropertyChanged event fired. If you do fire the PropertyChanged, then they sometimes change the value back. This can cause infinite loops.
There is no feedback to the user. They typed in one thing, and it looked right, but now it says something different. Some controls might show the change and others won't.
There is no feedback to the developer. The watch window will seemingly change values. And it is not obvious where to see the validation rules.
The entity-framework itself uses these methods when it loads data from the database. So if the database already contains values that don't match the cleanup rules, it will clean them when loading from the database. This can make LINQ queries misbehave depending on what logic is run on the SQL server and what logic is run in the C# code. The SQL code will see one value, the C# will see another.
You might also want to look into what the Entity-Framework's change tracking does in this case. If a property set does a cleanup while loading values from the database, does it consider that a change to the entity? Will a .Save() call write it back to the database? Could this cause code that never intended to change the database to suddenly do so?
ALTERNATIVE
Instead of doing this, I suggest creating a Validate() method that looks at each property and returns errors indicating what is wrong. You could also even create a Cleanup() method that fixes the things that are wrong. This means the cleanups are no longer transparent, so the developer must call them explicitly. But that is a good thing: the code isn't changing values without them realizing it. The person writing the business logic or the UI knows at what point the values will change, and can get a list of why.
The only way you can achieve this is by creating a new property you actually use in your application. Perhaps you can hide the original property in the designer. The actual property you use could look like this:
public string ExternalName
{
get { return Name; }
set
{
string cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
{
Name = cleanName;
}
}
}
As an alternative, you can use POCO classes:
If you want to keep using database-first, check this answer
Use code-first for an existing database, see this detailed guide
Add partial to the generated class.
Change the scope of Name in the generated class from public to internal.
Add the following in the same assembly:
public partial class classname
{
[NotMapped]
public string CleanName
{
get { return Name; }
set
{
var cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
Name = cleanName;
}
}
}
Caveat: you'd have to remember to do steps 1-2 every time you regenerated your POCOs ... I'd seriously consider Code First to Existing Database.
EDIT
Optionally:
Rename Name as InternalName in the generated classname; decorate it with [Column("Name")].
Rename CleanName as Name in the partial class under your control.
Caveat in 4 becomes "remember to do steps 1, 2, and 5 every time you regenerate POCOs".
This approach has the added benefit of not having to modify any of your client code (i.e., use of Name remains Name). And I'd still strongly consider Code First to Existing Database.
I have been working with ASP.NET MVC for over a year now. I love ASP.NET MVC. In the meantime, every now and then I develop a Windows Forms Application. This application allows our customers to create a group structure for their webshop.
For that purpose I use a TreeView. How does this relate to ASP.NET MVC? Well, MVC has these action filter attributes that come in quite handy and makes the code better readable (in my opinion). I mean filters like for example the [Authorize] attribute, which stops the action from executing if the user is not authorized.
So the actual question is, can a simular filter be created for the methods and events in a Windows Forms Application? I need to check (in a lot of methods and events) if the SelectedNode property of the TreeView has a value. Now I do that this way:
private void setSelectedGroupInformation(bool refreshProductCount)
{
GroupNode selectedNode = trvGroupTree.SelectedNode;
if (selectedNode == null || !selectedNode.HasGroup)
return;
// Code that actually DOES something
}
But it would be nice if this would be possible:
[SelectedNodeRequired]
[GroupRequired]
private void setSelectedGroupInformation(bool refreshProductCount)
{
// Code that actually DOES something
}
That is much better readable. I checked out the internet for this but I can't find a similar question.
It might be nice, but you need to do some work for it (one example, is constructing a type at runtime). What is relatively easy to do is to call some common method at first
[SelectedNodeRequired]
[GroupRequired]
private void setSelectedGroupInformation(bool refreshProductCount)
{
if(MethodTester())
return;
// Code that actually DOES something
}
bool MethodTester()
{
// use call stack to get caller method name
// use reflection to get attributes of method
// check attributes and conditions
...
return true; // if has to be filtered
...
return false;
}
But, why not making methods what actually does all logic you need to check? Like this
private void setSelectedGroupInformation(bool refreshProductCount)
{
if(Global.IsGroupRequired && Global.IsSelectedNodeRequired)
{
// Code that actually DOES something
}
}
I'm working on asp.net mvc3 application. I guess it's described somewhere but a fast search didn't help much and also I get very diverse opinions on using Session variables but still I have a little time to make a decision.
The problem is that I have a razor view where in image gallery images are shown and user that has permissions can delete one or many images. However I can't delete each image at the time the user click the button, instead I have to collect the id's of the images that have been "deleted" from the user (I remove the span tag which holds the image) but the actual deleting is perform by service and I have to pass the service all the data at once.
All this is inside Html.BeginForm so the data is collected on submit and before that I need a way to store those values somehow. Using sessions seems the easiest way by now. I tried something like this :
public void DeletePicture(long documentImageID)
{
if (documentImageID != null)
{
Session["imagesIdForDeleting"] = documentImageID;
}
}
But with a simple debug I can not figure out first - how at all I can access the documentImageID value from the Session["imagesIdForDeleting"]. I don't know if it's just matter of syntax or I'm trying to make the things less complicated than they are in real. Also - I'm not sure if making something like this I'm actually saving all the values or I rewrite the previous value and always have only one value stored in the session variable. Anyways, Any help how to implement this right would be appreciated.
You can store list in Session.
e.g.
public void DeletePicture(long documentImageID)
{
if(Session["imagesIdForDeleting"] == null)
{
Session["imagesIdForDeleting"] = new List<long>();
}
if (documentImageID != null)
{
var list = (List<long >)Session["imagesIdForDeleting"];
list.Add(documentImageID);
}
}
I'm not sure if making something like this I'm actually saving all the
values or I rewrite the previous value and always have only one value
stored in the session variable.
Yes you are right about overwriting the stored value.
Use a typed list to store the images and put it in session.
public void DeletePicture(long documentImageID)
{
if (documentImageID != null)
{
if(Session["imagesIdForDeleting"]) == null
{
Session["imagesIdForDeleting"] = new List<long>();
}
((List<long>)Session["imagesIdForDeleting"]).Add( documentImageID);
}
}
For some time now I am trying to figure out how I can refactor some of my code to reduce redundancy throughout my application. I am just learning the basics of OOP and can create simple classes and methods but my knowledge is limited in terms of practical applicability. The following bit of code illustrates my frustration:
#region DELETE selected users - button
protected void btnDeleteSelected_Click(object sender, EventArgs e)
{
try
{
foreach (GridViewRow row in GridView1.Rows)
{
CheckBox cb = (CheckBox)row.FindControl("chkRows");
if (cb != null && cb.Checked)
{
// get the row index values (DataKeyNames) and assign them to variable
string userName = GridView1.DataKeys[row.RowIndex].Value.ToString();
// delete selected users and their profiles
ProfileManager.DeleteProfile(userName);
Membership.DeleteUser(userName);
Msg.Text = "User(s) were sucessfully <b>DELETED</b>!";
Msg.Visible = true;
}
}
}
catch (Exception ex)
{
Msg.Text = "Oops! " + ex.Message;
Msg.Visible = true;
}
finally
{
// refresh gridview to reflect changes
GridView1.DataBind();
}
}
#endregion
This bit of code is used on several pages of my project in the pages codebehind file. How can I move this to a class file. I don't know how to reference an object like a gridview in a class because it does not exist like it does on the actual page.
Could some one help out please? Thank you.
There are many principles that you generally apply when trying to refactor code. Currently, you're trying to refactor your code as to not violoate the DRY principle (DRY = don't repeat yourself). It would be a great move to refactor that code.
But, some other principals might come in to play that you might want to consider. The single responsibility principle would suggest that each method does only one unambiguous thing. Think about the operations your current method does. It extracts the usernames from the GridView, and then deletes some data associated with that user. That might be better off as two methods.
Also, loosely coupled code is good. You don't want a bunch of dependencies between your classes. For example, if you moved your whole method, as is, to a separate class or library, that class or library would be dependent on the ASP.NET libraries, because it makes a specific reference to the GridView control. But it doesn't need to. You could have a separate method that pulls the username out of the GridView (this method would be tightly coupled with ASP.NET), and then a separate one that does the rest of your actions (such as deleting the user's data), which only requires the username. That second method would not be coupled to ASP.NET in any way. So by having separate methods that each have a single responsibility, you'll have more loosely coupled code. Two for one.
As for what you said here:
I don't know how to reference an
object like a gridview in a class
because it does not exist like it does
on the actual page.
You'd just want to pass a reference to your GridView when you call the method that would extract the usernames. Something like this:
public static class Util
{
public static IEnumerable<string> GetUsernames(GridView gv)
{
List<string> userNames = new List<string>();
foreach (GridViewRow row in gv.Rows)
{
CheckBox cb = (CheckBox)row.FindControl("chkRows");
if (cb != null && cb.Checked)
{
// get the row index values (DataKeyNames) and assign them to variable
string userName = gv.DataKeys[row.RowIndex].Value.ToString();
userNames.Add(userName);
}
}
return userNames;
}
}
Now, in any of your asp.net code behind pages, you can do:
IEnumerable<string> usernames = Util.GetUsernames(GridView1);
foreach(string username in usernames)
doSomething(username);
The doSomething(username) would be a call to some other method that does your delete operations, or whatever you want.
Hope this helps.
Oh, and if you're just learning the basics of OOP, I would recommend something like Head First Object-Oriented Analysis and Design, or any book that seems to adequately cover the subject. I like the O'Reilly Head First series. The information is very digestible.
In Webforms applications, a common technique for factoring out application and/or business logic out of the code behind is to use the MVP pattern. This doesn't mean all the code typically found in the code-behind just gets moved to another class, as this really isn't any different than a code-behind. The UI rendering logic (e.g. Databinding setup, access to UI controls, etc.) is best left to the code-behind, but the business logic (in this case, the removal of users) is performed by a method within the Presenter.
In your case, I would build up a collection of usernames and call a RemoveUsers() method on the Presenter, passing in the list of usernames, which would handle interfacing with the ProfileManager and Membership components(ideally through abstractions). This allows you to write unit tests for the logic within the Presenter.