ASP.NET: URI handling - asp.net

I'm writing a method which, let's say, given 1 and hello should return http://something.com/?something=1&hello=en.
I could hack this together pretty easily, but what abstraction functionality does ASP.NET 3.5 provide for building URIs? I'd like something like:
URI uri = new URI("~/Hello.aspx"); // E.g. ResolveUrl is used here
uri.QueryString.Set("something", "1");
uri.QueryString.Set("hello", "en");
return uri.ToString(); // /Hello.aspx?something=1&hello=en
I found the Uri class which sounds highly relevant, but I can't find anything which does the above really. Any ideas?
(For what it's worth, the order of the parameters doesn't matter to me.)

Edited to correct massively incorrect code
Based on this answer to a similar question you could easily do something like:
UriBuilder ub = new UriBuilder();
// You might want to take more care here, and set the host, scheme and port too
ub.Path = ResolveUrl("~/hello.aspx"); // Assumes we're on a page or control.
// Using var gets around internal nature of HttpValueCollection
var coll = HttpUtility.ParseQueryString(string.Empty);
coll["something"] = "1";
coll["hello"] = "en";
ub.Query = coll.ToString();
return ub.ToString();
// This returned the following on the VS development server:
// http://localhost/Hello.aspx?something=1&hello=en
This will also urlencode the collection, so:
coll["Something"] = "1";
coll["hello"] = "en&that";
Will output:
Something=1&hello=en%26that

As far I know nothing here. So everybody has its own implementation.
Example from LinqToTwitter.
internal static string BuildQueryString(IEnumerable<KeyValuePair<string, string>> parameters)
{
if (parameters == null)
{
throw new ArgumentNullException("parameters");
}
StringBuilder builder = new StringBuilder();
foreach (var pair in parameters.Where(p => !string.IsNullOrEmpty(p.Value)))
{
if (builder.Length > 0)
{
builder.Append("&");
}
builder.Append(Uri.EscapeDataString(pair.Key));
builder.Append("=");
builder.Append(Uri.EscapeDataString(pair.Value));
}
return builder.ToString();
}
UPDATE:
You can also create extension method:
public static UriBuilder AddArgument(this UriBuilder builder, string key, string value)
{
#region Contract
Contract.Requires(builder != null);
Contract.Requires(key != null);
Contract.Requires(value != null);
#endregion
var query = builder.Query;
if (query.Length > 0)
{
query = query.Substring(1) + "&";
}
query += Uri.EscapeDataString(key) + "="
+ Uri.EscapeDataString(value);
builder.Query = query;
return builder;
}
And usage:
var b = new UriBuilder();
b.AddArgument("test", "test");
Please note that everything here is untested.

Just combined answers=>
public static class UriBuilderExtensions
{
public static void AddQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key)) throw new ArgumentNullException
("Key '{0}' already exists!".FormatWith(key));
x.Add(key, value);
b.Query = x.ToString();
}
public static void EditQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x[key] = value;
else throw new ArgumentNullException
("Key '{0}' does not exists!".FormatWith(key));
b.Query = x.ToString();
}
public static void AddOrEditQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x[key] = value;
else
x.Add(key, value);
b.Query = x.ToString();
}
public static void DeleteQueryArgument(this UriBuilder b, string key)
{
key = Uri.EscapeDataString(key);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x.Remove(key);
b.Query = x.ToString();
}
}
Half baked code. But should work well enough.

There's also the UriBuilder class

This is something that might appeal to you- recently at work I was looking at a way to "type" commonly used URL query string variables and so developed this interface:
'Represent a named parameter that is passed from page-to-page via a range of methods- query strings, HTTP contexts, cookies, session, etc.
Public Interface INamedParam
'A key that uniquely identfies this parameter in any HTTP value collection (query string, context, session, etc.)
ReadOnly Property Key() As String
'The default value of the paramter.
ReadOnly Property DefaultValue() As Object
End Interface
You can then implement this interface to describe a query string parameter, such an implementation for your "Hello" param might look like this:
Public Class HelloParam
Implements INamedParam
Public ReadOnly Property DefaultValue() As Object Implements INamedParam.DefaultValue
Get
Return "0"
End Get
End Property
Public ReadOnly Property Key() As String Implements INamedParam.Key
Get
Return "hello"
End Get
End Property
End Class
I developed a small (and very, very basic) class to help build URLs using these strongly typed parameters:
Public Class ParametrizedHttpUrlBuilder
Private _RelativePath As String
Private _QueryString As String
Sub New(ByVal relativePath As String)
_RelativePath = relativePath
_QueryString = ""
End Sub
Public Sub AddQueryParameterValue(ByVal param As INamedParam, ByVal value As Object)
Dim sb As New Text.StringBuilder(30)
If _QueryString.Length > 0 Then
sb.Append("&")
End If
sb.AppendFormat("{0}={1}", param.Key, value.ToString())
_QueryString &= sb.ToString()
End Sub
Public Property RelativePath() As String
Get
Return _RelativePath
End Get
Set(ByVal value As String)
If value Is Nothing Then
_RelativePath = ""
End If
_RelativePath = value
End Set
End Property
Public ReadOnly Property Query() As String
Get
Return _QueryString
End Get
End Property
Public ReadOnly Property PathAndQuery() As String
Get
Return _RelativePath & "?" & _QueryString
End Get
End Property
End Class

Here's my version (needs .NET4 or a ToArray() call on the Select)
var items = new Dictionary<string,string> { { "Name", "Will" }, { "Age", "99" }};
String query = String.Join("&", items.Select(i => String.Concat(i.Key, "=", i.Value)));
I thought the use of Dictionary might mean the items can get reordered, but that doesn't actually seem to be happening in experiments here - not sure what that's about.

Related

Web Api adding underscore prefix to json property names

This is what I get
{"_ProviderId":476,"_FirstName":" ","_LastName":"Nam Of Provders","_Specialty":"Pediatrics"}
api code
Public Class ProviderSimpleModel
Public Property ProviderId As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property Specialty As String
Public ReadOnly Property Name() As String
Get
Return If(FirstName, "").Trim() + " " + If(LastName, "").Trim()
End Get
End Property
End Class
<HttpGet>
Public Function GetProviderSimpleList(Optional id As Integer = 0) As List(Of ProviderSimpleModel)
Dim db As New LinqConsole.MyDataContext
Dim q = From pr In db.Providers
Where pr.ProviderId = id OrElse id = 0
Select New ProviderSimpleModel With
{
.ProviderId = pr.ProviderId,
.FirstName = pr.FirstName,
.LastName = pr.LastName,
.Specialty = pr.Specialty
}
Dim list = q.OrderBy(Function(x) x.FirstName).ThenBy(Function(x) x.LastName).ToList()
db.Dispose()
Return list
End Function
why is this happening and how to stop it?
Updated Newtonsoft from 10.x to 13.x, issue solved.

How to dynamically use getters/setters in Dart

class User{
String firstName;
String lastName;
String email;
}
I want to be able to get and set one of the fields in user with a dynamically selected symbol or string. For example String value = u[new Symbol("firstName")];
I see that InstanceMirror has a getField method, but it doesn't seem to return the value. All I need is the value.
If you create a symbol with # you need to know the name at compile time.
I got it working this way:
library operator_lib;
import 'dart:mirrors';
void main(List<String> args) {
var x = new X();
var f = new Symbol('firstName');
var r = reflect(x);
print(r.getField(f).reflectee);
r.setField(f, "John");
print(r.getField(f).reflectee);
}
class X {
String firstName = 'Mike';
}

Load ProfileBase without HTTP Context

I'm in the process of converting user profile data that was serialized in the classic ASP.Net Membership Provider for use in SimpleMembership. I cannot figure out how to get the ProfileBase object for every user in the system.
If a specific user is logged in, I can do something like:
MyModel myModel =
(MyModel)HttpContext.Current.Profile.GetPropertyValue("MyKey");
where MyKey refers to a profile key established in web.config like this:
<add name="MyModel" type="MyNS.MyModel" serializeAs="Binary" />
However, without the benefit of an HTTP context (I'm trying to do this for all users in the system, not a logged-in user) I can't figure out how to load the profile and ultimately an instance of MyModel for each user in the system.
I have tried:
ProfileInfoCollection profiles =
ProfileManager.GetAllProfiles(ProfileAuthenticationOption.All);
foreach (var profile in profiles)
{
var pi = (ProfileBase)profile;
// OOPS! Unfortunately GetAllProfiles returns
// ProfileInfo and not ProfileCommon or ProfileBase
}
and
MembershipUserCollection existingUsers = Membership.GetAllUsers();
foreach (MembershipUser mu in existingUsers)
{
mu. // OOPS! No link to the profile from the user...
}
How can I retrieve the ProfileCommon or ProfileBase instance for each profile in the system, and thus ultimately the MyModel associated with each user?
Since I could not find an answer to this question, I opted to just read the profile data directly from SQL.
It turns out that the format is straightforward. In aspnet_Profile:
PropertyNames uses a format NameOfProperty:TypeFlag:Offset:Length (repeat for all properties).
FlagType is "S" for string or "B" for binary
Offset is the offset in the appropriate data field
Length is the length of data in the appropriate data field
PropertyValuesString holds all string properties concatenated without a delimiter.
PropertyValuesBinary holds all binary properties concatenated without a delimiter.
BinaryFormatter is used to serialize binary (non-string) properties
Here's a little code I wrote to parse the data:
private class Migrate_PropNames
{
public string Name { get; set; }
public bool IsString { get; set; }
public int Offset { get; set; }
public int Length { get; set; }
}
....
Dictionary<string, Migrate_PropNames> propInfo = ParsePropInfo(propertyNames);
// Example string property
string firstName = Migrate_GetString(propInfo["FirstName"], propertyValuesString);
// Example binary property
MyType myType =
Migrate_GetBinary<MyType>(propInfo["MyTypeKey"], propertyValuesBinary));
private T Migrate_GetBinary<T>(Migrate_PropNames propNames, byte[] propertyValuesBinary)
{
byte[] data = new byte[propNames.Length];
Array.Copy(propertyValuesBinary, propNames.Offset, data, 0, propNames.Length);
var fmt = new BinaryFormatter();
using (var ms = new MemoryStream(data))
{
T original = (T)fmt.Deserialize(ms);
return original;
}
}
private string Migrate_GetString(Migrate_PropNames propNames, string propertyNames)
{
return propertyNames.Substring(propNames.Offset, propNames.Length);
}
private Dictionary<string, Migrate_PropNames> ParsePropInfo(string propertyNames)
{
Dictionary<string, Migrate_PropNames> result = new Dictionary<string,Migrate_PropNames>();
string[] parts = propertyNames.Split(new string[] { ":"}, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < parts.Length; i += 4)
{
Migrate_PropNames pn = new Migrate_PropNames();
pn.Name = parts[i];
pn.IsString = (parts[i + 1] == "S");
pn.Offset = int.Parse(parts[i + 2]);
pn.Length = int.Parse(parts[i + 3]);
result.Add(pn.Name, pn);
}
return result;
}
I hope this helps someone. I'll gladly accept a different answer that correctly shows how to use the API.
From the ProfileInfo or MemberShipUser object, you should can get a ProfileBase one using ProfileBase.Create(string username).

ASP .NET MVC 3 Data Annotations GreaterThan LowerThan for DateTime and int

I would like to know what is the easiest way to have a "Greater Than" & "Lower Than" validation on a ASP.NET MVC 3 form?
I use unobtrusive JavaScript for client validation.
I have two DateTime properties (StartDate & EndDate) and I need a validation to be sure that the EndDate is greater than the StartDate.
I have another similar case with another form on which I have a MinValue (int) & MaxValue (int).
Does this type of validation exist by default? Or does someone know an article which explains how to implement it?
Could look at the dataannotationsextensions it does Min/Max for int
Also have a look at a foolproof validation it inlcudes GreaterThan comparison for numeric/datetime etc
You can simply do this with custom validation.
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
public class DateGreaterThanAttribute : ValidationAttribute
{
string otherPropertyName;
public DateGreaterThanAttribute(string otherPropertyName, string errorMessage)
: base(errorMessage)
{
this.otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult validationResult = ValidationResult.Success;
try
{
// Using reflection we can get a reference to the other date property, in this example the project start date
var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
// Let's check that otherProperty is of type DateTime as we expect it to be
if (otherPropertyInfo.PropertyType.Equals(new DateTime().GetType()))
{
DateTime toValidate = (DateTime)value;
DateTime referenceProperty = (DateTime)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
// if the end date is lower than the start date, than the validationResult will be set to false and return
// a properly formatted error message
if (toValidate.CompareTo(referenceProperty) < 1)
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
else
{
validationResult = new ValidationResult("An error occurred while validating the property. OtherProperty is not of type DateTime");
}
}
catch (Exception ex)
{
// Do stuff, i.e. log the exception
// Let it go through the upper levels, something bad happened
throw ex;
}
return validationResult;
}
}
and use it in model like
[DisplayName("Start date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime StartDate { get; set; }
[DisplayName("Estimated end date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
[DateGreaterThan("StartDate", "End Date end date must not exceed start date")]
public DateTime EndDate { get; set; }
This works well with server side validation.For client side validaion you can write the method like GetClientValidationRules like
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
//string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
string errorMessage = ErrorMessageString;
// The value we set here are needed by the jQuery adapter
ModelClientValidationRule dateGreaterThanRule = new ModelClientValidationRule();
dateGreaterThanRule.ErrorMessage = errorMessage;
dateGreaterThanRule.ValidationType = "dategreaterthan"; // This is the name the jQuery adapter will use
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
dateGreaterThanRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);
yield return dateGreaterThanRule;
}
Now simply in view
$.validator.addMethod("dategreaterthan", function (value, element, params) {
return Date.parse(value) > Date.parse($(params).val());
});
$.validator.unobtrusive.adapters.add("dategreaterthan", ["otherpropertyname"], function (options) {
options.rules["dategreaterthan"] = "#" + options.params.otherpropertyname;
options.messages["dategreaterthan"] = options.message;
});
You can find more details in this link
I don't know if writing your own validator class is the "easiest" way, but that's what I did.
Usage:
<DataType(DataType.Date)>
Public Property StartDate() As DateTime
<DataType(DataType.Date)>
<DateGreaterThanEqual("StartDate", "end date must be after start date")>
Public Property EndDate() As DateTime
Class:
<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)>
Public Class DateGreaterThanEqualAttribute
Inherits ValidationAttribute
Public Sub New(ByVal compareDate As String, ByVal errorMessage As String)
MyBase.New(errorMessage)
_compareDate = compareDate
End Sub
Public ReadOnly Property CompareDate() As String
Get
Return _compareDate
End Get
End Property
Private ReadOnly _compareDate As String
Protected Overrides Function IsValid(ByVal value As Object, ByVal context As ValidationContext) As ValidationResult
If value Is Nothing Then
' no need to do or check anything
Return Nothing
End If
' find the other property we need to compare with using reflection
Dim compareToValue = Nothing
Dim propAsDate As Date
Try
compareToValue = context.ObjectType.GetProperty(CompareDate).GetValue(context.ObjectInstance, Nothing).ToString
propAsDate = CDate(compareToValue)
Catch
Try
Dim dp As String = CompareDate.Substring(CompareDate.LastIndexOf(".") + 1)
compareToValue = context.ObjectType.GetProperty(dp).GetValue(context.ObjectInstance, Nothing).ToString
propAsDate = CDate(compareToValue)
Catch
compareToValue = Nothing
End Try
End Try
If compareToValue Is Nothing Then
'date is not supplied or not valid
Return Nothing
End If
If value < compareToValue Then
Return New ValidationResult(FormatErrorMessage(context.DisplayName))
End If
Return Nothing
End Function
End Class
Take a look at the answer of this thread,
There is a lib called MVC.ValidationToolkit.
Though I'm not sure whether it works in case of DateTime fields.
You can use the DateGreaterThanEqual attribute in your model. Here is a snippet of code that I used to validate two fields in my form.
[DataType(DataType.Date)]
[DisplayName("From Date")]
public DateTime? StartDate { get; set; }
[DataType(DataType.Date)]
[DisplayName("To Date")]
[DateGreaterThanEqual("StartDate")]
public DateTime? EndDate { get; set; }

DataTable to Json using jquery

I'm trying to execute a web service which returns a DataTable with the following piece of code:
$.ajax({
type: "POST",
url: url,
data: data,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
//do things
}
});
If the webservice returns a class then it works so it has nothing to do with the input paramters etc. It only fails when the web method returns a datatable (the datatable only has 2 columns and 2 rows for the test I'm doing).
The WebService class is decorated with the [ScriptService] attribute so I thought that ASP.NET would automatically serialize the return value as JSON. It doesn't seem to work with datatable.
The only solution I've found was to return a string (a manually JSON serialized object) but it doesn't seem right to me to do it this way.
I'm using Visual Studio 2008 with .Net 3.5
In the end, I've decided to use the JavaScriptSerializer class to convert the DataTable into a JSON string.
Unfortunately, this class doesn't work with a DataTable so I converted the DataTable into a list of dictionnaries and pass that list to the JavaScriptSerializer class. It takes only a few lines of code and it works fine.
Example in VB.net:
Public Function GetJson(ByVal dt As DataTable) As String
Dim serializer As System.Web.Script.Serialization.JavaScriptSerializer = New System.Web.Script.Serialization.JavaScriptSerializer()
Dim rows As New List(Of Dictionary(Of String, Object))
Dim row As Dictionary(Of String, Object)
For Each dr As DataRow In dt.Rows
row = New Dictionary(Of String, Object)
For Each col As DataColumn In dt.Columns
row.Add(col.ColumnName, dr(col))
Next
rows.Add(row)
Next
Return serializer.Serialize(rows)
End Function
Easiest way is to use the LINQ to DataSet extensions. First need to create a generic list (SearchSerialResults is just a DTO in this case) from the DataTable using LINQ to DataSet.
var resultItems = (from DataRow dr in _returnedData.AsEnumerable() select new SearchSerialResults {
ContractLineItem = (int) dr["fldContractLineItemID"],
SearchItem = (string) dr["Search Item"],
Customer = (string) dr["Customer"],
DeviceFound = (string) dr["Device Found"],
Country = (string) dr["Country"],
City = (string) dr["City"],
ContractNumber = (string) dr["Contract Number"],
QuoteNumber = (string) dr["Quote Number"],
BeginDate = (string) dr["Begin Date"],
EndDate = (string) dr["End Date"]
}).ToList();
_returnedData is the DataTable in this case. Step 2 is to do the conversion. In this case, I am returning a Json object for a jqGrid.
var jsonObject = new {
total = totalPages,
pageSize,
records = totalRecords,
rows = (from SearchSerialResults item in resultItems select new {
id = item.ContractLineItem,
cell = new [] {
item.ContractLineItem.ToString(),
item.SearchItem,
item.DeviceFound,
item.Customer,
item.ContractNumber,
item.QuoteNumber,
item.Country,
item.City,
item.BeginDate,
item.EndDate,
""
}
}).ToArray()
};
return Json(jsonObject) // for MVC
Json.NET has the ability to write DataSets/DataTables to JSON.
http://james.newtonking.com/archive/2008/09/06/dataset-datatable-serialization-with-json-net.aspx
http://schotime.net/blog/index.php/2008/07/27/dataset-datatable-to-json/
It works for very well for me with a WebService
Imports System.Web.Script.Serialization
Dim wsServicio As New ["YourWsInstance"]
Dim dsInstEstado As New DataSet
Dim sSql As String
sSql = " Your SQL Statement"
dsInstEstado = wsServicio.getData("YourWebServiceParameters")
Dim jsonString = DataTableToJSON(dsInstEstado.Tables("CA_INSTITUCION"))
Return Json(jsonString, JsonRequestBehavior.AllowGet)
Function DataTableToJSon(dt As DataTable) As Object
Dim arr(dt.Rows.Count - 1) As Object
Dim column As DataColumn
For i = 0 To dt.Rows.Count - 1
Dim dict As New Dictionary(Of String, Object)
For Each column In dt.Columns
dict.Add(column.ColumnName, dt.Rows(i)(column))
Next
arr(i) = dict
Next
Return arr
End Function
I must admit I'm not hugely surprised - DataTable basically breaks most of the rules of structured data. Why not simply project from the data-table into a typed object? A related question came up earlier... or if you know the schema of the DataTable just do the conversion in C#...
Manually building the JSON might work, but there are a lot of edge-cases to avoid; I'd rather let an existing framework handle it, to be honest.
.Net 3.5 has a JSONSerializer that should be able to handle a datatable. You may want to look at your service code again and try getting it to use that. Also, I put some code together to do it manually in this question.
Like Marc, I too am not surprised that the DataTable breaks your webservice/json exchange. I'd like to endorse Json.NET also.
But if you decide to not go with it, you still don't have to build the json manually. Just make your own lean custom class with all the properties you need and then return an array of that class. You will of course have to write code to "convert" your data table into your new class. I know, it could be a lot of code writing, but it's a lot less error prone then trying to manually make a json string.
I found this C# class very useful:
[Serializable]
public class TableMethod
{
private int m_total; public int total { get { return this.m_total; } set { this.m_total = value; } }
private int m_page; public int page { get { return this.m_page; } set { this.m_page = value; } }
private int m_records; public int records { get { return this.m_records; } set { this.m_records = value; } }
private IList<RowElement> m_rows; public IList<RowElement> rows { get { return this.m_rows; } set { this.m_rows = value; } }
public TableMethod()
{
this.m_records = 20;
this.m_total = 20;
this.m_page = 1;
}
}
[Serializable]
public class RowElement
{
public string id;
public string[] cell;
}

Resources