This question might be a little ambiguous so I'll explain my goal and my previous implementation. I'm looking for some advice on how to improve my implementation.
My application needs a certain set of days and times (hours and minutes) to be used for criteria later in the program.
The days and times are variable and depend on whether a user is a member of a particular group or not.
My previous implementation was to get the name of the group that was selected and then go to the web server and download the appropriate file which contained the day and time. There was a file for each group.
The format of the text file was:
Day,Day,Day,etc..HH,HH,MM,MM
It was then read into two separate arrays with the positions hardcoded. E.g. Index 0, 1,2 where days while 3,4 where hours and 5,6 where minutes.
This method also meant that I'd need a longer array for a group that had more days than another.
Obviously this was all very inefficient and the code wasn't very reusable or extendable. I'd have to alter if a new group was introduced and it had more of less data in the text file.
Edit - due to the vagueness of the question I have included code:
This method is passed the group name in the fileName parameter of CollectQualifyingTimes. The string looked like gtstimes.txt or gtsdates.txt or gectimes.txt or gecdates.txt
internal static class DownloadQualifyingTimes
{
//TimeDate Arrays
public static readonly List<string> QDaysList = new List<string>();
public static readonly List<int> QTimesList = new List<int>();
private static StreamReader _reader;
private static string _line;
private static string _file;
/// <summary>
/// Collects the Times
/// </summary>
public static void CollectQualifyingTimes(string fileName)
{
Logger.Debug("About to download the " + fileName + " Qualifying Times and Dates");
FileDownload.DownloadOnlineFile(fileName);
OpenQualifyingFile(fileName);
}
/// <summary>
/// Open the qualifying file and read the values.
/// </summary>
/// <returns></returns>
private static void OpenQualifyingFile(string fileName)
{
try
{
_file = Path + "\\" + fileName;
using (_reader = new StreamReader(_file))
{
while ((_line = _reader.ReadLine()) != null)
{
if (fileName.Contains("Times"))
{
QTimesList.Add(Convert.ToInt16(_line));
Logger.Debug("Times " + _line);
}
else
{
QDaysList.Add(_line);
Logger.Debug("Days " + _line);
}
}
}
}
catch (WebException exception)
{
Logger.Error(exception);
}
}
}
//The method that calls the Downloading class looks like this:
/// <summary>
///
/// </summary>
/// <param name="selectedLeague"></param>
/// <returns></returns>
public static bool QualificationTimeCheck(string selectedLeague)
{
var currentUtcTime = DateTime.UtcNow;
//Day check regardless of league
if (DownloadQualifyingTimes.QDaysList.Contains(currentUtcTime.DayOfWeek.ToString()))
{
Logger.Debug("Qualifying day condition meet");
if (selectedLeague.IsOneOf("GTS", "CAT"))
{
Logger.Debug("GTS or CAT qualifying logic");
if (currentUtcTime.Hour ==
DownloadQualifyingTimes.QTimesList[0] ||
currentUtcTime.Hour ==
DownloadQualifyingTimes.QTimesList[1])
{
Logger.Debug("Qualifying hour condition meet");
if (((currentUtcTime.Minute > DownloadQualifyingTimes.QTimesList[2])
&& (currentUtcTime.Minute < DownloadQualifyingTimes.QTimesList[3])) || SessionObject.LastLapStartedMinute <= DownloadQualifyingTimes.QTimesList[3])
{
Logger.Debug("Qualifying minute condition meet");
return true;
}
I hope this illustrates the nature of my question and the problem.
Can you think of a better way to implement this process? If you need any more information regarding it please don't hesitate to ask.
Edit - Ended up implementing List as per first comment suggestion.
Related
Currently, I am working for a Winforms project.
When I am scanning my Winforms application through CheckMarx then I am getting multiple Reflected_xss_all_clients vulnerabilities.
I know there is no scripting in Winforms. XSS is a web threat but may be there would be some way to remediate these threats during scanning.
Here is the error code section 1:
private void UpdatePreviewValue()
{
try
{
// Set the preview value
if (txtFieldValue.Text != string.Empty)
{
// Show the preview value
lblPreview.Text = "(" + txtFieldValue.Text + ")";
}
else
{
// Show that there is no field value
lblPreview.Text = Properties.Resources.Std_Txt_Fld_NoFieldValue;
}
}
catch (Exception ex)
{
frmErrorHandler.ShowDataError(Properties.ErrorStrings.ErrorTitle_SrcFldCtlInteger_UpdatePreviewValue, DataErrorImageConstants.Exclamation, ex);
}
}
in above code section, the line lblPreview.Text = "(" + txtFieldValue.Text + ")";is throwing Reflected_xss_all_clients vulnerabilities.
Here is the error code section 2:
/// <summary>
/// Method to copy an existing node for moving inside a grid
/// </summary>
/// <param name="rowToCopy">GridRow to copy</param>
/// <returns>GridRow</returns>
private GridRow CopyGridRow(GridRow rowToCopy)
{
GridRow newRow = gridCategories.NewRow();
newRow.Tag = rowToCopy.Tag;
newRow.Cells[0].Text = rowToCopy.Cells[0].Text;
newRow.Cells[0].Image = rowToCopy.Cells[0].Image;
newRow.Cells[1].Text = rowToCopy.Cells[1].Text;
if (rowToCopy.HasRows)
{
foreach (GridRow nestedRow in rowToCopy.NestedRows)
{
newRow.NestedRows.Add(CopyGridRow(nestedRow));
}
}
return newRow;
}
in above code section, the line newRow.Cells[0].Text = rowToCopy.Cells[0].Text; and newRow.Cells[1].Text = rowToCopy.Cells[1].Text;are throwing Reflected_xss_all_clientsvulnerabilities.
Here is the error code section 3:
/// <summary>
/// Method used to add a new discrete value to the listview
/// </summary>
private void AddDiscreteValue()
{
// check we have an entry to add
if (txtDiscreteValue.Text != "")
{
SetDiscreteValue(txtDiscreteValue.Text, true, null, false);
}
}
In above code section, the line SetDiscreteValue(txtDiscreteValue.Text, true, null, false); is throwing Reflected_xss_all_clients vulnerabilities for txtDiscreteValue.Text
Please suggest any way to remediate it, if possible.
Checkmarx will follow the string from input to use. Sometimes it identifies a variable which is not fillterd transited to the frontend as a XSS.
As for me, I always ignore the XSS reported from Checkmarx. Maybe you can use a fillter function before use the string variable. Like this
txtFieldValue.Text=cleanXSS(txtFieldValue.Text)
As for cleanXSS(), you can find many examples after google.
I'm looking for a way to programmatically get the summary portion of Xml-comments of a method in ASP.net.
I have looked at the previous related posts and they do not supply a way of doing so in a web environment.
I can not use any 3rd party apps and due to a web environment, Visual studio plugin's aren't much use either.
The closest thing I have found to a working solution was the JimBlackler project, but it only works on DLL's.
Naturally, something like 'supply .CS file, get XML documentation' would be optimal.
Current situation
I have a web-service and trying to dynamically generate documentation for it.
Reading the Methods, and properties is easy, but getting the Summary for each method is throwing me off a bit.
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public class SomeClass()
{
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public void SomeMethod()
{
}
}
A Workaround - Using reflection on Program.DLL/EXE together with Program.XML file
If you take a look at the sibling .XML file generated by Visual Studio you will see that there is a fairly flat hierarchy of /members/member.
All you have to do is get hold on each method from your DLL via MethodInfo object. Once you have this object you turn to the XML and use XPATH to get the member containing the XML documentation for this method.
Members are preceded by a letter. XML doc for methods are preceded by "M:" for class by "T:" etc.
Load your sibling XML
string docuPath = dllPath.Substring(0, dllPath.LastIndexOf(".")) + ".XML";
if (File.Exists(docuPath))
{
_docuDoc = new XmlDocument();
_docuDoc.Load(docuPath);
}
Use this xpath to get the member representing the method XML docu
string path = "M:" + mi.DeclaringType.FullName + "." + mi.Name;
XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
"//member[starts-with(#name, '" + path + "')]");
Now scan childnodes for all the rows of "///"
Sometimes the /// Summary contains extra blanks, if this bothers use this to remove
var cleanStr = Regex.Replace(row.InnerXml, #"\s+", " ");
The XML summary isn't stored in the .NET assembly - it's optionally written out to an XML file as part of your build (assuming you're using Visual Studio).
Consequently there is no way to "pull out" the XML summaries of each method via reflection on a compiled .NET assembly (either .EXE or .DLL) - because the data simply isn't there for you to pull out. If you want the data, you'll have to instruct your build environment to output the XML files as part of your build process and parse those XML files at runtime to get at the summary information.
You could 'document' your method using the System.ComponentModel.DataAnnotations.DisplayAttribute attribute, e.g.
[Display(Name = "Foo", Description = "Blah")]
void Foo()
{
}
then use reflection to pull the description at runtime.
A deleted post, made by #OleksandrIeremenko, on this thread links to this article https://jimblackler.net/blog/?p=49 which was the basis for my solution.
Below is a modification of Jim Blackler's code making extension methods off the MemberInfo and Type objects and adding code that returns the summary text or an empty string if not available.
Usage
var typeSummary = typeof([Type Name]).GetSummary();
var methodSummary = typeof([Type Name]).GetMethod("[Method Name]").GetSummary();
Extension Class
/// <summary>
/// Utility class to provide documentation for various types where available with the assembly
/// </summary>
public static class DocumentationExtensions
{
/// <summary>
/// Provides the documentation comments for a specific method
/// </summary>
/// <param name="methodInfo">The MethodInfo (reflection data ) of the member to find documentation for</param>
/// <returns>The XML fragment describing the method</returns>
public static XmlElement GetDocumentation(this MethodInfo methodInfo)
{
// Calculate the parameter string as this is in the member name in the XML
var parametersString = "";
foreach (var parameterInfo in methodInfo.GetParameters())
{
if (parametersString.Length > 0)
{
parametersString += ",";
}
parametersString += parameterInfo.ParameterType.FullName;
}
//AL: 15.04.2008 ==> BUG-FIX remove “()” if parametersString is empty
if (parametersString.Length > 0)
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")");
else
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name);
}
/// <summary>
/// Provides the documentation comments for a specific member
/// </summary>
/// <param name="memberInfo">The MemberInfo (reflection data) or the member to find documentation for</param>
/// <returns>The XML fragment describing the member</returns>
public static XmlElement GetDocumentation(this MemberInfo memberInfo)
{
// First character [0] of member type is prefix character in the name in the XML
return XmlFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name);
}
/// <summary>
/// Returns the Xml documenation summary comment for this member
/// </summary>
/// <param name="memberInfo"></param>
/// <returns></returns>
public static string GetSummary(this MemberInfo memberInfo)
{
var element = memberInfo.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Provides the documentation comments for a specific type
/// </summary>
/// <param name="type">Type to find the documentation for</param>
/// <returns>The XML fragment that describes the type</returns>
public static XmlElement GetDocumentation(this Type type)
{
// Prefix in type names is T
return XmlFromName(type, 'T', "");
}
/// <summary>
/// Gets the summary portion of a type's documenation or returns an empty string if not available
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string GetSummary(this Type type)
{
var element = type.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Obtains the XML Element that describes a reflection element by searching the
/// members for a member that has a name that describes the element.
/// </summary>
/// <param name="type">The type or parent type, used to fetch the assembly</param>
/// <param name="prefix">The prefix as seen in the name attribute in the documentation XML</param>
/// <param name="name">Where relevant, the full name qualifier for the element</param>
/// <returns>The member that has a name that describes the specified reflection element</returns>
private static XmlElement XmlFromName(this Type type, char prefix, string name)
{
string fullName;
if (string.IsNullOrEmpty(name))
fullName = prefix + ":" + type.FullName;
else
fullName = prefix + ":" + type.FullName + "." + name;
var xmlDocument = XmlFromAssembly(type.Assembly);
var matchedElement = xmlDocument["doc"]["members"].SelectSingleNode("member[#name='" + fullName + "']") as XmlElement;
return matchedElement;
}
/// <summary>
/// A cache used to remember Xml documentation for assemblies
/// </summary>
private static readonly Dictionary<Assembly, XmlDocument> Cache = new Dictionary<Assembly, XmlDocument>();
/// <summary>
/// A cache used to store failure exceptions for assembly lookups
/// </summary>
private static readonly Dictionary<Assembly, Exception> FailCache = new Dictionary<Assembly, Exception>();
/// <summary>
/// Obtains the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
/// <remarks>This version uses a cache to preserve the assemblies, so that
/// the XML file is not loaded and parsed on every single lookup</remarks>
public static XmlDocument XmlFromAssembly(this Assembly assembly)
{
if (FailCache.ContainsKey(assembly))
{
throw FailCache[assembly];
}
try
{
if (!Cache.ContainsKey(assembly))
{
// load the docuemnt into the cache
Cache[assembly] = XmlFromAssemblyNonCached(assembly);
}
return Cache[assembly];
}
catch (Exception exception)
{
FailCache[assembly] = exception;
throw;
}
}
/// <summary>
/// Loads and parses the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
private static XmlDocument XmlFromAssemblyNonCached(Assembly assembly)
{
var assemblyFilename = assembly.Location;
if (!string.IsNullOrEmpty(assemblyFilename))
{
StreamReader streamReader;
try
{
streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename, ".xml"));
}
catch (FileNotFoundException exception)
{
throw new Exception("XML documentation not present (make sure it is turned on in project properties when building)", exception);
}
var xmlDocument = new XmlDocument();
xmlDocument.Load(streamReader);
return xmlDocument;
}
else
{
throw new Exception("Could not ascertain assembly filename", null);
}
}
}
You can use Namotion.Reflection NuGet package to get these information:
string summary = typeof(Foo).GetXmlDocsSummary();
You can look at https://github.com/NSwag/NSwag - source for nuget NSwag.CodeGeneration - it gets summary as well, usage
var generator = new WebApiAssemblyToSwaggerGenerator(settings);<br/>
var swaggerService = generator.GenerateForController("namespace.someController");<br/>
// string with comments <br/>
var swaggerJson = swaggerService.ToJson();
(try ILSPY decompiler against your dll, you check code and comments)
If you have access to the source code you're trying to get comments for, then you can use Roslyn compiler platform to do that. It basically gives you access to all the intermediary compiler metadata and you can do anything you want with it.
It's a bit more complicated than what other people are suggesting, but depending on what your needs are, might be an option.
It looks like this post has a code sample for something similar.
I often want to do this:
public void Foo(Bar arg)
{
throw new ArgumentException("Argument is incompatible with " + name(Foo));
}
Because if I change the name of Foo the IDE will refactor my error message too, what won't happen if I put the name of the method (or any other kind of member identifier) inside a string literal. The only way I know of implementing "name" is by using reflection, but I think the performance loss outweighs the mantainability gain and it won't cover all kinds of identifiers.
The value of the expression between parenthesis could be computed at compile time (like typeof) and optimized to become one string literal by changing the language specification. Do you think this is a worthy feature?
PS: The first example made it look like the question is related only to exceptions, but it is not. Think of every situation you may want to reference a type member identifier. You'll have to do it through a string literal, right?
Another example:
[RuntimeAcessibleDocumentation(Description="The class " + name(Baz) +
" does its job. See method " + name(DoItsJob) + " for more info.")]
public class Baz
{
[RuntimeAcessibleDocumentation(Description="This method will just pretend " +
"doing its job if the argument " + name(DoItsJob.Arguments.justPretend) +
" is true.")]
public void DoItsJob(bool justPretend)
{
if (justPretend)
Logger.log(name(justPretend) + "was true. Nothing done.");
}
}
UPDATE: this question was posted before C# 6, but may still be relevant for those who are using previous versions of the language. If you are using C# 6 check out the nameof operator, which does pretty much the same thing as the name operator in the examples above.
well, you could cheat and use something like:
public static string CallerName([CallerMemberName]string callerName = null)
{
return callerName;
}
and:
public void Foo(Bar arg)
{
throw new ArgumentException("Argument is incompatible with " + CallerName());
}
Here, all the work is done by the compiler (at compile-time), so if you rename the method it will immediately return the correct thing.
If you simply want the current method name: MethodBase.GetCurrentMethod().Name
If it's a type typeof(Foo).Name
If you want the name of a variable/parameter/field/property, with a little Expression tree
public static string GetFieldName<T>(Expression<Func<T>> exp)
{
var body = exp.Body as MemberExpression;
if (body == null)
{
throw new ArgumentException();
}
return body.Member.Name;
}
string str = "Hello World";
string variableName = GetFieldName(() => str);
For method names it's a little more tricky:
public static readonly MethodInfo CreateDelegate = typeof(Delegate).GetMethod("CreateDelegate", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(Type), typeof(object), typeof(MethodInfo) }, null);
public static string GetMethodName<T>(Expression<Func<T>> exp)
{
var body = exp.Body as UnaryExpression;
if (body == null || body.NodeType != ExpressionType.Convert)
{
throw new ArgumentException();
}
var call = body.Operand as MethodCallExpression;
if (call == null)
{
throw new ArgumentException();
}
if (call.Method != CreateDelegate)
{
throw new ArgumentException();
}
var method = call.Arguments[2] as ConstantExpression;
if (method == null)
{
throw new ArgumentException();
}
MethodInfo method2 = (MethodInfo)method.Value;
return method2.Name;
}
and when you call them you have to specify the type of a compatible delegate (Action, Action<...>, Func<...> ...)
string str5 = GetMethodName<Action>(() => Main);
string str6 = GetMethodName<Func<int>>(() => Method1);
string str7 = GetMethodName<Func<int, int>>(() => Method2);
or more simply, without using expressions :-)
public static string GetMethodName(Delegate del)
{
return del.Method.Name;
}
string str8 = GetMethodName((Action)Main);
string str9 = GetMethodName((Func<int>)Method1);
string str10 = GetMethodName((Func<int, int>)Method2);
As has been covered, using this approach for exceptions seems unnecessary due to the method name being in the call stack on the exception.
In relation to the other example in the question of logging the parameter value, it seems PostSharp would be a good candidate here, and probably would allow lots of new features of this kind that you're interested in.
Have a look at this page on PostSharp which came up when I searched for how to use PostSharp to log parameter values (which it covers). An excerpt taken from that page:
You can get a lot of useful information with an aspect, but there are three popular categories:
Code information: function name, class name, parameter values, etc. This can help you to reduce guessing in pinning down logic flaws or edge-case scenarios
Performance information: keep track of how much time a method is taking
Exceptions: catch select/all exceptions and log information about them
The original question is named "How to refer to an identifier without writing it into a string literal in C#?" This answer does not answer that question, instead, it answers the question "How to refer to an identifier by writing its name into a string literal using a preprocessor?"
Here is a very simple "proof of concept" C# preprocessor program:
using System;
using System.IO;
namespace StackOverflowPreprocessor
{
/// <summary>
/// This is a C# preprocessor program to demonstrate how you can use a preprocessor to modify the
/// C# source code in a program so it gets self-referential strings placed in it.
/// </summary>
public class PreprocessorProgram
{
/// <summary>
/// The Main() method is where it all starts, of course.
/// </summary>
/// <param name="args">must be one argument, the full name of the .csproj file</param>
/// <returns>0 = OK, 1 = error (error message has been written to console)</returns>
static int Main(string[] args)
{
try
{
// Check the argument
if (args.Length != 1)
{
DisplayError("There must be exactly one argument.");
return 1;
}
// Check the .csproj file exists
if (!File.Exists(args[0]))
{
DisplayError("File '" + args[0] + "' does not exist.");
return 1;
}
// Loop to process each C# source file in same folder as .csproj file. Alternative
// technique (used in my real preprocessor program) is to read the .csproj file as an
// XML document and process the <Compile> elements.
DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(args[0]));
foreach (FileInfo fileInfo in directoryInfo.GetFiles("*.cs"))
{
if (!ProcessOneFile(fileInfo.FullName))
return 1;
}
}
catch (Exception e)
{
DisplayError("Exception while processing .csproj file '" + args[0] + "'.", e);
return 1;
}
Console.WriteLine("Preprocessor normal completion.");
return 0; // All OK
}
/// <summary>
/// Method to do very simple preprocessing of a single C# source file. This is just "proof of
/// concept" - in my real preprocessor program I use regex and test for many different things
/// that I recognize and process in one way or another.
/// </summary>
private static bool ProcessOneFile(string fileName)
{
bool fileModified = false;
string lastMethodName = "*unknown*";
int i = -1, j = -1;
try
{
string[] sourceLines = File.ReadAllLines(fileName);
for (int lineNumber = 0; lineNumber < sourceLines.Length - 1; lineNumber++)
{
string sourceLine = sourceLines[lineNumber];
if (sourceLine.Trim() == "//?GrabMethodName")
{
string nextLine = sourceLines[++lineNumber];
j = nextLine.IndexOf('(');
if (j != -1)
i = nextLine.LastIndexOf(' ', j);
if (j != -1 && i != -1 && i < j)
lastMethodName = nextLine.Substring(i + 1, j - i - 1);
else
{
DisplayError("Unable to find method name in line " + (lineNumber + 1) +
" of file '" + fileName + "'.");
return false;
}
}
else if (sourceLine.Trim() == "//?DumpNameInStringAssignment")
{
string nextLine = sourceLines[++lineNumber];
i = nextLine.IndexOf('\"');
if (i != -1 && i != nextLine.Length - 1)
{
j = nextLine.LastIndexOf('\"');
if (i != j)
{
sourceLines[lineNumber] =
nextLine.Remove(i + 1) + lastMethodName + nextLine.Substring(j);
fileModified = true;
}
}
}
}
if (fileModified)
File.WriteAllLines(fileName, sourceLines);
}
catch (Exception e)
{
DisplayError("Exception while processing C# file '" + fileName + "'.", e);
return false;
}
return true;
}
/// <summary>
/// Method to display an error message on the console.
/// </summary>
private static void DisplayError(string errorText)
{
Console.WriteLine("Preprocessor: " + errorText);
}
/// <summary>
/// Method to display an error message on the console.
/// </summary>
internal static void DisplayError(string errorText, Exception exceptionObject)
{
Console.WriteLine("Preprocessor: " + errorText + " - " + exceptionObject.Message);
}
}
}
And here's a test file, based on the first half of the original question:
using System;
namespace StackOverflowDemo
{
public class DemoProgram
{
public class Bar
{}
static void Main(string[] args)
{}
//?GrabMethodName
public void Foo(Bar arg)
{
//?DumpNameInStringAssignment
string methodName = "??"; // Will be changed as necessary by preprocessor
throw new ArgumentException("Argument is incompatible with " + methodName);
}
}
}
To make the running of the preprocessor program a part of the build process you modify the .csproj file in two places. Insert this line in the first section:
<UseHostCompilerIfAvailable>false</UseHostCompilerIfAvailable>
(This is optional - see here https://stackoverflow.com/a/12163384/253938 for more information.)
And at the end of the .csproj file replace some lines that are commented-out with these lines:
<Target Name="BeforeBuild">
<Exec WorkingDirectory="D:\Merlinia\Trunk-Debug\Common\Build Tools\Merlinia Preprocessor\VS2012 projects\StackOverflowPreprocessor\bin" Command="StackOverflowPreprocessor.exe "$(MSBuildProjectFullPath)"" />
</Target>
Now when you recompile the test program the line that says
string methodName = "??"; // Will be changed as necessary by preprocessor
will be magically converted to say
string methodName = "Foo"; // Will be changed as necessary by preprocessor
OK?
Version 6 of C# has introduced the nameof operator which works like the name operator described in the examples of the question, but with some restrictions. Here are some examples and excerpts from the C# FAQ blog:
(if x == null) throw new ArgumentNullException(nameof(x));
You can put more elaborate dotted names in a nameof expression, but that’s just to tell the compiler where to look: only the final identifier will be used:
WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode"
Note: there are small design changes to nameof since the Preview was built. In the preview, dotted expressions like in the last example, where person is a variable in scope, are not allowed. Instead you have to dot in through the type.
Summary
I have an ASP.NET 3.5 website and a mobile detection method inside a C# class file inside my app_code folder. I want to call this method which sets a cookie, then switch my master page file if it's a mobile device.
I'm using a method i got from the comment section down in this article: http://www.codeproject.com/Articles/34422/Detecting-a-mobile-browser-in-ASP-NET
This just seemed simpler than using the 51degrees method of detection since i didn't really need a high level of detection, and i didn't want to send them to a different URL, but rather just flip to a different masterpage, and the NuGet package which makes a nice easy install doesn't work for ASP.NET 3.5.
The problem i'm at currently is with calling the method.
Here's the Code
External app_code class
public static class fooBar // test method
{
public static bool ean()
{
return true;
}
}
public static class HttpRequestExt
{
#region Private Fields
// These regular expressions retrieved from http://detectmobilebrowser.com/ "Open source mobile phone detection".
private static Regex MobileBrowsers = new Regex(#"android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|symbian|treo|up\\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino", RegexOptions.IgnoreCase | RegexOptions.Multiline);
private static Regex MobileApps = new Regex(#"1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|e\\-|e\\/|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\\-|2|g)|yas\\-|your|zeto|zte\\-", RegexOptions.IgnoreCase | RegexOptions.Multiline);
#endregion
public const string ViewMobileSiteCookieName = "ViewMobile";
/// <summary>
/// Determines if the request emanated from a mobile-device client;
/// and stores the result in a cookie on the response.
/// </summary>
/// <param name="request"></param>
/// <param name="Response"></param>
/// <returns></returns>
///
public static bool IsMobileClient(this System.Web.HttpRequest request, System.Web.HttpRequest Response)
{
bool isMobile = false;
bool isCookieSet = false;
var viewMobileCookie = request.Cookies[ViewMobileSiteCookieName];
if (viewMobileCookie != null && bool.TryParse(viewMobileCookie.Value, out isMobile))
{
isCookieSet = true;
}
else if (request.Browser.IsMobileDevice)
{
isMobile = true;
}
else if (request.ServerVariables["HTTP_X_WAP_PROFILE"].IsNotEmpty())
{
isMobile = true;
}
else if
(
request.ServerVariables["HTTP_ACCEPT"].IsNotEmpty()
&&
(
request.ServerVariables["HTTP_ACCEPT"].ToLower().Contains("wap")
|| request.ServerVariables["HTTP_ACCEPT"].ToLower().Contains("wml+xml")
)
)
{
isMobile = true;
}
else if (request.ServerVariables["HTTP_USER_AGENT"].IsNotEmpty())
{
string userAgent = request.ServerVariables["HTTP_USER_AGENT"];
isMobile = ((MobileBrowsers.IsMatch(userAgent) || MobileApps.IsMatch(userAgent.Substring(0, 4))));
}
// Store the result as a cookie.
if (!isCookieSet)
Response.Cookies.Add(new HttpCookie(ViewMobileSiteCookieName, isMobile.ToString()));
return isMobile;
}
public static bool IsNotEmpty(this string instance)
{
return instance != null && instance.Length > 0;
}
}
My call to it)
Right now im doing it on the page, but i figure i'll do this in global.asax on session start?
sectionTitle.InnerHtml = fooBar.ean().ToString(); // test works
sectionTitle.InnerHtml = HttpRequestExt.IsMobileClient.ToString(); // compile error
Compile Error:
CS0119: 'SWIC.HttpRequestExt.IsMobileClient(System.Web.HttpRequest, System.Web.HttpRequest)' is a 'method', which is not valid in the given context
Do i have to somehow cast this to the current instance? Should i just be doing this differently all together?
You're trying to call a method like a property. Methods need parentheses:
HttpRequestExt.IsMobileClient().ToString()
^^
You'll also have to call it on the current request, not just statically, as it is an extension method (which takes a parameter). E.g.:
sectionTitle.InnerHtml = Page.Request.IsMobileClient(Page.Response).ToString();
This question already has answers here:
How can I get the assembly last modified date?
(6 answers)
Closed 9 years ago.
How can I retrieve the Created date from the current .NET assembly?
I'd like to add some realy simple functionality where my app stops working one week after the build date of the main assembly. I already wrote the code that kills my app after a given date. I just need to programmatically retrieve the creation date from the assembly.
The following is based on: https://blog.codinghorror.com/determining-build-date-the-hard-way/
public static class ApplicationInformation
{
/// <summary>
/// Gets the executing assembly.
/// </summary>
/// <value>The executing assembly.</value>
public static System.Reflection.Assembly ExecutingAssembly
{
get { return executingAssembly ?? (executingAssembly = System.Reflection.Assembly.GetExecutingAssembly()); }
}
private static System.Reflection.Assembly executingAssembly;
/// <summary>
/// Gets the executing assembly version.
/// </summary>
/// <value>The executing assembly version.</value>
public static System.Version ExecutingAssemblyVersion
{
get { return executingAssemblyVersion ?? (executingAssemblyVersion = ExecutingAssembly.GetName().Version); }
}
private static System.Version executingAssemblyVersion;
/// <summary>
/// Gets the compile date of the currently executing assembly.
/// </summary>
/// <value>The compile date.</value>
public static System.DateTime CompileDate
{
get
{
if (!compileDate.HasValue)
compileDate = RetrieveLinkerTimestamp(ExecutingAssembly.Location);
return compileDate ?? new System.DateTime();
}
}
private static System.DateTime? compileDate;
/// <summary>
/// Retrieves the linker timestamp.
/// </summary>
/// <param name="filePath">The file path.</param>
/// <returns></returns>
/// <remarks>http://www.codinghorror.com/blog/2005/04/determining-build-date-the-hard-way.html</remarks>
private static System.DateTime RetrieveLinkerTimestamp(string filePath)
{
const int peHeaderOffset = 60;
const int linkerTimestampOffset = 8;
var b = new byte[2048];
System.IO.FileStream s = null;
try
{
s = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
s.Read(b, 0, 2048);
}
finally
{
if(s != null)
s.Close();
}
var dt = new System.DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(System.BitConverter.ToInt32(b, System.BitConverter.ToInt32(b, peHeaderOffset) + linkerTimestampOffset));
return dt.AddHours(System.TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours);
}
}
I don't think the assembly itself contains it's creation date. I suspect the closest you can get is the creation date of the assembly file itself:
File.GetCreationTime(Assembly.GetExecutingAssembly().Location)
should do the trick.
EDIT:
I think Jeff Atwood's solution, written up by "grenade" in this thread, is probably the better way to go now.
What's wrong with:
System.IO.File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location);
Maybe this post on coding horror may help
This should work:
var entryAssembly = Assembly.GetEntryAssembly();
var fileInfo = new FileInfo(entryAssembly.Location);
var buildDate = fileInfo.LastWriteTime;
The best way to do this would be with a custom attribute that you set on the PreBuild of your assembly.
And then use the standard reflection to get the attribute you created.
But out of curiosity, why kill the app after the BUILD date?
If you're writing an application for a mobile device using the compact framwork, Assembly.Location is not available.
Here, I found an alternative:
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)