Contents tagged with C#
-
How to switch between HTTP and HTTPS in ASP.NET MVC2
ASP.NET MVC2 has the new RequireHttpsAttribute that you can use to decorate any action to force any non-secure request to come through HTTPS (SSL). It can be used as simply as this:
[RequireHttps]
public ActionResult LogOn()
{
.....
}Now any request to the LogOn action that is not over HTTPS will automatically be redirected back to itself over HTTPS. This is very useful and easy to implement.
Unfortunately though, once the scheme is changed to HTTPS, all following requests will also be under HTTPS, which is not necessarily what you want. In most cases you do not need all requests to your site to be secure but only certain ones such as the logon page or pages that accept credit card information.
To handle this you can override the Controller.OnAuthorization method. From within this method, you can check to see if the RequireHttps attribute is set on the Controller Action by searching the Attributes collection of the ActionDescriptor object. If the RequireHttpsAttribute is not set AND the request is under SSL, then return a redirect result to the HTTP (non-SSL) url:
public class ControllerBase : Controller
{protected override void OnAuthorization(AuthorizationContext filterContext)
{//the RequireHttpsAttribute set on the Controller Action will handle redirecting to Https.
// We just need to handle any requests that are already under SSL but should not be.
if (Request.IsSecureConnection)
{
Boolean requireHttps = false;
requireHttps = filterContext.ActionDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), false).Count() >= 1;
//If this request is under ssl but yet the controller action
// does not require it, then redirect to the http version.
if (!requireHttps && !filterContext.IsChildAction)
{
UriBuilder uriBuilder = new UriBuilder(Request.Url);
//change the scheme
uriBuilder.Scheme = "http";
uriBuilder.Port = 80;
filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
}
}
base.OnAuthorization(filterContext);
}}
Now any HTTPS requests to controller actions that are not decorated with a RequireHttps attribute will be forced back to an HTTP (non-secure) request.
EDITED (2010-08-21): Modified the code as recommended by the comment from Donnie Hale to move the check for Request.SecureConnection to the outermost if block.
EDITED (2011-01-06): Modified the code as recommended by the comment from Sergio Tai to not perform this check if use Html.Action in views
-
How to implement ISNULL(SUM(ColumnName),0) in Linq2SQL
I had a Linq2Sql statement like this:
var q = from o in db.OrderItem
where o.UserId == UserId
select o.Amount;And then I was trying to get the sum of the OrderItem.Amount column by doing this:
total = q.Sum();
This worked fine as long as there were rows for this UserId in the OrderItem table. But if there are no rows in the table for this UserId I was getting this exception:
System.InvalidOperationException: The null value cannot be assigned to a member with type System.Decimal which is a non-nullable value type.
Linq2Sql was creating the following SQL query:
SELECT SUM([t0].[Amount]) AS [value]
FROM [dbo].[OrderItem] AS [t0]
WHERE [t0].[UserId] = @p0which is fine but I really want this:
SELECT ISNULL(SUM([t0].[Amount]),0) AS [value]
FROM [dbo].[OrderItem] AS [t0]
WHERE [t0].[UserId] = @p0Notice the ISNULL in the statement above that will return 0 if there are no rows found for this UserId.
Eventually I was able to track it down to var q being an IQueryable in the Linq2Sql statement above. By using ToArray() like this:
var q = (from o in db.OrderItem
where o.UserId == UserId
select o.Amount).ToArray();
total = q.Sum();var q is now an IEnumerable (because ToArray() returns an IEnumerable<Decimal>). And instead of IQueryable.Sum() trying to apply an expression to the Null value, IEnumerable.Sum() will just loop through all of the items in the array and sum the values. In this case there are no items in the array so 0 is returned.
I could have also explicitly declared q as an IEnumerable<Decimal>:
IEnumerable<Decimal> q = from o in db.OrderItem
where o.UserId == UserId
select o.Amount;
total = q.Sum();Just to note, the SQL statement is still not using ISNULL() but I am getting the result I want from within C#.
UPDATE (2010-07-22): Another solution from RichardD:
var q = from o in db.OrderItem
where o.UserId == UserId
select o.Amount;
Decimal total = q.Sum(a => (Decimal?)a) ?? 0M;OR
IQueryable<Decimal?> q = from o in db.OrderItem
where o.UserId == UserId
select (Decimal?)o.Amount;
Decimal total = q.Sum() ?? 0; -
Linq2Sql: How to join tables on more than one column
You can join two tables in Linq2Sql by using an anonymous type to specify the join.
var r =
from o in db.Orders
join p in db.Products on o.ProductId equals p.ProductId
join pu in db.ProductUsers on new { p.ProductId, o.UserId } equals new { pu.ProductId, pu.UserId }
select new {o, p, pu};This is equivalent to the following SQL:
SELECT * FROM Order o
JOIN Product p ON o.ProductId=p.ProductId
JOIN ProductUser pu ON p.ProductId=pu.ProductId AND o.UserId=pu.UserIdThe anonymous type { p.ProductId, o.UserId } is lined up with the second anonymous type { pu.ProductId, pu.UserId } to create the join on both columns.
But what if you are trying to join two tables where the column names do not match? For instance this SQL:
SELECT * FROM Order o
JOIN Product p ON o.ProductId=p.ProductId
JOIN ProductUser pu ON p.ProductId=pu.ProductId AND o.UserId=pu.CreatedByUserIdYou would think you could create two anonymous types just like above and line up the properties like this:
First anonymous type: { p.ProductId, o.UserId }
Second anonymous type: { pu.ProductId, pu.CreatedByUserId }But if you try that you will get a compilation error:
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
What is going on here? Both o.UserId and pu.CreatedByUserId have the same data type so why is there a compilation error?
Because Linq2Sql lines up the two anonymous types by name and with the different names it cannot resolve the two column join. Both the order and the name need to match for this to work. To resolve this just specify an alias property name for one of the anonymous types that do not match like so:
First anonymous type: { p.ProductId, CreatedByUserId = o.UserId }
Second anonymous type: { pu.ProductId, pu.CreatedByUserId }Now Linq2Sql knows how to completely match up the two column join. Here is the full Linq2Sql statement for a join with two columns when the column names do not match:
var r =
from o in db.Orders
join p in db.Products on o.ProductId equals p.ProductId
join pu in db.ProductUsers on new { p.ProductId, CreatedByUserId = o.UserId } equals new { pu.ProductId, pu.CreatedByUserId}
select new {o, p, pu};Technorati Tags: Linq2Sql two column join -
How to 301 Permanent Redirect in ASP.NET
In the next release of the .NET Framework (.NET Framework 4.0) there is a new response method for permanently redirecting a request: Response.RedirectPermanent. You can see the Beta MSDN documentation for Response.RedirectPermanent here. This will automatically issue the 301 moved permanently status code and redirect to the target page. A permanent redirect status code tells a search engine to update their cache and reassign the old url to the new url.
But if you need to do this now (prior to .NET Framework 4.0) you will need to do it the manual way. To do so you will need to manually add the status and location headers to the response.
The pre .NET Framework 4.0 versions are:
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", "/");Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", "/");
Response.End();There is also a RedirectToRoutePermanent method which will allow you to redirect to a new url using route parameters (and sends the 301 Moved Permanently status code).
UPDATE: See RichardD's comment below for a different method. I have not tried it that way but I will next time because it certainly does look much cleaner.
Technorati Tags: C#,Permanent Redirects -
How to get an indexed item of an IEnumerable object (Linq)
If you have an IEnumerable<T> collection of some objects T and you need to get a particular item out of that collection, you cannot use the standard indexing that you would normally use with brackets ([index]). If you try you will get an error such as:
Cannot apply indexing with [] to an expression of type ‘System.Collections.Generic.IEnumerable<T>
But there is the extension method ElementAt(index) (in the System.Linq namespace) for IEnumerable<T> that will allow you to get at that particular indexed item:
MyItem = MyIEnumerableExpression.ElementAt(index);
From the MSDN Documentation: http://msdn.microsoft.com/en-us/library/bb299233.aspx
-
Line-breaks and carriage returns (\r \n) are invalid in email subject
I received this exception when sending an email using System.Net.Mail.MailMessage:
"The specified string is not in the form required for a subject."
System.ArgumentException: The specified string is not in the form required for a subject.
at System.Net.Mail.Message.set_Subject(String value)
at System.Net.Mail.MailMessage.set_Subject(String value)The subject line of the email I was sending had a carriage return and line break in it so it made sense that this would be an invalid subject line.
And I was able to confirm that a check is made on the subject line to make sure there are not Carriage Returns or Line Feeds when the MailMessage.Subject property is set, but also that these are the only characters checked for.
To remove the line breaks and carriage returns I just did a simple find and replace:
subject= subject.Replace("\n", " ").Replace("\r", " ");
Technorati Tags: System.Net.Mail.MailMessage.Subject -
GDI+ Error when converting Tiff to Jpeg: Parameter is not valid
One of the web applications that I work on is Instant Church Directory, a website that helps churches create a church photo directory. Since the main focus of the application is to create a photo directory, customers are uploading all sorts of images into Instant Church Directory and the application needs to correctly convert and store the images and insert them in the final output, which is a PDF.
Since the web application was launched in September 2008 we have had an issue with some Tiff images that are uploaded and throw a System.ArgumentException when the image is converted to a Jpeg.
System.ArgumentException: Parameter is not valid. at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) at System.Drawing.Image.Save(Stream stream, ImageFormat format)
Today I was finally able to track it down to the Tiff image being created (or modified) by Adobe Photoshop and Adobe Photoshop is adding extra Property Items (meta data tags) into the image.
With the help of Bob Powell’s tool: Discovering the Property Items present in an image, I was able to see that the Adobe Photoshop Tiff images included several additional property items that were not present in other Tiff images that worked. Removing the tags from the original image prior to converting it to a Jpeg resolved the issue.
Here is a list of all of the Microsoft Windows GDI+ image property tags in numerical order.
The PropertyTagICCProfile is id 0x8773 and the other tag that I removed to resolve this issue is id 0x935C (this Property Item is not defined in the list above but it has a header in it with “Adobe Photoshop Document Data Block”). Property Items 0x02BC and 0x8649 are also not defined in the GDI+ list above and also are added by Adobe Photoshop but they did not cause any issues so I just left them (although if removing the first two do not work for you then try removing the other two also).
Here is the code that I used to remove the offending Property Items from the Tiff image before converting it to a Jpeg in order to prevent the GDI+ Parameter is not valid exception:
// Check if this is a Tiff file
if (bmp.RawFormat.Guid.Equals(ImageFormat.Tiff.Guid))
{
//loop through all of the Properties
//looking for the offending property items added by Photoshop
foreach (PropertyItem pItem in bmp.PropertyItems)
{
switch (pItem.Id)
{
//ICC Profile tag (PropertyTagICCProfile)
//Remove this tag for Tiff to be converted to Jpeg.
case 0x8773:
bmp.RemovePropertyItem(0x8773);
break;//Unknown tag added by Adobe Photoshop
//"Adobe Photoshop Document Data Block".
//Removing this tag for Tiff to be converted to Jpeg
case 0x935C:
bmp.RemovePropertyItem(0x935C);
break;default:
break;
}
}
}//convert the image to jpeg
System.IO.MemoryStream ms = new System.IO.MemoryStream();
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
bmp = new Bitmap(ms); -
Handle the button click event from an ASP.NET ListView control
Here is how you can handle the event of a button from within an ASP.NET ListView control.
Add your button to the ListView Template like you would any other control:
<asp:ListView ItemPlaceholderID="Test" runat="server" ID="ListView1" >
<ItemTemplate>
<asp:Button runat="server" Text='<%# Eval("MyProperty") %>' CommandArgument='<%# Eval("MyProperty") %>' /><br />
</ItemTemplate>
</asp:ListView>And then handle the ListView.ItemCommand event in your code:
That is really all there is to it. The ListView will bubble up the Button’s click event to the ListView_ItemCommand event handler and then you can handle it there. You can also use the CommandArgument property to send button specific data, such as the particular ID of the row or item that the user clicked the button for.
This call also be used on some of the other databound controls such as the ASP.NET Repeater.
Full Example Code:
ListViewButtonExample.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ListViewButtonExample.aspx.cs" Inherits="ListViewButtonExample" %>
<html>
<body>
<form id="form1" runat="server">
<div>
<asp:ListView ItemPlaceholderID="Test" runat="server" ID="ListView1" >
<LayoutTemplate>
<asp:Literal runat="server" ID="Test"></asp:Literal>
</LayoutTemplate>
<ItemTemplate>
<asp:Literal runat="server" Text='<%# Eval("MyProperty") %>'></asp:Literal>
<asp:Button runat="server" Text='<%# Eval("MyProperty") %>' CommandArgument='<%# Eval("MyProperty") %>' /><br />
</ItemTemplate>
</asp:ListView>
<asp:Literal runat="server" ID="Literal1"></asp:Literal>
</div>
</form>
</body>
</html>ListViewButtonExample.aspx.cs:
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;
using System.Collections;public class MyObject
{
public String MyProperty { get; set; }
}
public partial class ListViewButtonExample : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
ListView1.ItemCommand += new EventHandler<ListViewCommandEventArgs>(ListView1_ItemCommand);
}void ListView1_ItemCommand(object sender, ListViewCommandEventArgs e)
{
Literal1.Text = "You clicked the " + (String)e.CommandArgument + " button";
}protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
List<MyObject> col = new List<MyObject>();
col.Add(new MyObject { MyProperty = "test 1" });
col.Add(new MyObject { MyProperty = "test 2" });
ListView1.DataSource = col;
ListView1.DataBind();
}
}
}Technorati Tags: ListView,ItemCommand Event -
Generating a random strong password
You can use the ASP.NET Membership provider to generate a new random strong password.
In the past I have usually either rolled my own authentication system or integrated an asp.net authentication system into an existing application and therefore I did not use the ASP.NET Membership system. In the current application I am writing, I had a need to generate a random strong password for the customer.
The ASP.NET Membership system already has a static method built-in for this. You can use the GeneratePassword static method from the Membership class to create a new password:
String strongPassword = System.Web.Security.Membership.GeneratePassword(8, 1);
From the MSDN documentation, the two parameters are:
- length – Int32
- The number of characters in the generated password. The length must be between 1 and 128 characters.
- numberOfNonAlphanumericCharacters – Int32
- The minimum number of punctuation characters in the generated password.
Also from the documentation: the generated password will contain alphanumeric characters and the following punctuation marks: !@#$%^&*()_-+=[{]};:<>|./?.
But also not included in the documentation is that the returned password will not be a “dangerous string”; in other words it won’t look like a block of script.
The Membership.GeneratePassword checks the newly generated password string using an internal method called CrossSiteScriptingValidation.IsDangerousString() and will not return a password that does not pass this test. It will just loop and continue to generate a new one until it is not considered a dangerous string. Pretty cool stuff!
-
How to quickly format a C# class file – Ctrl-K, Ctrl-D
In C# as you are typing in a class file the indenting of your code can easily get out of alignment making the code hard to read. Where does your class, if block, or method begin and end??
You can manually go and fix each line but there is a much simpler way to correct the indenting. You can use Ctrl-K, Ctrl-D to format the entire document. Now your code will be reformatted to the correct indenting for your page.
Couple things to note:
- If there are errors in the structure of your code (missing closing brackets, missing semi-colon, etc.) then this will not work. This will still work with other errors such as undeclared variables.
- If you are working on legacy code (for example fixing a bug) and there is a different indenting structure, it is good practice to stick with the legacy indenting and not modify the indenting of the entire file just because you fixed a bug in one line of code. You should try to minimize your code change to only what was necessary to fix the bug (don’t reformat the entire file). The next person that does a diff on your check-in with the previous check-in will thank you.
- I also leave the Visual Studio indentation settings alone and stick with the defaults. If you have a different indentation style than a code file that was previously checked, this process could also cause the subsequent diff to show lots of red.
- You can use Ctrl-K, Ctrl-F on smaller blocks of text, so if you have a strange indenting style then highlighting only your change can correct the indentation on the selected lines and not the entire file.
- These can be found under the Edit > Advanced Menu:
- Cutting and Pasting is also another way to get the content formatted quickly:
- Ctrl-A, Ctrl-X, Ctrl-V
Technorati Tags: Visual Studio Tip and Tricks
