Parsing Lisp S-Expressions with known schema in C# - c#

I'm working with a service that provides data as a Lisp-like S-Expression string. This data is arriving thick and fast, and I want to churn through it as quickly as possible, ideally directly on the byte stream (it's only single-byte characters) without any backtracking. These strings can be quite lengthy and I don't want the GC churn of allocating a string for the whole message.
My current implementation uses CoCo/R with a grammar, but it has a few problems. Due to the backtracking, it assigns the whole stream to a string. It's also a bit fiddly for users of my code to change if they have to. I'd rather have a pure C# solution. CoCo/R also does not allow for the reuse of parser/scanner objects, so I have to recreate them for each message.
Conceptually the data stream can be thought of as a sequence of S-Expressions:
(item 1 apple)(item 2 banana)(item 3 chainsaw)
Parsing this sequence would create three objects. The type of each object can be determined by the first value in the list, in the above case "item". The schema/grammar of the incoming stream is well known.
Before I start coding I'd like to know if there are libraries out there that do this already. I'm sure I'm not the first person to have this problem.
EDIT
Here's a little more detail on what I want as I think the original question may have been a little vague.
Given some SExpressions, such as:
(Hear 12.3 HelloWorld)
(HJ LAJ1 -0.42)
(FRP lf (pos 2.3 1.7 0.4))
I want a list of objects equivalent to this:
{
new HearPerceptorState(12.3, "HelloWorld"),
new HingeJointState("LAJ1", -0.42),
new ForceResistancePerceptorState("lf", new Polar(2.3, 1.7, 0.4))
}
The actual data set I'm working on is a list of perceptors from a robot model in the RoboCup 3D simulated soccer league. I may potentially also need to deserialise another set of related data with a more complex structure.

In my opinion a parse generator is unneccessary to parse simple S-expressions consisting only of lists, numbers and symbols. A hand-written recursive descent parser is probably simpler and at least as fast. The general pattern would look like this (in java, c# should be very similar):
Object readDatum(PushbackReader in) {
int ch = in.read();
return readDatum(in, ch);
}
Object readDatum(PushbackReader in, int ch) {
if (ch == '(')) {
return readList(in, ch);
} else if (isNumber(ch)) {
return readNumber(in, ch);
} else if (isSymbolStart(ch)) {
return readSymbol(in, ch);
} else {
error(ch);
}
}
List readList(PushbackReader in, int lookAhead) {
if (ch != '(') {
error(ch);
}
List result = new List();
while (true) {
int ch = in.read();
if (ch == ')') {
break;
} else if (isWhiteSpace(ch)) {
skipWhiteSpace(in);
} else {
result.append(readDatum(in, ch);
}
}
return result;
}
String readSymbol(PushbackReader in, int ch) {
StringBuilder result = new StringBuilder();
result.append((char)ch);
while (true) {
int ch2 = in.read();
if (isSymbol(ch2)) {
result.append((char)ch2);
} else if (isWhiteSpace(ch2) || ch2 == ')') {
in.unread(ch2);
break;
} else if (ch2 == -1) {
break;
} else {
error(ch2);
}
}
return result.toString();
}

I wrote an S-Expression parser in C# using OMeta#. It can parse the kind of S-Expressions that you are giving in your examples, you just need to add decimal numbers to the parser.
The code is available as SExpression.NET on github and a related article is available here. As an alternative I suggest to take a look at the YaYAML YAML parser for .NET also written using OMeta#.

Consider using Ragel. It's a state machine compiler and produces reasonably fast code.
It may not be apparent from the home page, but Ragel does have C# support.
Here's a trivial example of how to use it in C#

Look at gplex and gppg.
Alternatively, you can trivially translate the S-expressions to XML and let .NET do the rest.

Drew, perhaps you should add some context to the question, otherwise this answer will make no sense to other users, but try this:
CHARACTERS
letter = 'A'..'Z' + 'a'..'z' .
digit = "0123456789" .
messageChar = '\u0020'..'\u007e' - ' ' - '(' - ')' .
TOKENS
double = ['-'] digit { digit } [ '.' digit { digit } ] .
ident = letter { letter | digit | '_' } .
message = messageChar { messageChar } CONTEXT (")") .
Oh, I have to point out that '\u0020' is the unicode SPACE, which you are subsequently removing with "- ' '". Oh, and you can use CONTEXT (')') if you don't need more than one character lookahead.
FWIW: CONTEXT does not consume the enclosed sequence, you must still consume it in your production.
EDIT:
Ok, this seems to work. Really, I mean it this time :)
CHARACTERS
letter = 'A'..'Z' + 'a'..'z' .
digit = "0123456789" .
// messageChar = '\u0020'..'\u007e' - ' ' - '(' - ')' .
TOKENS
double = ['-'] digit { digit } [ '.' digit { digit } ] .
ident = letter { letter | digit | '_' } .
// message = letter { messageChar } CONTEXT (')') .
// MessageText<out string m> = message (. m = t.val; .)
// .
HearExpr<out HeardMessage message> = (. TimeSpan time; Angle direction = Angle.NaN; string messageText; .)
"(hear"
TimeSpan<out time>
( "self" | AngleInDegrees<out direction> )
// MessageText<out messageText> // REMOVED
{ ANY } (. messageText = t.val; .) // MOD
')' (. message = new HeardMessage(time, direction, new Message(messageText)); .)
.

Here's a relatively simple (and hopefully, easy to extend) solution:
public delegate object Acceptor(Token token, string match);
public class Symbol
{
public Symbol(string id) { Id = id ?? Guid.NewGuid().ToString("P"); }
public override string ToString() => Id;
public string Id { get; private set; }
}
public class Token : Symbol
{
internal Token(string id) : base(id) { }
public Token(string pattern, Acceptor acceptor) : base(pattern) { Regex = new Regex(string.Format("^({0})", !string.IsNullOrEmpty(Pattern = pattern) ? Pattern : ".*"), RegexOptions.Compiled); ValueOf = acceptor; }
public string Pattern { get; private set; }
public Regex Regex { get; private set; }
public Acceptor ValueOf { get; private set; }
}
public class SExpressionSyntax
{
private readonly Token Space = Token("\\s+", Echo);
private readonly Token Open = Token("\\(", Echo);
private readonly Token Close = Token("\\)", Echo);
private readonly Token Quote = Token("\\'", Echo);
private Token comment;
private static Exception Error(string message, params object[] arguments) => new Exception(string.Format(message, arguments));
private static object Echo(Token token, string match) => new Token(token.Id);
private static object Quoting(Token token, string match) => NewSymbol(token, match);
private Tuple<Token, string, object> Read(ref string input)
{
if (!string.IsNullOrEmpty(input))
{
var found = null as Match;
var sofar = input;
var tuple = Lexicon.FirstOrDefault(current => (found = current.Item2.Regex.Match(sofar)).Success && (found.Length > 0));
var token = tuple != null ? tuple.Item2 : null;
var match = token != null ? found.Value : null;
input = match != null ? input.Substring(match.Length) : input;
return token != null ? Tuple.Create(token, match, token.ValueOf(token, match)) : null;
}
return null;
}
private Tuple<Token, string, object> Next(ref string input)
{
Tuple<Token, string, object> read;
while (((read = Read(ref input)) != null) && ((read.Item1 == Comment) || (read.Item1 == Space))) ;
return read;
}
public object Parse(ref string input, Tuple<Token, string, object> next)
{
var value = null as object;
if (next != null)
{
var token = next.Item1;
if (token == Open)
{
var list = new List<object>();
while (((next = Next(ref input)) != null) && (next.Item1 != Close))
{
list.Add(Parse(ref input, next));
}
if (next == null)
{
throw Error("unexpected EOF");
}
value = list.ToArray();
}
else if (token == Quote)
{
var quote = next.Item3;
next = Next(ref input);
value = new[] { quote, Parse(ref input, next) };
}
else
{
value = next.Item3;
}
}
else
{
throw Error("unexpected EOF");
}
return value;
}
protected Token TokenOf(Acceptor acceptor)
{
var found = Lexicon.FirstOrDefault(pair => pair.Item2.ValueOf == acceptor);
var token = found != null ? found.Item2 : null;
if ((token == null) && (acceptor != Commenting))
{
throw Error("missing required token definition: {0}", acceptor.Method.Name);
}
return token;
}
protected IList<Tuple<string, Token>> Lexicon { get; private set; }
protected Token Comment { get { return comment = comment ?? TokenOf(Commenting); } }
public static Token Token(string pattern, Acceptor acceptor) => new Token(pattern, acceptor);
public static object Commenting(Token token, string match) => Echo(token, match);
public static object NewSymbol(Token token, string match) => new Symbol(match);
public static Symbol Symbol(object value) => value as Symbol;
public static string Moniker(object value) => Symbol(value) != null ? Symbol(value).Id : null;
public static string ToString(object value)
{
return
value is object[] ?
(
((object[])value).Length > 0 ?
((object[])value).Aggregate(new StringBuilder("("), (result, obj) => result.AppendFormat(" {0}", ToString(obj))).Append(" )").ToString()
:
"( )"
)
:
(value != null ? (value is string ? string.Concat('"', (string)value, '"') : (value is bool ? value.ToString().ToLower() : value.ToString())).Replace("\\\r\n", "\r\n").Replace("\\\n", "\n").Replace("\\t", "\t").Replace("\\n", "\n").Replace("\\r", "\r").Replace("\\\"", "\"") : null) ?? "(null)";
}
public SExpressionSyntax()
{
Lexicon = new List<Tuple<string, Token>>();
Include(Space, Open, Close, Quote);
}
public SExpressionSyntax Include(params Token[] tokens)
{
foreach (var token in tokens)
{
Lexicon.Add(new Tuple<string, Token>(token.Id, token));
}
return this;
}
public object Parse(string input)
{
var next = Next(ref input);
var value = Parse(ref input, next);
if ((next = Next(ref input)) != null)
{
throw Error("unexpected ", next.Item1);
}
return value;
}
}
public class CustomSExpressionSyntax : SExpressionSyntax
{
public CustomSExpressionSyntax()
: base()
{
Include
(
// "//" comments
Token("\\/\\/.*", SExpressionSyntax.Commenting),
// Obvious
Token("false", (token, match) => false),
Token("true", (token, match) => true),
Token("null", (token, match) => null),
Token("\\-?[0-9]+\\.[0-9]+", (token, match) => double.Parse(match)),
Token("\\-?[0-9]+", (token, match) => int.Parse(match)),
// String literals
Token("\\\"(\\\\\\n|\\\\t|\\\\n|\\\\r|\\\\\\\"|[^\\\"])*\\\"", (token, match) => match.Substring(1, match.Length - 2)),
// Identifiers
Token("[_A-Za-z][_0-9A-Za-z]*", NewSymbol)
);
}
}
public class Node { }
public class HearPerceptorState : Node
{
public string Ident { get; set; }
public double Value { get; set; }
}
public class HingeJointState : Node
{
public string Ident { get; set; }
public double Value { get; set; }
}
public class Polar : Tuple<double, double, double>
{
public Polar(double a, double b, double c) : base(a, b, c) { }
}
public class ForceResistancePerceptorState : Node
{
public string Ident { get; set; }
public Polar Polar { get; set; }
}
public class Test
{
public static void Main()
{
var input = #"
(
(Hear 12.3 HelloWorld)
(HJ LAJ1 -0.42)
(FRP lf (pos 2.3 1.7 0.4))
)
";
// visit DRY helpers
Func<object, object[]> asRecord = value => (object[])value;
Func<object, Symbol> symbol = value => SExpressionSyntax.Symbol(value);
Func<object, string> identifier = value => symbol(value).Id;
// the SExpr visit, proper
Func<object[], Node[]> visitAll = null;
Func<object[], Node> visitHear = null;
Func<object[], Node> visitHJ = null;
Func<object[], Node> visitFRP = null;
visitAll =
all =>
all.
Select
(
item =>
symbol(asRecord(item)[0]).Id != "Hear" ?
(
symbol(asRecord(item)[0]).Id != "HJ" ?
visitFRP(asRecord(item))
:
visitHJ(asRecord(item))
)
:
visitHear(asRecord(item))
).
ToArray();
visitHear =
item =>
new HearPerceptorState { Value = (double)asRecord(item)[1], Ident = identifier(asRecord(item)[2]) };
visitHJ =
item =>
new HingeJointState { Ident = identifier(asRecord(item)[1]), Value = (double)asRecord(item)[2] };
visitFRP =
item =>
new ForceResistancePerceptorState
{
Ident = identifier(asRecord(item)[1]),
Polar =
new Polar
(
(double)asRecord(asRecord(item)[2])[1],
(double)asRecord(asRecord(item)[2])[2],
(double)asRecord(asRecord(item)[2])[3]
)
};
var syntax = new CustomSExpressionSyntax();
var sexpr = syntax.Parse(input);
var nodes = visitAll(asRecord(sexpr));
Console.WriteLine("SO_3051254");
Console.WriteLine();
Console.WriteLine(nodes.Length == 3);
Console.WriteLine(nodes[0] is HearPerceptorState);
Console.WriteLine(nodes[1] is HingeJointState);
Console.WriteLine(nodes[2] is ForceResistancePerceptorState);
}
}
Testable here:
https://repl.it/CnLC/1
'HTH,

Related

Convert from Object to QueryString [duplicate]

How do I serialize an object into query-string format? I can't seem to find an answer on google. Thanks.
Here is the object I will serialize as an example.
public class EditListItemActionModel
{
public int? Id { get; set; }
public int State { get; set; }
public string Prefix { get; set; }
public string Index { get; set; }
public int? ParentID { get; set; }
}
I'm 99% sure there's no built-in utility method for this. It's not a very common task, since a web server doesn't typically respond with a URLEncoded key/value string.
How do you feel about mixing reflection and LINQ? This works:
var foo = new EditListItemActionModel() {
Id = 1,
State = 26,
Prefix = "f",
Index = "oo",
ParentID = null
};
var properties = from p in foo.GetType().GetProperties()
where p.GetValue(foo, null) != null
select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(foo, null).ToString());
// queryString will be set to "Id=1&State=26&Prefix=f&Index=oo"
string queryString = String.Join("&", properties.ToArray());
Update:
To write a method that returns the QueryString representation of any 1-deep object, you could do this:
public string GetQueryString(object obj) {
var properties = from p in obj.GetType().GetProperties()
where p.GetValue(obj, null) != null
select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());
return String.Join("&", properties.ToArray());
}
// Usage:
string queryString = GetQueryString(foo);
You could also make it an extension method without much additional work
public static class ExtensionMethods {
public static string GetQueryString(this object obj) {
var properties = from p in obj.GetType().GetProperties()
where p.GetValue(obj, null) != null
select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());
return String.Join("&", properties.ToArray());
}
}
// Usage:
string queryString = foo.GetQueryString();
Using Json.Net it would be much easier, by serializing and then deserializing to key value pairs.
Here is a code example:
using Newtonsoft.Json;
using System.Web;
string ObjToQueryString(object obj)
{
var step1 = JsonConvert.SerializeObject(obj);
var step2 = JsonConvert.DeserializeObject<IDictionary<string, string>>(step1);
var step3 = step2.Select(x => HttpUtility.UrlEncode(x.Key) + "=" + HttpUtility.UrlEncode(x.Value));
return string.Join("&", step3);
}
Building on the good ideas from other comments, I have made a generic extension method .ToQueryString(), which can be used on any object.
public static class UrlHelpers
{
public static string ToQueryString(this object request, string separator = ",")
{
if (request == null)
throw new ArgumentNullException("request");
// Get all properties on the object
var properties = request.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(request, null) != null)
.ToDictionary(x => x.Name, x => x.GetValue(request, null));
// Get names for all IEnumerable properties (excl. string)
var propertyNames = properties
.Where(x => !(x.Value is string) && x.Value is IEnumerable)
.Select(x => x.Key)
.ToList();
// Concat all IEnumerable properties into a comma separated string
foreach (var key in propertyNames)
{
var valueType = properties[key].GetType();
var valueElemType = valueType.IsGenericType
? valueType.GetGenericArguments()[0]
: valueType.GetElementType();
if (valueElemType.IsPrimitive || valueElemType == typeof (string))
{
var enumerable = properties[key] as IEnumerable;
properties[key] = string.Join(separator, enumerable.Cast<object>());
}
}
// Concat all key/value pairs into a string separated by ampersand
return string.Join("&", properties
.Select(x => string.Concat(
Uri.EscapeDataString(x.Key), "=",
Uri.EscapeDataString(x.Value.ToString()))));
}
}
It will also work for objects that have properties of the type Array and generic Lists if they only contain primitives or strings.
Try it out, comments are welcome: Serialize object into a query string with Reflection
Based on the the popular answers, I needed to update the code to support arrays as well. Sharing the implementation:
public string GetQueryString(object obj)
{
var result = new List<string>();
var props = obj.GetType().GetProperties().Where(p => p.GetValue(obj, null) != null);
foreach (var p in props)
{
var value = p.GetValue(obj, null);
var enumerable = value as ICollection;
if (enumerable != null)
{
result.AddRange(from object v in enumerable select string.Format("{0}={1}", p.Name, HttpUtility.UrlEncode(v.ToString())));
}
else
{
result.Add(string.Format("{0}={1}", p.Name, HttpUtility.UrlEncode(value.ToString())));
}
}
return string.Join("&", result.ToArray());
}
It will also be useful for nested objects
public static class HttpQueryStrings
{
private static readonly StringBuilder _query = new();
public static string ToQueryString<T>(this T #this) where T : class
{
_query.Clear();
BuildQueryString(#this, "");
if (_query.Length > 0) _query[0] = '?';
return _query.ToString();
}
private static void BuildQueryString<T>(T? obj, string prefix = "") where T : class
{
if (obj == null) return;
foreach (var p in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (p.GetValue(obj, Array.Empty<object>()) != null)
{
var value = p.GetValue(obj, Array.Empty<object>());
if (p.PropertyType.IsArray && value?.GetType() == typeof(DateTime[]))
foreach (var item in (DateTime[])value)
_query.Append($"&{prefix}{p.Name}={item.ToString("yyyy-MM-dd")}");
else if (p.PropertyType.IsArray)
foreach (var item in (Array)value!)
_query.Append($"&{prefix}{p.Name}={item}");
else if (p.PropertyType == typeof(string))
_query.Append($"&{prefix}{p.Name}={value}");
else if (p.PropertyType == typeof(DateTime) && !value!.Equals(Activator.CreateInstance(p.PropertyType))) // is not default
_query.Append($"&{prefix}{p.Name}={((DateTime)value).ToString("yyyy-MM-dd")}");
else if (p.PropertyType.IsValueType && !value!.Equals(Activator.CreateInstance(p.PropertyType))) // is not default
_query.Append($"&{prefix}{p.Name}={value}");
else if (p.PropertyType.IsClass)
BuildQueryString(value, $"{prefix}{p.Name}.");
}
}
}
}
An example of using the solution:
string queryString = new
{
date = new DateTime(2020, 1, 1),
myClass = new MyClass
{
FirstName = "john",
LastName = "doe"
},
myArray = new int[] { 1, 2, 3, 4 },
}.ToQueryString();
Perhaps this Generic approach will be useful:
public static string ConvertToQueryString<T>(T entity) where T: class
{
var props = typeof(T).GetProperties();
return $"?{string.Join('&', props.Where(r=> r.GetValue(entity) != null).Select(r => $"{HttpUtility.UrlEncode(r.Name)}={HttpUtility.UrlEncode(r.GetValue(entity).ToString())}"))}";
}
public static class UrlHelper
{
public static string ToUrl(this Object instance)
{
var urlBuilder = new StringBuilder();
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
for (int i = 0; i < properties.Length; i++)
{
urlBuilder.AppendFormat("{0}={1}&", properties[i].Name, properties[i].GetValue(instance, null));
}
if (urlBuilder.Length > 1)
{
urlBuilder.Remove(urlBuilder.Length - 1, 1);
}
return urlBuilder.ToString();
}
}
This my solution:
public static class ObjectExtensions
{
public static string ToQueryString(this object obj)
{
if (!obj.GetType().IsComplex())
{
return obj.ToString();
}
var values = obj
.GetType()
.GetProperties()
.Where(o => o.GetValue(obj, null) != null);
var result = new QueryString();
foreach (var value in values)
{
if (!typeof(string).IsAssignableFrom(value.PropertyType)
&& typeof(IEnumerable).IsAssignableFrom(value.PropertyType))
{
var items = value.GetValue(obj) as IList;
if (items.Count > 0)
{
for (int i = 0; i < items.Count; i++)
{
result = result.Add(value.Name, ToQueryString(items[i]));
}
}
}
else if (value.PropertyType.IsComplex())
{
result = result.Add(value.Name, ToQueryString(value));
}
else
{
result = result.Add(value.Name, value.GetValue(obj).ToString());
}
}
return result.Value;
}
private static bool IsComplex(this Type type)
{
var typeInfo = type.GetTypeInfo();
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))
{
// nullable type, check if the nested type is simple.
return IsComplex(typeInfo.GetGenericArguments()[0]);
}
return !(typeInfo.IsPrimitive
|| typeInfo.IsEnum
|| type.Equals(typeof(Guid))
|| type.Equals(typeof(string))
|| type.Equals(typeof(decimal)));
}
}
I use this extension for my integration test, it works perfectly :)
Just another variation of the above, but I wanted to utilize the existing DataMember attributes in my model class, so only the properties I want to serialize are sent to the server in the url in the GET request.
public string ToQueryString(object obj)
{
if (obj == null) return "";
return "?" + string.Join("&", obj.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(DataMemberAttribute)) && p.GetValue(obj, null) != null)
.Select(p => $"{p.Name}={Uri.EscapeDataString(p.GetValue(obj).ToString())}"));
}
Here is something I wrote that does what you need.
public string CreateAsQueryString(PageVariables pv) //Pass in your EditListItemActionModel instead
{
int i = 0;
StringBuilder sb = new StringBuilder();
foreach (var prop in typeof(PageVariables).GetProperties())
{
if (i != 0)
{
sb.Append("&");
}
var x = prop.GetValue(pv, null).ToString();
if (x != null)
{
sb.Append(prop.Name);
sb.Append("=");
sb.Append(x.ToString());
}
i++;
}
Formating encoding = new Formating();
// I am encoding my query string - but you don''t have to
return "?" + HttpUtility.UrlEncode(encoding.RC2Encrypt(sb.ToString()));
}
I was looking for a solution to this for a Windows 10 (UWP) App. Taking the Relection approach suggested by Dave, and after adding the Microsoft.AspNet.WebApi.Client Nuget package, I used the following code,
which handles Url Encoding of the property values:
private void AddContentAsQueryString(ref Uri uri, object content)
{
if ((uri != null) && (content != null))
{
UriBuilder builder = new UriBuilder(uri);
HttpValueCollection query = uri.ParseQueryString();
IEnumerable<PropertyInfo> propInfos = content.GetType().GetRuntimeProperties();
foreach (var propInfo in propInfos)
{
object value = propInfo.GetValue(content, null);
query.Add(propInfo.Name, String.Format("{0}", value));
}
builder.Query = query.ToString();
uri = builder.Uri;
}
}
A simple approach that supports list properties:
public static class UriBuilderExtensions
{
public static UriBuilder SetQuery<T>(this UriBuilder builder, T parameters)
{
var fragments = typeof(T).GetProperties()
.Where(property => property.CanRead)
.Select(property => new
{
property.Name,
Value = property.GetMethod.Invoke(parameters, null)
})
.Select(pair => new
{
pair.Name,
List = (!(pair.Value is string) && pair.Value is IEnumerable list ? list.Cast<object>() : new[] { pair.Value })
.Select(element => element?.ToString())
.Where(element => !string.IsNullOrEmpty(element))
})
.Where(pair => pair.List.Any())
.SelectMany(pair => pair.List.Select(value => Uri.EscapeDataString(pair.Name) + '=' + Uri.EscapeDataString(value)));
builder.Query = string.Join("&", fragments);
return builder;
}
}
A faster solution which is as fast as spelling out the code to serialize each type:
public static class UriBuilderExtensions
{
public static UriBuilder SetQuery<TSource>(this UriBuilder builder, TSource parameters)
{
var fragments = Cache<TSource>.Properties
.Select(property => new
{
property.Name,
List = property.FetchValue(parameters)?.Where(item => !string.IsNullOrEmpty(item))
})
.Where(parameter => parameter.List?.Any() ?? false)
.SelectMany(pair => pair.List.Select(item => Uri.EscapeDataString(pair.Name) + '=' + Uri.EscapeDataString(item)));
builder.Query = string.Join("&", fragments);
return builder;
}
/// <summary>
/// Caches dynamically emitted code which converts a types getter property values to a list of strings.
/// </summary>
/// <typeparam name="TSource">The type of the object being serialized</typeparam>
private static class Cache<TSource>
{
public static readonly IEnumerable<IProperty> Properties =
typeof(TSource).GetProperties()
.Where(propertyInfo => propertyInfo.CanRead)
.Select(propertyInfo =>
{
var source = Expression.Parameter(typeof(TSource));
var getter = Expression.Property(source, propertyInfo);
var cast = Expression.Convert(getter, typeof(object));
var expression = Expression.Lambda<Func<TSource, object>>(cast, source).Compile();
return new Property
{
Name = propertyInfo.Name,
FetchValue = typeof(IEnumerable).IsAssignableFrom(propertyInfo.PropertyType) && propertyInfo.PropertyType != typeof(string) ?
CreateListFetcher(expression) :
CreateValueFetcher(expression)
};
})
.OrderBy(propery => propery.Name)
.ToArray();
/// <summary>
/// Creates a function which serializes a <see cref="IEnumerable"/> property value to a list of strings.
/// </summary>
/// <param name="get">A lambda function which retrieves the property value from a given source object.</param>
private static Func<TSource, IEnumerable<string>> CreateListFetcher(Func<TSource, object> get)
=> obj => ((IEnumerable)get(obj))?.Cast<object>().Select(item => item?.ToString());
/// <summary>
/// Creates a function which serializes a <see cref="object"/> property value to a list of strings.
/// </summary>
/// <param name="get">A lambda function which retrieves the property value from a given source object.</param>
private static Func<TSource, IEnumerable<string>> CreateValueFetcher(Func<TSource, object> get)
=> obj => new[] { get(obj)?.ToString() };
public interface IProperty
{
string Name { get; }
Func<TSource, IEnumerable<string>> FetchValue { get; }
}
private class Property : IProperty
{
public string Name { get; set; }
public Func<TSource, IEnumerable<string>> FetchValue { get; set; }
}
}
}
An example of using either solution:
var url = new UriBuilder("test.com").SetQuerySlow(new
{
Days = new[] { WeekDay.Tuesday, WeekDay.Wednesday },
Time = TimeSpan.FromHours(14.5),
Link = "conferences.com/apple/stream/15",
Pizzas = default(int?)
}).Uri;
Output:
http://test.com/Days=Tuesday&Days=Wednesday&Time=14:30:00&Link=conferences.com%2Fapple%2Fstream%2F15
Neither of the solutions handle exotic types, indexed parameters, or nested parameters.
When manual serialization is simpler, this c#7/.net4.7 approach can help:
public static class QueryParameterExtensions
{
public static UriBuilder SetQuery(this UriBuilder builder, params (string Name, object Obj)[] parameters)
{
var list = parameters
.Select(parameter => new
{
parameter.Name,
Values = SerializeToList(parameter.Obj).Where(value => !string.IsNullOrEmpty(value))
})
.Where(parameter => parameter.Values.Any())
.SelectMany(parameter => parameter.Values.Select(item => Uri.EscapeDataString(parameter.Name) + '=' + Uri.EscapeDataString(item)));
builder.Query = string.Join("&", list);
return builder;
}
private static IEnumerable<string> SerializeToList(object obj)
{
switch (obj)
{
case string text:
yield return text;
break;
case IEnumerable list:
foreach (var item in list)
{
yield return SerializeToValue(item);
}
break;
default:
yield return SerializeToValue(obj);
break;
}
}
private static string SerializeToValue(object obj)
{
switch (obj)
{
case bool flag:
return flag ? "true" : null;
case byte number:
return number == default(byte) ? null : number.ToString();
case short number:
return number == default(short) ? null : number.ToString();
case ushort number:
return number == default(ushort) ? null : number.ToString();
case int number:
return number == default(int) ? null : number.ToString();
case uint number:
return number == default(uint) ? null : number.ToString();
case long number:
return number == default(long) ? null : number.ToString();
case ulong number:
return number == default(ulong) ? null : number.ToString();
case float number:
return number == default(float) ? null : number.ToString();
case double number:
return number == default(double) ? null : number.ToString();
case DateTime date:
return date == default(DateTime) ? null : date.ToString("s");
case TimeSpan span:
return span == default(TimeSpan) ? null : span.ToString();
case Guid guid:
return guid == default(Guid) ? null : guid.ToString();
default:
return obj?.ToString();
}
}
}
Example usage:
var uri = new UriBuilder("test.com")
.SetQuery(("days", standup.Days), ("time", standup.Time), ("link", standup.Link), ("pizzas", standup.Pizzas))
.Uri;
Output:
http://test.com/?days=Tuesday&days=Wednesday&time=14:30:00&link=conferences.com%2Fapple%2Fstream%2F15
In addition to existing answers
public static string ToQueryString<T>(this T input)
{
if (input == null)
{
return string.Empty;
}
var queryStringBuilder = new StringBuilder("?");
var properties = input.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
var value = property.GetValue(input);
if (value is null || property.HasIgnoreDataMember())
continue;
queryStringBuilder.AppendFormat("{0}={1}&", property.GetName(), HttpUtility.UrlEncode(value.ToString()));
}
queryStringBuilder.Length--;
return queryStringBuilder.ToString();
}
private static bool HasIgnoreDataMember(this PropertyInfo propertyInfo)
{
return propertyInfo.GetCustomAttribute(typeof(IgnoreDataMemberAttribute), true) is not null;
}
private static DataMemberAttribute GetDataMemberAttribute(this PropertyInfo propertyInfo)
{
return propertyInfo.GetCustomAttribute<DataMemberAttribute>();
}
private static T GetCustomAttribute<T>(this PropertyInfo propertyInfo) where T : class
{
return propertyInfo.GetCustomAttribute(typeof(T), true) as T;
}
private static string GetName(this PropertyInfo propertyInfo)
{
return propertyInfo.GetDataMemberAttribute()?.Name ?? propertyInfo.Name;
}
}
Usage: var queryString = object.ToQueryString()
Faced with a similar situation what I did, is to XML serialize the object and pass it around as query string parameter.
The difficulty with this approach was that despite encoding, the receiving form throws exception saying "potentially dangerous request...". The way I got around was to encrypt the serialized object and then encode to pass it around as query string parameter. Which in turn made the query string tamper proof (bonus wandering into the HMAC territory)!
FormA XML serializes an object > encrypts the serialized string > encode > pass as query string to FormB
FormB decrypts the query parameter value (as request.querystring decodes also) > deserialize the resulting XML string to object using XmlSerializer.
I can share my VB.NET code upon request to howIdidit-at-applecart-dot-net

Custom array sort

Each item/string in my array starts with two letters followed by two or three numbers and then sometimes followed by another letter.
Examples, RS01 RS10 RS32A RS102 RS80 RS05A RS105A RS105B
I tried to sort this using the default Array.Sort but it came back with this...
RS01
RS05A
RS10
RS102
RS105A
RS105B
RS32A
RS80
But I need it like this..
RS01
RS05A
RS10
RS32A
RS80
RS102
RS105A
RS105B
Any Ideas?
Here is sorting with custom comparison delegate and regular expressions:
string[] array = { "RS01", "RS10", "RS32A", "RS102",
"RS80", "RS05A", "RS105A", "RS105B" };
Array.Sort(array, (s1, s2) =>
{
Regex regex = new Regex(#"([a-zA-Z]+)(\d+)([a-zA-Z]*)");
var match1 = regex.Match(s1);
var match2 = regex.Match(s2);
// prefix
int result = match1.Groups[1].Value.CompareTo(match2.Groups[1].Value);
if (result != 0)
return result;
// number
result = Int32.Parse(match1.Groups[2].Value)
.CompareTo(Int32.Parse(match2.Groups[2].Value));
if (result != 0)
return result;
// suffix
return match1.Groups[3].Value.CompareTo(match2.Groups[3].Value);
});
UPDATE (little refactoring, and moving all stuff to separate comparer class). Usage:
Array.Sort(array, new RSComparer());
Comparer itself:
public class RSComparer : IComparer<string>
{
private Dictionary<string, RS> entries = new Dictionary<string, RS>();
public int Compare(string x, string y)
{
if (!entries.ContainsKey(x))
entries.Add(x, new RS(x));
if (!entries.ContainsKey(y))
entries.Add(y, new RS(y));
return entries[x].CompareTo(entries[y]);
}
private class RS : IComparable
{
public RS(string value)
{
Regex regex = new Regex(#"([A-Z]+)(\d+)([A-Z]*)");
var match = regex.Match(value);
Prefix = match.Groups[1].Value;
Number = Int32.Parse(match.Groups[2].Value);
Suffix = match.Groups[3].Value;
}
public string Prefix { get; private set; }
public int Number { get; private set; }
public string Suffix { get; private set; }
public int CompareTo(object obj)
{
RS rs = (RS)obj;
int result = Prefix.CompareTo(rs.Prefix);
if (result != 0)
return result;
result = Number.CompareTo(rs.Number);
if (result != null)
return result;
return Suffix.CompareTo(rs.Suffix);
}
}
}
You can use this linq query:
var strings = new[] {
"RS01","RS05A","RS10","RS102","RS105A","RS105B","RS32A","RS80"
};
strings = strings.Select(str => new
{
str,
num = int.Parse(String.Concat(str.Skip(2).TakeWhile(Char.IsDigit))),
version = String.Concat(str.Skip(2).SkipWhile(Char.IsDigit))
})
.OrderBy(x => x.num).ThenBy(x => x.version)
.Select(x => x.str)
.ToArray();
DEMO
Result:
RS01
RS05A
RS10
RS32A
RS80
RS102
RS105A
RS105B
You'll want to write a custom comparer class implementing IComparer<string>; it's pretty straightforward to break your strings into components. When you call Array.Sort, give it an instance of your comparer and you'll get the results you want.

String condition in List<T>

I have a class named HomeInfo
public class HomeInfo
{
public int ID {get;set;}
public string OwnerName {get;set;}
public string Address {get;set;}
public int EstimatedValue {get;set;}
}
I get data from server and i add that into List<HomeInfo> listHomeInfo
Now in my GUI I need to allow filtering results based on user input, so my client wants a textbox for Estimated Value and he wants to enter text there like '>30k and <50k' or '>50k', I parsed and converted these values and created object of class
public class ExpressionValue
{
public float? FirstDigit { get; set; }
/// <summary>
/// >, >=, <,<=
/// </summary>
public string FirstExpCondition { get; set; }
/// <summary>
/// OR, AND
/// </summary>
public string ConditionOperator { get; set; }
public float SecondDigit { get; set; }
public string SecondExpCondition { get; set; }
}
Using an ExpressionValue object I am able to create a proper condition string.
Now I am able to create a condition string like 'EstimatedValue > 30000 AND EstimatedValue < 60000' or 'EstimatedValue < 50000'
I don't know how can I effectively apply this condition on 'List listHomeInfo' since as far i know List<T>.Where() doesn't support string condition. I know a way around it is to convert the list to DataTable and use Select(string expression) method and then convert DataRow[] to List<HomeInfo>, but I think there may be a better way to achieve this.
[EDIT]
I created two methods to help me out but i am getting exception "The binary operator GreaterThan is not defined for the types 'System.Single' and 'System.Double'." when creating BinaryExpression.
public static Expression<Func<T, bool>> ParseExpressionCondition<T>(string expression, string fieldName)
{
try
{
string decimalNumRegex = #"\d+(\.\d{1,2})?";
List<string> matchPatterns = new List<string>() { ">=", ">", "<=", "<" };
ExpressionValue expValue = new ExpressionValue();
Dictionary<string, string> conditions = new Dictionary<string, string>();
var parameter = Expression.Parameter(typeof(T), typeof(T).ToString());
//var lhs = Expression.GreaterThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(30000));
BinaryExpression lhs = null, rhs = null;
object objectValue = null;
string condOperator = null;
foreach (string pattern in matchPatterns)
{
Match match = Regex.Match(expression, pattern + decimalNumRegex);
if (match.Success)
{
//get digit part
double digit = double.Parse(Regex.Match(match.Value, decimalNumRegex).Value);
if (!expValue.FirstDigit.HasValue)
{
objectValue = digit;
condOperator = match.Value.Replace(digit.ToString(), "");
lhs = GetBinaryExpression(parameter, fieldName, objectValue, condOperator);
}
else
{
objectValue = digit;
condOperator = match.Value.Replace(digit.ToString(), "");
rhs = GetBinaryExpression(parameter, fieldName, objectValue, condOperator);
}
}
}
if (expression.ToLower().Contains("and"))
return Expression.Lambda<Func<T, bool>>(Expression.And(lhs, rhs), parameter);
else if (expression.ToLower().Contains("or"))
return Expression.Lambda<Func<T, bool>>(Expression.Or(lhs, rhs), parameter);
return null;
}
catch (Exception ex)
{
Logger.WriteLog(ex);
throw ex;
}
}
private static BinaryExpression GetBinaryExpression(ParameterExpression paraExp, string fieldName, object expressionValue, string conditionOperator)
{
try
{
BinaryExpression binExp = null;
MemberExpression expressionLeft = Expression.Property(paraExp, fieldName);
Expression expressionRight = Expression.Constant(expressionValue );
switch (conditionOperator)
{
case ">":
binExp = Expression.GreaterThan(expressionLeft, expressionRight);
break;
case ">=":
binExp = Expression.GreaterThanOrEqual(expressionLeft, expressionRight);
break;
case "<":
binExp = Expression.LessThan(expressionLeft, expressionRight);
break;
case "<=":
binExp = Expression.LessThanOrEqual(expressionLeft, expressionRight);
break;
}
return binExp;
}
catch (Exception ex)
{
throw ex;
}
}
Assuming you have parsing logic already implemented to some extent, I would suggest generating an expression tree (rather than using your own custom ExpressionValue class).
E.g. 'EstimatedValue > 30000 AND EstimatedValue < 60000' could become an expression tree of the form:
var parameter = Expression.Parameter(typeof(HomeInfo), "homeInfo");
var lhs = Expression.GreaterThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(30000));
var rhs = Expression.LessThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(60000));
var expression = Expression.Lambda<Func<HomeInfo, bool>>(Expression.AndAlso(lhs, rhs), parameter);
The list can then be queried using the generated expression tree as follows:
var results = listHomeInfo.AsQueryable().Where(expression);
Do not reinvent the wheel: NCalc does that kind of stuff already.
With a variable named EstimatedValue and a user defined expression UserExpression, in NCalc you'd do:
myList.Where(elem => new Expression(EstimatedValue.ToString() + UserExpression).Evaluate());
In your position I'd create a mini rule engine.
So
public abstract class ExpressionBase {
public float value {get;set;}
}
public class GreaterThanExpression : ExpressionBase {}
public class LessThanExpression : ExpressionBase {}
Now as you parse the entered string you can build a list of the expressions entered and then apply them to an IQueryable in the order you want to.
Write a LINQ extention method....
public static IEnumerable<HomeInfo> PassesExpression(this IEnumerable<HomeInfo> homes, ExpressionValue expression)
{
foreach(HomeInfo home in homes)
{
bool one, two;
if(expression.FirstExpCondition == '>')
one = (home.EstimatedValue > expression.FirstDigit);
else if(expression.FirstExpCondition == '>=')
one = (home.EstimatedValue >= expression.FirstDigit);
else if(expression.FirstExpCondition == '<')
one = (home.EstimatedValue < expression.FirstDigit);
else if(expression.FirstExpCondition == '<=')
one = (home.EstimatedValue <= expression.FirstDigit);
if(expression.SecondExpCondition == '>')
two = (home.EstimatedValue > expression.SecondDigit);
else if(expression.SecondExpCondition == '>=')
two = (home.EstimatedValue >= expression.SecondDigit);
else if(expression.SecondExpCondition == '<')
two = (home.EstimatedValue < expression.SecondDigit);
else if(expression.SecondExpCondition == '<=')
two = (home.EstimatedValue <= expression.SecondDigit);
if((expression.ConditionOperator == 'OR' && (one || two)) || (expression.ConditionOperator == 'AND' && (one && two)))
yield return home;
}
}
I usually have two textboxes for value ranges. One for the minimum value, one for the maximum value. They can be empty, if the limit is not required
int? min = null
int? max = null;
int i;
if (Int32.TryParse(txtMin.Text, out i) min = i;
if (Int32.TryParse(txtMax.Text, out i) max = i;
string name = txtName.Text;
With these definitions you can combine where clauses dynamically
IEnumerable<HomeInfo> result = list;
if (min.HasValue) result = result.Where(h => h.EstimatedValue >= min.Value);
if (max.HasValue) result = result.Where(h => h.EstimatedValue <= max.Value);
if (name != "")
result = result.Where(
h => h.OwnerName.StartsWith(name, StringComparison.OrdinalIgnoreCase)
);
use LinqToObjects
List<HomeInfo> homeInfos = new List<HomeInfo>();
homeInfos.Where(x => x.EstimatedValue > 1000).Where(x => x.EstimatedValue < 10000);

Return a finite set matching a regex expression

Something similar to http://regexio.com/prototype.html, I'm trying to get a set matching a particular regex.
Basically, you need to parse the regular expression and then, instead of reading input while walking the parsed expression, output the variants.
I have hacked the following program doing what you need for a very simple regular expression (only alternate options using |, iteration using *, grouping using (), and escaping using \ is supported). Note that the iteration is done simply 0–5 times, conversion to possibly infinite iteration left as an exercise for the reader ;-).
I have used a straightforward recursive-descent parser building an abstract syntax tree in memory; this tree is in the end walked and all possible sets are built. The solution is probably not optimal at all, but it works. Enjoy:
public class TestPrg
{
static void Main()
{
var expression = new RegexParser("a(b|c)*d").Parse();
foreach (var item in expression.Generate())
{
Console.WriteLine(item);
}
}
}
public static class EnumerableExtensions
{
// Build a Cartesian product of a sequence of sequences
// Code by Eric Lippert, copied from <http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx>
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item }));
}
}
public class RegexParser
{
private const char EOF = '\x0000';
private readonly string str;
private char curr;
private int pos;
public RegexParser(string s)
{
str = s;
}
public RegExpression Parse()
{
pos = -1;
Read();
return ParseExpression();
}
private void Read()
{
++pos;
curr = pos < str.Length ? str[pos] : EOF;
}
private RegExpression ParseExpression()
{
var term = ParseTerm();
if (curr == '|')
{
Read();
var secondExpr = ParseExpression();
return new Variants(term, secondExpr);
}
else
{
return term;
}
}
private RegExpression ParseTerm()
{
var factor = ParseFactor();
if (curr != '|' && curr != '+' && curr != '*' && curr != ')' && curr != EOF)
{
var secondTerm = ParseTerm();
return new Concatenation(factor, secondTerm);
}
else
{
return factor;
}
}
private RegExpression ParseFactor()
{
var element = ParseElement();
if (curr == '*')
{
Read();
return new Repeat(element);
}
else
{
return element;
}
}
private RegExpression ParseElement()
{
switch (curr)
{
case '(':
Read();
var expr = ParseExpression();
if (curr != ')') throw new FormatException("Closing paren expected");
Read();
return expr;
case '\\':
Read();
var escapedChar = curr;
Read();
return new Literal(escapedChar);
default:
var literal = curr;
Read();
return new Literal(literal);
}
}
}
public abstract class RegExpression
{
protected static IEnumerable<RegExpression> Merge<T>(RegExpression head, RegExpression tail, Func<T, IEnumerable<RegExpression>> selector)
where T : RegExpression
{
var other = tail as T;
if (other != null)
{
return new[] { head }.Concat(selector(other));
}
else
{
return new[] { head, tail };
}
}
public abstract IEnumerable<string> Generate();
}
public class Variants : RegExpression
{
public IEnumerable<RegExpression> Subexpressions { get; private set; }
public Variants(RegExpression term, RegExpression rest)
{
Subexpressions = Merge<Variants>(term, rest, c => c.Subexpressions);
}
public override IEnumerable<string> Generate()
{
return Subexpressions.SelectMany(sub => sub.Generate());
}
}
public class Concatenation : RegExpression
{
public IEnumerable<RegExpression> Subexpressions { get; private set; }
public Concatenation(RegExpression factor, RegExpression rest)
{
Subexpressions = Merge<Concatenation>(factor, rest, c => c.Subexpressions);
}
public override IEnumerable<string> Generate()
{
foreach (var variant in Subexpressions.Select(sub => sub.Generate()).CartesianProduct())
{
var builder = new StringBuilder();
foreach (var item in variant) builder.Append(item);
yield return builder.ToString();
}
}
}
public class Repeat : RegExpression
{
public RegExpression Expr { get; private set; }
public Repeat(RegExpression expr)
{
Expr = expr;
}
public override IEnumerable<string> Generate()
{
foreach (var subexpr in Expr.Generate())
{
for (int cnt = 0; cnt < 5; ++cnt)
{
var builder = new StringBuilder(subexpr.Length * cnt);
for (int i = 0; i < cnt; ++i) builder.Append(subexpr);
yield return builder.ToString();
}
}
}
}
public class Literal : RegExpression
{
public char Ch { get; private set; }
public Literal(char c)
{
Ch = c;
}
public override IEnumerable<string> Generate()
{
yield return new string(Ch, 1);
}
}
My answer to a similar question might work for you. If the regex doesn't involve
any * operations (so that the language it recognizes is finite), it should be easy
to rewrite the regex as a BNF grammar, then do a bottom-up analysis producing
the finite sets corresponding to each nonterminal symbol until you reach the
start symbol, at which point you're done.

What are your favorite extension methods for C#? (codeplex.com/extensionoverflow)

Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
Let's make a list of answers where you post your excellent and favorite extension methods.
The requirement is that the full code must be posted and a example and an explanation on how to use it.
Based on the high interest in this topic I have setup an Open Source Project called extensionoverflow on Codeplex.
Please mark your answers with an acceptance to put the code in the Codeplex project.
Please post the full sourcecode and not a link.
Codeplex News:
24.08.2010 The Codeplex page is now here: http://extensionoverflow.codeplex.com/
11.11.2008 XmlSerialize / XmlDeserialize is now Implemented and Unit Tested.
11.11.2008 There is still room for more developers. ;-) Join NOW!
11.11.2008 Third contributer joined ExtensionOverflow, welcome to BKristensen
11.11.2008 FormatWith is now Implemented and Unit Tested.
09.11.2008 Second contributer joined ExtensionOverflow. welcome to chakrit.
09.11.2008 We need more developers. ;-)
09.11.2008 ThrowIfArgumentIsNull in now Implemented and Unit Tested on Codeplex.
public static bool In<T>(this T source, params T[] list)
{
if(null==source) throw new ArgumentNullException("source");
return list.Contains(source);
}
Allows me to replace:
if(reallyLongIntegerVariableName == 1 ||
reallyLongIntegerVariableName == 6 ||
reallyLongIntegerVariableName == 9 ||
reallyLongIntegerVariableName == 11)
{
// do something....
}
and
if(reallyLongStringVariableName == "string1" ||
reallyLongStringVariableName == "string2" ||
reallyLongStringVariableName == "string3")
{
// do something....
}
and
if(reallyLongMethodParameterName == SomeEnum.Value1 ||
reallyLongMethodParameterName == SomeEnum.Value2 ||
reallyLongMethodParameterName == SomeEnum.Value3 ||
reallyLongMethodParameterName == SomeEnum.Value4)
{
// do something....
}
With:
if(reallyLongIntegerVariableName.In(1,6,9,11))
{
// do something....
}
and
if(reallyLongStringVariableName.In("string1","string2","string3"))
{
// do something....
}
and
if(reallyLongMethodParameterName.In(SomeEnum.Value1, SomeEnum.Value2, SomeEnum.Value3, SomeEnum.Value4)
{
// do something....
}
I have various extension methods in my MiscUtil project (full source is available there - I'm not going to repeat it here). My favourites, some of which involve other classes (such as ranges):
Date and time stuff - mostly for unit tests. Not sure I'd use them in production :)
var birthday = 19.June(1976);
var workingDay = 7.Hours() + 30.Minutes();
Ranges and stepping - massive thanks to Marc Gravell for his operator stuff to make this possible:
var evenNaturals = 2.To(int.MaxValue).Step(2);
var daysSinceBirth = birthday.To(DateTime.Today).Step(1.Days());
Comparisons:
var myComparer = ProjectionComparer.Create(Person p => p.Name);
var next = myComparer.ThenBy(p => p.Age);
var reversed = myComparer.Reverse();
Argument checking:
x.ThrowIfNull("x");
LINQ to XML applied to anonymous types (or other types with appropriate properties):
// <Name>Jon</Name><Age>32</Age>
new { Name="Jon", Age=32}.ToXElements();
// Name="Jon" Age="32" (as XAttributes, obviously)
new { Name="Jon", Age=32}.ToXAttributes()
Push LINQ - would take too long to explain here, but search for it.
string.Format shortcut:
public static class StringExtensions
{
// Enable quick and more natural string.Format calls
public static string F(this string s, params object[] args)
{
return string.Format(s, args);
}
}
Example:
var s = "The co-ordinate is ({0}, {1})".F(point.X, point.Y);
For quick copy-and-paste go here.
Don't you find it more natural to type "some string".F("param") instead of string.Format("some string", "param") ?
For a more readable name, try one of these suggestion:
s = "Hello {0} world {1}!".Fmt("Stack", "Overflow");
s = "Hello {0} world {1}!".FormatBy("Stack", "Overflow");
s = "Hello {0} world {1}!".FormatWith("Stack", "Overflow");
s = "Hello {0} world {1}!".Display("Stack", "Overflow");
s = "Hello {0} world {1}!".With("Stack", "Overflow");
..
Are these any use?
public static bool CoinToss(this Random rng)
{
return rng.Next(2) == 0;
}
public static T OneOf<T>(this Random rng, params T[] things)
{
return things[rng.Next(things.Length)];
}
Random rand;
bool luckyDay = rand.CoinToss();
string babyName = rand.OneOf("John", "George", "Radio XBR74 ROCKS!");
public static class ComparableExtensions
{
public static bool Between<T>(this T actual, T lower, T upper) where T : IComparable<T>
{
return actual.CompareTo(lower) >= 0 && actual.CompareTo(upper) < 0;
}
}
Example:
if (myNumber.Between(3,7))
{
// ....
}
The extension method:
public static void AddRange<T, S>(this ICollection<T> list, params S[] values)
where S : T
{
foreach (S value in values)
list.Add(value);
}
The method applies for all types and lets you add a range of items to a list as parameters.
Example:
var list = new List<Int32>();
list.AddRange(5, 4, 8, 4, 2);
By all means put this in the codeplex project.
Serializing / Deserializing objects to XML:
/// <summary>Serializes an object of type T in to an xml string</summary>
/// <typeparam name="T">Any class type</typeparam>
/// <param name="obj">Object to serialize</param>
/// <returns>A string that represents Xml, empty otherwise</returns>
public static string XmlSerialize<T>(this T obj) where T : class, new()
{
if (obj == null) throw new ArgumentNullException("obj");
var serializer = new XmlSerializer(typeof(T));
using (var writer = new StringWriter())
{
serializer.Serialize(writer, obj);
return writer.ToString();
}
}
/// <summary>Deserializes an xml string in to an object of Type T</summary>
/// <typeparam name="T">Any class type</typeparam>
/// <param name="xml">Xml as string to deserialize from</param>
/// <returns>A new object of type T is successful, null if failed</returns>
public static T XmlDeserialize<T>(this string xml) where T : class, new()
{
if (xml == null) throw new ArgumentNullException("xml");
var serializer = new XmlSerializer(typeof(T));
using (var reader = new StringReader(xml))
{
try { return (T)serializer.Deserialize(reader); }
catch { return null; } // Could not be deserialized to this type.
}
}
ForEach for IEnumerables
public static class FrameworkExtensions
{
// a map function
public static void ForEach<T>(this IEnumerable<T> #enum, Action<T> mapFunction)
{
foreach (var item in #enum) mapFunction(item);
}
}
Naive example:
var buttons = GetListOfButtons() as IEnumerable<Button>;
// click all buttons
buttons.ForEach(b => b.Click());
Cool example:
// no need to type the same assignment 3 times, just
// new[] up an array and use foreach + lambda
// everything is properly inferred by csc :-)
new { itemA, itemB, itemC }
.ForEach(item => {
item.Number = 1;
item.Str = "Hello World!";
});
Note:
This is not like Select because Select expects your function to return something as for transforming into another list.
ForEach simply allows you to execute something for each of the items without any transformations/data manipulation.
I made this so I can program in a more functional style and I was surprised that List has a ForEach while IEnumerable does not.
Put this in the codeplex project
My conversion extensions which allow you to do:
int i = myString.To<int>();
Here it is, as posted on TheSoftwareJedi.com
public static T To<T>(this IConvertible obj)
{
return (T)Convert.ChangeType(obj, typeof(T));
}
public static T ToOrDefault<T>
(this IConvertible obj)
{
try
{
return To<T>(obj);
}
catch
{
return default(T);
}
}
public static bool ToOrDefault<T>
(this IConvertible obj,
out T newObj)
{
try
{
newObj = To<T>(obj);
return true;
}
catch
{
newObj = default(T);
return false;
}
}
public static T ToOrOther<T>
(this IConvertible obj,
T other)
{
try
{
return To<T>obj);
}
catch
{
return other;
}
}
public static bool ToOrOther<T>
(this IConvertible obj,
out T newObj,
T other)
{
try
{
newObj = To<T>(obj);
return true;
}
catch
{
newObj = other;
return false;
}
}
public static T ToOrNull<T>
(this IConvertible obj)
where T : class
{
try
{
return To<T>(obj);
}
catch
{
return null;
}
}
public static bool ToOrNull<T>
(this IConvertible obj,
out T newObj)
where T : class
{
try
{
newObj = To<T>(obj);
return true;
}
catch
{
newObj = null;
return false;
}
}
You can ask for default (calls blank constructor or "0" for numerics) on failure, specify a "default" value (I call it "other"), or ask for null (where T : class). I've also provided both silent exception models, and a typical TryParse model that returns a bool indicating the action taken, and an out param holds the new value.
So our code can do things like this
int i = myString.To<int>();
string a = myInt.ToOrDefault<string>();
//note type inference
DateTime d = myString.ToOrOther(DateTime.MAX_VALUE);
double d;
//note type inference
bool didItGiveDefault = myString.ToOrDefault(out d);
string s = myDateTime.ToOrNull<string>();
I couldn't get Nullable types to roll into the whole thing very cleanly. I tried for about 20 minutes before I threw in the towel.
I have an extension method for logging exceptions:
public static void Log(this Exception obj)
{
//your logging logic here
}
And it is used like this:
try
{
//Your stuff here
}
catch(Exception ex)
{
ex.Log();
}
[sorry for posting twice; the 2nd one is better designed :-)]
public static class StringExtensions {
/// <summary>
/// Parses a string into an Enum
/// </summary>
/// <typeparam name="T">The type of the Enum</typeparam>
/// <param name="value">String value to parse</param>
/// <returns>The Enum corresponding to the stringExtensions</returns>
public static T EnumParse<T>(this string value) {
return StringExtensions.EnumParse<T>(value, false);
}
public static T EnumParse<T>(this string value, bool ignorecase) {
if (value == null) {
throw new ArgumentNullException("value");
}
value = value.Trim();
if (value.Length == 0) {
throw new ArgumentException("Must specify valid information for parsing in the string.", "value");
}
Type t = typeof(T);
if (!t.IsEnum) {
throw new ArgumentException("Type provided must be an Enum.", "T");
}
return (T)Enum.Parse(t, value, ignorecase);
}
}
Useful to parse a string into an Enum.
public enum TestEnum
{
Bar,
Test
}
public class Test
{
public void Test()
{
TestEnum foo = "Test".EnumParse<TestEnum>();
}
}
Credit goes to Scott Dorman
--- Edit for Codeplex project ---
I have asked Scott Dorman if he would mind us publishing his code in the Codeplex project. This is the reply I got from him:
Thanks for the heads-up on both the SO post and the CodePlex project. I have upvoted your answer on the question. Yes, the code is effectively in the public domain currently under the CodeProject Open License (http://www.codeproject.com/info/cpol10.aspx).
I have no problems with this being included in the CodePlex project, and if you want to add me to the project (username is sdorman) I will add that method plus some additional enum helper methods.
I find this one pretty useful:
public static class PaulaBean
{
private static String paula = "Brillant";
public static String GetPaula<T>(this T obj) {
return paula;
}
}
You may use it on CodePlex.
DateTimeExtensions
Examples:
DateTime firstDayOfMonth = DateTime.Now.First();
DateTime lastdayOfMonth = DateTime.Now.Last();
DateTime lastFridayInMonth = DateTime.Now.Last(DayOfWeek.Friday);
DateTime nextFriday = DateTime.Now.Next(DayOfWeek.Friday);
DateTime lunchTime = DateTime.Now.SetTime(11, 30);
DateTime noonOnFriday = DateTime.Now.Next(DayOfWeek.Friday).Noon();
DateTime secondMondayOfMonth = DateTime.Now.First(DayOfWeek.Monday).Next(DayOfWeek.Monday).Midnight();
gitorious.org/cadenza is a full library of some of the most useful extension methods I've seen.
Here is one I use frequently for presentation formatting.
public static string ToTitleCase(this string mText)
{
if (mText == null) return mText;
System.Globalization.CultureInfo cultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;
System.Globalization.TextInfo textInfo = cultureInfo.TextInfo;
// TextInfo.ToTitleCase only operates on the string if is all lower case, otherwise it returns the string unchanged.
return textInfo.ToTitleCase(mText.ToLower());
}
Here's a to-and-from for Roman numerals. Not often used, but could be handy. Usage:
if ("IV".IsValidRomanNumeral())
{
// Do useful stuff with the number 4.
}
Console.WriteLine("MMMDCCCLXXXVIII".ParseRomanNumeral());
Console.WriteLine(3888.ToRomanNumeralString());
The source:
public static class RomanNumeralExtensions
{
private const int NumberOfRomanNumeralMaps = 13;
private static readonly Dictionary<string, int> romanNumerals =
new Dictionary<string, int>(NumberOfRomanNumeralMaps)
{
{ "M", 1000 },
{ "CM", 900 },
{ "D", 500 },
{ "CD", 400 },
{ "C", 100 },
{ "XC", 90 },
{ "L", 50 },
{ "XL", 40 },
{ "X", 10 },
{ "IX", 9 },
{ "V", 5 },
{ "IV", 4 },
{ "I", 1 }
};
private static readonly Regex validRomanNumeral = new Regex(
"^(?i:(?=[MDCLXVI])((M{0,3})((C[DM])|(D?C{0,3}))"
+ "?((X[LC])|(L?XX{0,2})|L)?((I[VX])|(V?(II{0,2}))|V)?))$",
RegexOptions.Compiled);
public static bool IsValidRomanNumeral(this string value)
{
return validRomanNumeral.IsMatch(value);
}
public static int ParseRomanNumeral(this string value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
value = value.ToUpperInvariant().Trim();
var length = value.Length;
if ((length == 0) || !value.IsValidRomanNumeral())
{
throw new ArgumentException("Empty or invalid Roman numeral string.", "value");
}
var total = 0;
var i = length;
while (i > 0)
{
var digit = romanNumerals[value[--i].ToString()];
if (i > 0)
{
var previousDigit = romanNumerals[value[i - 1].ToString()];
if (previousDigit < digit)
{
digit -= previousDigit;
i--;
}
}
total += digit;
}
return total;
}
public static string ToRomanNumeralString(this int value)
{
const int MinValue = 1;
const int MaxValue = 3999;
if ((value < MinValue) || (value > MaxValue))
{
throw new ArgumentOutOfRangeException("value", value, "Argument out of Roman numeral range.");
}
const int MaxRomanNumeralLength = 15;
var sb = new StringBuilder(MaxRomanNumeralLength);
foreach (var pair in romanNumerals)
{
while (value / pair.Value > 0)
{
sb.Append(pair.Key);
value -= pair.Value;
}
}
return sb.ToString();
}
}
A convenient way to deal with sizes:
public static class Extensions {
public static int K(this int value) {
return value * 1024;
}
public static int M(this int value) {
return value * 1024 * 1024;
}
}
public class Program {
public void Main() {
WSHttpContextBinding serviceMultipleTokenBinding = new WSHttpContextBinding() {
MaxBufferPoolSize = 2.M(), // instead of 2097152
MaxReceivedMessageSize = 64.K(), // instead of 65536
};
}
}
For Winform Controls:
/// <summary>
/// Returns whether the function is being executed during design time in Visual Studio.
/// </summary>
public static bool IsDesignTime(this Control control)
{
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
return true;
}
if (control.Site != null && control.Site.DesignMode)
{
return true;
}
var parent = control.Parent;
while (parent != null)
{
if (parent.Site != null && parent.Site.DesignMode)
{
return true;
}
parent = parent.Parent;
}
return false;
}
/// <summary>
/// Sets the DropDownWidth to ensure that no item's text is cut off.
/// </summary>
public static void SetDropDownWidth(this ComboBox comboBox)
{
var g = comboBox.CreateGraphics();
var font = comboBox.Font;
float maxWidth = 0;
foreach (var item in comboBox.Items)
{
maxWidth = Math.Max(maxWidth, g.MeasureString(item.ToString(), font).Width);
}
if (comboBox.Items.Count > comboBox.MaxDropDownItems)
{
maxWidth += SystemInformation.VerticalScrollBarWidth;
}
comboBox.DropDownWidth = Math.Max(comboBox.Width, Convert.ToInt32(maxWidth));
}
IsDesignTime Usage:
public class SomeForm : Form
{
public SomeForm()
{
InitializeComponent();
if (this.IsDesignTime())
{
return;
}
// Do something that makes the visual studio crash or hang if we're in design time,
// but any other time executes just fine
}
}
SetDropdownWidth Usage:
ComboBox cbo = new ComboBox { Width = 50 };
cbo.Items.Add("Short");
cbo.Items.Add("A little longer");
cbo.Items.Add("Holy cow, this is a really, really long item. How in the world will it fit?");
cbo.SetDropDownWidth();
I forgot to mention, feel free to use these on Codeplex...
The ThrowIfArgumentIsNull is a nice way to do that null check we all should do.
public static class Extensions
{
public static void ThrowIfArgumentIsNull<T>(this T obj, string parameterName) where T : class
{
if (obj == null) throw new ArgumentNullException(parameterName + " not allowed to be null");
}
}
Below is the way to use it and it works on all classes in your namespace or wherever you use the namespace its within.
internal class Test
{
public Test(string input1)
{
input1.ThrowIfArgumentIsNull("input1");
}
}
It's ok to use this code on the CodePlex project.
I miss the Visual Basic's With statement when moving to C#, so here it goes:
public static void With<T>(this T obj, Action<T> act) { act(obj); }
And here's how to use it in C#:
someVeryVeryLonggggVariableName.With(x => {
x.Int = 123;
x.Str = "Hello";
x.Str2 = " World!";
});
Saves a lot of typing!
Compare this to:
someVeryVeryLonggggVariableName.Int = 123;
someVeryVeryLonggggVariableName.Str = "Hello";
someVeryVeryLonggggVariableName.Str2 = " World!";
put in codeplex project
Takes a camelCaseWord or PascalCaseWord and "wordifies" it, ie camelCaseWord => camel Case Word
public static string Wordify( this string camelCaseWord )
{
// if the word is all upper, just return it
if( !Regex.IsMatch( camelCaseWord, "[a-z]" ) )
return camelCaseWord;
return string.Join( " ", Regex.Split( camelCaseWord, #"(?<!^)(?=[A-Z])" ) );
}
I often use it in conjuction with Capitalize
public static string Capitalize( this string word )
{
return word[0].ToString( ).ToUpper( ) + word.Substring( 1 );
}
Example usage
SomeEntityObject entity = DataAccessObject.GetSomeEntityObject( id );
List<PropertyInfo> properties = entity.GetType().GetPublicNonCollectionProperties( );
// wordify the property names to act as column headers for an html table or something
List<string> columns = properties.Select( p => p.Name.Capitalize( ).Wordify( ) ).ToList( );
Free to use in codeplex project
I found this one helpful
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> pSeq)
{
return pSeq ?? Enumerable.Empty<T>();
}
It removes the null check in the calling code. You could now do
MyList.EmptyIfNull().Where(....)
Convert a double to string formatted using the specified culture:
public static class ExtensionMethods
{
public static string ToCurrency(this double value, string cultureName)
{
CultureInfo currentCulture = new CultureInfo(cultureName);
return (string.Format(currentCulture, "{0:C}", value));
}
}
Example:
double test = 154.20;
string testString = test.ToCurrency("en-US"); // $154.20
Below is an extension method that adapts Rick Strahl's code (and the comments too) to stop you having to guess or read the byte order mark of a byte array or text file each time you convert it to a string.
The snippet allows you to simply do:
byte[] buffer = File.ReadAllBytes(#"C:\file.txt");
string content = buffer.GetString();
If you find any bugs please add to the comments. Feel free to include it in the Codeplex project.
public static class Extensions
{
/// <summary>
/// Converts a byte array to a string, using its byte order mark to convert it to the right encoding.
/// Original article: http://www.west-wind.com/WebLog/posts/197245.aspx
/// </summary>
/// <param name="buffer">An array of bytes to convert</param>
/// <returns>The byte as a string.</returns>
public static string GetString(this byte[] buffer)
{
if (buffer == null || buffer.Length == 0)
return "";
// Ansi as default
Encoding encoding = Encoding.Default;
/*
EF BB BF UTF-8
FF FE UTF-16 little endian
FE FF UTF-16 big endian
FF FE 00 00 UTF-32, little endian
00 00 FE FF UTF-32, big-endian
*/
if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
encoding = Encoding.UTF8;
else if (buffer[0] == 0xfe && buffer[1] == 0xff)
encoding = Encoding.Unicode;
else if (buffer[0] == 0xfe && buffer[1] == 0xff)
encoding = Encoding.BigEndianUnicode; // utf-16be
else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
encoding = Encoding.UTF32;
else if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
encoding = Encoding.UTF7;
using (MemoryStream stream = new MemoryStream())
{
stream.Write(buffer, 0, buffer.Length);
stream.Seek(0, SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(stream, encoding))
{
return reader.ReadToEnd();
}
}
}
}
Here's one I just created today.
// requires .NET 4
public static TReturn NullOr<TIn, TReturn>(this TIn obj, Func<TIn, TReturn> func,
TReturn elseValue = default(TReturn)) where TIn : class
{ return obj != null ? func(obj) : elseValue; }
// versions for CLR 2, which doesn't support optional params
public static TReturn NullOr<TIn, TReturn>(this TIn obj, Func<TIn, TReturn> func,
TReturn elseValue) where TIn : class
{ return obj != null ? func(obj) : elseValue; }
public static TReturn NullOr<TIn, TReturn>(this TIn obj, Func<TIn, TReturn> func)
where TIn : class
{ return obj != null ? func(obj) : default(TReturn); }
It lets you do this:
var lname = thingy.NullOr(t => t.Name).NullOr(n => n.ToLower());
which is more fluent and (IMO) easier to read than this:
var lname = (thingy != null ? thingy.Name : null) != null
? thingy.Name.ToLower() : null;
"Please mark your answers with an acceptance to put the code in the Codeplex project."
Why? All the Stuff on this site under CC-by-sa-2.5, so just put your Extension overflow Project under the same license and you can freely use it.
Anyway, here is a String.Reverse function, based on this question.
/// <summary>
/// Reverse a String
/// </summary>
/// <param name="input">The string to Reverse</param>
/// <returns>The reversed String</returns>
public static string Reverse(this string input)
{
char[] array = input.ToCharArray();
Array.Reverse(array);
return new string(array);
}
I got tired of tedious null-checking while pulling values from MySqlDataReader, so:
public static DateTime? GetNullableDateTime(this MySqlDataReader dr, string fieldName)
{
DateTime? nullDate = null;
return dr.IsDBNull(dr.GetOrdinal(fieldName)) ? nullDate : dr.GetDateTime(fieldName);
}
public static string GetNullableString(this MySqlDataReader dr, string fieldName)
{
return dr.IsDBNull(dr.GetOrdinal(fieldName)) ? String.Empty : dr.GetString(fieldName);
}
public static char? GetNullableChar(this MySqlDataReader dr, string fieldName)
{
char? nullChar = null;
return dr.IsDBNull(dr.GetOrdinal(fieldName)) ? nullChar : dr.GetChar(fieldName);
}
Of course this could be used with any SqlDataReader.
Both hangy and Joe had some good comments on how to do this, and I have since had an opportunity to implement something similar in a different context, so here is another version:
public static int? GetNullableInt32(this IDataRecord dr, int ordinal)
{
int? nullInt = null;
return dr.IsDBNull(ordinal) ? nullInt : dr.GetInt32(ordinal);
}
public static int? GetNullableInt32(this IDataRecord dr, string fieldname)
{
int ordinal = dr.GetOrdinal(fieldname);
return dr.GetNullableInt32(ordinal);
}
public static bool? GetNullableBoolean(this IDataRecord dr, int ordinal)
{
bool? nullBool = null;
return dr.IsDBNull(ordinal) ? nullBool : dr.GetBoolean(ordinal);
}
public static bool? GetNullableBoolean(this IDataRecord dr, string fieldname)
{
int ordinal = dr.GetOrdinal(fieldname);
return dr.GetNullableBoolean(ordinal);
}
It irritated me that LINQ gives me an OrderBy that takes a class implementing IComparer as an argument, but does not support passing in a simple anonymous comparer function. I rectified that.
This class creates an IComparer from your comparer function...
/// <summary>
/// Creates an <see cref="IComparer{T}"/> instance for the given
/// delegate function.
/// </summary>
internal class ComparerFactory<T> : IComparer<T>
{
public static IComparer<T> Create(Func<T, T, int> comparison)
{
return new ComparerFactory<T>(comparison);
}
private readonly Func<T, T, int> _comparison;
private ComparerFactory(Func<T, T, int> comparison)
{
_comparison = comparison;
}
#region IComparer<T> Members
public int Compare(T x, T y)
{
return _comparison(x, y);
}
#endregion
}
...and these extension methods expose my new OrderBy overloads on enumerables. I doubt this works for LINQ to SQL, but it's great for LINQ to Objects.
public static class EnumerableExtensions
{
/// <summary>
/// Sorts the elements of a sequence in ascending order by using a specified comparison delegate.
/// </summary>
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
Func<TKey, TKey, int> comparison)
{
var comparer = ComparerFactory<TKey>.Create(comparison);
return source.OrderBy(keySelector, comparer);
}
/// <summary>
/// Sorts the elements of a sequence in descending order by using a specified comparison delegate.
/// </summary>
public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
Func<TKey, TKey, int> comparison)
{
var comparer = ComparerFactory<TKey>.Create(comparison);
return source.OrderByDescending(keySelector, comparer);
}
}
You're welcome to put this on codeplex if you like.
This one is for MVC it adds the ability to generate a <label /> tag to the Html variable that is available in every ViewPage. Hopefully it will be of use to others trying to develop similar extensions.
Use:
<%= Html.Label("LabelId", "ForId", "Text")%>
Output:
<label id="LabelId" for="ForId">Text</label>
Code:
public static class HtmlHelperExtensions
{
public static string Label(this HtmlHelper Html, string #for, string text)
{
return Html.Label(null, #for, text);
}
public static string Label(this HtmlHelper Html, string #for, string text, object htmlAttributes)
{
return Html.Label(null, #for, text, htmlAttributes);
}
public static string Label(this HtmlHelper Html, string #for, string text, IDictionary<string, object> htmlAttributes)
{
return Html.Label(null, #for, text, htmlAttributes);
}
public static string Label(this HtmlHelper Html, string id, string #for, string text)
{
return Html.Label(id, #for, text, null);
}
public static string Label(this HtmlHelper Html, string id, string #for, string text, object htmlAttributes)
{
return Html.Label(id, #for, text, new RouteValueDictionary(htmlAttributes));
}
public static string Label(this HtmlHelper Html, string id, string #for, string text, IDictionary<string, object> htmlAttributes)
{
TagBuilder tag = new TagBuilder("label");
tag.MergeAttributes(htmlAttributes);
if (!string.IsNullOrEmpty(id))
tag.MergeAttribute("id", Html.AttributeEncode(id));
tag.MergeAttribute("for", Html.AttributeEncode(#for));
tag.SetInnerText(Html.Encode(text));
return tag.ToString(TagRenderMode.Normal);
}
}
Turn this:
DbCommand command = connection.CreateCommand();
command.CommandText = "SELECT #param";
DbParameter param = command.CreateParameter();
param.ParameterName = "#param";
param.Value = "Hello World";
command.Parameters.Add(param);
... into this:
DbCommand command = connection.CreateCommand("SELECT {0}", "Hello World");
... using this extension method:
using System;
using System.Data.Common;
using System.Globalization;
using System.Reflection;
namespace DbExtensions {
public static class Db {
static readonly Func<DbConnection, DbProviderFactory> getDbProviderFactory;
static readonly Func<DbCommandBuilder, int, string> getParameterName;
static readonly Func<DbCommandBuilder, int, string> getParameterPlaceholder;
static Db() {
getDbProviderFactory = (Func<DbConnection, DbProviderFactory>)Delegate.CreateDelegate(typeof(Func<DbConnection, DbProviderFactory>), typeof(DbConnection).GetProperty("DbProviderFactory", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true));
getParameterName = (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), typeof(DbCommandBuilder).GetMethod("GetParameterName", BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null));
getParameterPlaceholder = (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), typeof(DbCommandBuilder).GetMethod("GetParameterPlaceholder", BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null));
}
public static DbProviderFactory GetProviderFactory(this DbConnection connection) {
return getDbProviderFactory(connection);
}
public static DbCommand CreateCommand(this DbConnection connection, string commandText, params object[] parameters) {
if (connection == null) throw new ArgumentNullException("connection");
return CreateCommandImpl(GetProviderFactory(connection).CreateCommandBuilder(), connection.CreateCommand(), commandText, parameters);
}
private static DbCommand CreateCommandImpl(DbCommandBuilder commandBuilder, DbCommand command, string commandText, params object[] parameters) {
if (commandBuilder == null) throw new ArgumentNullException("commandBuilder");
if (command == null) throw new ArgumentNullException("command");
if (commandText == null) throw new ArgumentNullException("commandText");
if (parameters == null || parameters.Length == 0) {
command.CommandText = commandText;
return command;
}
object[] paramPlaceholders = new object[parameters.Length];
for (int i = 0; i < paramPlaceholders.Length; i++) {
DbParameter dbParam = command.CreateParameter();
dbParam.ParameterName = getParameterName(commandBuilder, i);
dbParam.Value = parameters[i] ?? DBNull.Value;
command.Parameters.Add(dbParam);
paramPlaceholders[i] = getParameterPlaceholder(commandBuilder, i);
}
command.CommandText = String.Format(CultureInfo.InvariantCulture, commandText, paramPlaceholders);
return command;
}
}
}
More ADO.NET extension methods: DbExtensions

Categories

Resources