Is it possible to detect a bundle querystring in ASP.NET MVC?
For example if I have the following bundle request:
/css/bundles/mybundle.css?v=4Z9jKRKGzlz-D5dJi5VZtpy4QJep62o6A-xNjSBmKwU1
Is it possible to extract the v querystring?:
4Z9jKRKGzlz-D5dJi5VZtpy4QJep62o6A-xNjSBmKwU1
I've tried doing this in a bundle transform, but with no luck. I found that even with UseServerCache set to false the transform code didn't always run.
It's been a while since I've worked with the ASP Bundler (I remember it being terrible), and these notes are from my memory. Please verify that it's still valid.
Hopefully this will provide a starting point for your search.
To tackle this problem you'll want to explore around in System.Web.Optimization namespace.
Of most importance is the System.Web.Optimization.BundleResponse class, which has a method named GetContentHashCode() which is exactly what you want. Unfortunately, MVC Bundler has a bad architecture and I'm willing to bet that this is still an internal method. This means you won't be able to call it from your code.
Update
Thanks for the verification. So it looks like you have a few ways of accomplishing your goal:
Compute the hash your self using the same algorithm as ASP Bundler
Use reflection to call into the internal method of the Bundler
Get the URL from bundler (there is a public method for this I believe) and extract the query string, then extract the hash from that (using any string extraction methods)
Get angry at Microsoft for bad design
Lets go with #2 (Be careful, since its marked as internal and not part of the public API, a rename of the method by the Bundler team will break things)
//This is the url passed to bundle definition in BundleConfig.cs
string bundlePath = "~/bundles/jquery";
//Need the context to generate response
var bundleContext = new BundleContext(new HttpContextWrapper(HttpContext.Current), BundleTable.Bundles, bundlePath);
//Bundle class has the method we need to get a BundleResponse
Bundle bundle = BundleTable.Bundles.GetBundleFor(bundlePath);
var bundleResponse = bundle.GenerateBundleResponse(bundleContext);
//BundleResponse has the method we need to call, but its marked as
//internal and therefor is not available for public consumption.
//To bypass this, reflect on it and manually invoke the method
var bundleReflection = bundleResponse.GetType();
var method = bundleReflection.GetMethod("GetContentHashCode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
//contentHash is whats appended to your url (url?###-###...)
var contentHash = method.Invoke(bundleResponse, null);
The bundlePath variable is the same name that you gave to the bundle (from BundleConfig.cs)
Hope this helps! Good Luck!
Edit: Forgot to say that it would be a good idea to add a test around this. The test would check for the existence of the GetHashCode function. This way, in the future, should the internals of the Bundler change the test will fail and you'll know where the problem is.
Is there such a thing as a LINQ Query Provider for querying C# files?
I have a Winforms app that I use to assist me in generating code as well as to supplement Visual Studio's editing capabilities for existing code. One thing I would like to be able to do is to query a given class to see if a method exists. Or query for a list of classes, or query for classes with a particular type in the method signature, etc.
Further, I would love to be able to run the usual CRUDs on said C# files, but I realize that this may be out of scope for this question.
SOLVED!
Thanks to the folks who suggested Roslyn, and especially thanks to the code sample provided by Konrad Kokosa below, I was able to get exactly what I needed.
First thing you need to do is download the Roslyn DLLs (I used NuGet). Then query away. Here is another example for getting an alphabetized list of all methods in a class:
static List<string> GetMethodList(string filename, string className)
{
var syntaxTree = SyntaxTree.ParseFile(filename);
var root = syntaxTree.GetRoot();
var #class = root.DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault(md => md.Identifier.ValueText.Equals(className));
return (List<string>) #class.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList().OrderBy(m => m.Identifier.ValueText).Select(m => m.Identifier.ValueText);
}
I've found Roslyn very intuitive. Here is an example of parsing source file for a specified methodName within specified class className:
static void GetClassMethod(string filename, string className, string methodName)
{
var syntaxTree = SyntaxTree.ParseFile(filename);
var root = syntaxTree.GetRoot();
var #class = root.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.Where(md => md.Identifier.ValueText.Equals(className))
.FirstOrDefault();
var method = #class.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.Where(md => md.Identifier.ValueText.Equals(methodName))
.FirstOrDefault();
}
From this example you can easily build querying all classes withing a file.
Microsoft is working on a project called Roslyn which allows you to interact with C# code via ordinary .NET objects:
http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been officially released, though, and I'm honestly not sure that it would include things like manipulating the source files (for example, to add a function to a class).
If you're working with code that is already compiled, you could probably use a combination of a few tricks, such as loading an assembly at runtime and then using reflection to interrogate it.
However, I would suggest taking a look at tools that are already available for generating code, like T4 Templates. There might be a better way to solve the underlying problem than interrogating C# source files.
1 - I have this file content on the disc (cs file, not compiled):
namespace Test
{
using System;
public class TestClass
{
public string SomeTestMethod(){
return "test here";
}
}
}
How do I get in run time into a variable the method:
public string SomeTestMethod(){
return "test here";
}
for example: SourceCodeParser.GetMothod("path to file","SomeTestMethod");
2 - Is it possible the content of accessor member?
public string SomeMember {
get {
return "test here";
}
}
Roslyn is what you need. You can easily install it using nuget. Here is a working code for getting a method body:
string GetMethod(string filename, string methodName)
{
var syntaxTree = SyntaxTree.ParseFile(filename);
var root = syntaxTree.GetRoot();
var method = root.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.Where(md => md.Identifier.ValueText.Equals(methodName))
.FirstOrDefault();
return method.ToString();
}
and code for getting body of property getter:
string GetPropertyGetter(string filename, string propertyName)
{
var syntaxTree = SyntaxTree.ParseFile(filename);
var root = syntaxTree.GetRoot();
var property = root.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Where(md => md.Identifier.ValueText.Equals(propertyName))
.FirstOrDefault();
var getter = property.AccessorList.Accessors.First(a => a.Kind == SyntaxKind.GetAccessorDeclaration);
return getter.ToString();
}
You need a tool that can parse the source code, tracks code locations, and knows how to look up methods (or variables, or whatever named thing you care about) in the source code. With that, finding the lines of interest is pretty easy. Such a tool isn't easy to build because parsers for full languages aren't easy to build. Nor are the lookup functions easy; parameters, namespaces, templates, inheritance all combine to make name lookup for modern languages remarkably complex.
Program transformation (PT) tools (which often have such full parsers already available) often do this by building an AST in memory which represents the code. Then, given rules for name lookup, finding the code in the AST by name is relatively straightforward, and one can use the prettyprinter function of such a tool to pretty print the named-entity into a buffer/string/diskfile wherever you want it parked.
You are not likely to find a PT as a subroutine you can call from C# directly. You may be able to invoke such a tool from your program, and have it return the string as a text result/in a file/via a pipe whatever you thinks is best for interprocess communication.
If you want to show the method text embedded in an HTML page, you can often configure the PT to generate the entire page containing the prettyprinted entity text. (See the JavaSource browser via my bio for an example like this).
The .NET Reflection class does not support decompiling to C#. The best you can do is use MMethodInfo.GetMethodBody() and call MethodBody.GetILAsByteArray() on the response. That will give you the MSIL which is the best that .NET reflection can give you.
To decompile to C# you will need a decompiler - of which there are at least a dozen legitimate options. You'll need to investigate for one that meets your requirements and budget.
One option is Red Gate's .NET Reflector. Nick Harrison has a long and thorough article on using .NET Reflector in an application to render source code as HTML.
I am currently involved in writing an ASP.NET MVC 4 web version (using the Razor view engine) of an existing (Delphi) desktop based software product which at present allows customers (businesses) to completely customise all of the text in their instance of the application, both to localise it and to customise it to their specific environments.
For example the terms-
My tasks
Products
Workflows
Designs
Might all be changed to individual terms used within the business.
At present this customisation is simply done within the text strings which are stored within the application database, and compared and loaded on every form load in the Delphi database. I.e. every string on the form is compared with the database English strings and a replacement based on the selected locale is rendered on the form if available. I don't feel this is either scalable or especially performant.
I am also not personally comfortable with the idea of customisation happening within the localization method, that every string in the application can be changed by the end customer - it can lead to support issues in terms of consistency in text, and confusion where instructions are incorrectly changed or not kept up to date. There are lots of strings within an application that probably should not be changed beyond localizing them to the locale of the user - local language and/or formatting conventions.
I personally would rather stick with the ASP.NET APIs and conventions in localizing the web version of the application, using RESX resource files and resource keys rather than string matching. This is much more flexible than string matching where strings may have different contexts or cases and cannot simply be changed en-mass (there many English words which may have different meanings in different contexts, and may not map to the same set of meanings in other languages), crucially avoids round trips to the database to fetch the strings needed to fetch the page and also allows for ease of translation with a great set of tools around the standard RESX files. It also means no custom implementation is needed to maintain or document for future developers.
This does however give a problem of how we cope with these custom terms.
I'm currently thinking that we should have a separate RESX file for these terms, which lists defaults for the given locale. I'd then create a new database table which will be something like
CREATE TABLE [dbo].[WEB_CUSTOM_TERMS]
(
[TERM_ID] int identity primary key,
[COMPANY_ID] int NOT NULL, -- Present for legacy reasons
[LOCALE] varchar(8) NOT NULL,
[TERM_KEY] varchar(40) NOT NULL,
[TERM] nvarchar(50) -- Intentionally short, this is to be used for single words or short phrases
);
This can potentially read into a Dictionary<string, string> when needed and cached by IIS to provide lookup without the delay in connecting to the SQL server and conducting the query.
public static class DatabaseTerms
{
private static string DictionaryKey
{
get { return string.Format("CustomTermsDictionary-{0}", UserCulture); }
}
private static string UserCulture
{
get { return System.Threading.Thread.CurrentThread.CurrentCulture.Name; }
}
public static Dictionary<string, string> TermsDictionary
{
get
{
if (HttpContext.Current.Cache[DictionaryKey] != null)
{
var databaseTerms = HttpContext.Current.Cache[DictionaryKey] as Dictionary<string, string>;
if (databaseTerms != null)
{
return databaseTerms;
}
}
var membershipProvider = Membership.Provider as CustomMembershipProvider;
int? companyId = null;
if (membershipProvider != null)
{
companyId = CustomMembershipProvider.CompanyId;
}
using (var context = new VisionEntities())
{
var databaseTerms = (from term in context.CustomTerms
where (companyId == null || term.CompanyId == companyId) &&
(term.Locale == UserCulture)
orderby term.Key
select term).ToDictionary(t => t.Key, t => t.Text);
HttpContext.Current.Cache.Insert(DictionaryKey, databaseTerms, null, DateTime.MaxValue,
new TimeSpan(0, 30, 0), CacheItemPriority.BelowNormal, null);
return databaseTerms;
}
}
set
{
if (HttpContext.Current.Cache[DictionaryKey] != null)
{
HttpContext.Current.Cache.Remove(DictionaryKey);
}
HttpContext.Current.Cache.Insert(DictionaryKey, value, null, DateTime.Now.AddHours(8),
new TimeSpan(0, 30, 0), CacheItemPriority.BelowNormal, null);
}
}
}
I can then have a class which exposes public properties, returning a string based on either this dictionary value or the value in the RESX file - whichever is not null. Something like-
public static class CustomTerm
{
public static string Product
{
get
{
return (DatabaseTerms.TermsDictionary.ContainsKey("Product") ?
DatabaseTerms.TermsDictionary["Product"] : CustomTermsResources.Product);
}
}
}
These can then be added to larger localised strings using string formatting if required, or used by themselves as labels for menus etc.
The main disadvantage of this approach is the need to anticipate in advance which terms the end customers may wish to customise, but I do feel this might present the best of both worlds.
Does this seem like a workable approach and how have other devs approached this problem?
Thanks in advance.
I once designed an MVC application, whereby any string could be changed. In my case it was to handle other languages, but conceivably you could change anything just for aesthetic purposes. That and there is potential for the system to be marketed to other shops, and they may well call the same things different name (You say "Deferred Payment", I say "Lease Payment", etc.)
Warning: This solution is not about globalization and localization (e.g. left-to-right, word/verb ordering - it only needed to do what it did!)
It also considered the possibility of American English (en-US) vs British English (en-GB) vs Australian English (en-AU).
In the end, A Locale table was created in the database:
_id _localeName _idRoot
---------------------------
1 en-GB null
2 en-US 1
3 en-AU 2
Note how US and AU effectively have en-GB as their parent. en-GB therefore had every conceivably string that can be used in the application, in our translation table:
_id _idCulture _from _to
--------------------------------------
1 1 msgyes Yes
2 1 msgno No
3 1 msgcolour Colour
4 2 msgcolour Color
Now, during application initalisation, there was a config flag that specified the culture, which in my case happened to be en-AU. The system looks up the culture tree (en-AU derives from en-GB), and loads all the translations bottom up in to a dictionary cache. Therefore any en-AU specific translations overwrote the GB ones.
So, to describe it in your case - you'd have ALL translations in your database anyway, and that's your default setup. When the customer wishes to customise the text, they basically get a new node (or a derived culture in my example), and you build your cache again. Any terms they customised override the defaults. You no longer have to worry about what terms were done, it just works.
We have a similar setup in our application, we allow certain modules to have a custom names to fit the customers brand.
the first step to this solution is we know our client context at runtime and we stuff it into the HttpContext.Items.
For those items that can be customized, we introduced resource file containing the base keys. If the enterprise wants it customized we add a prefix in front of the key name (ie Client_key)
At once all this is in place its a simple coalesce to fetch the customized or default value.
Resx file snippet
<data name="TotalLeads" xml:space="preserve">
<value>Total Leads</value>
</data>
<data name="Client_TotalLeads" xml:space="preserve">
<value>Total Prospects</value>
</data>
Class to handle switch between custom and base resources
public static class CustomEnterpriseResource
{
public static string GetString(string key)
{
return GetString(key, Thread.CurrentThread.CurrentUICulture);
}
public static string GetString(string key, string languageCode)
{
return GetString(key, new CultureInfo(languageCode));
}
public static string GetString(string key, CultureInfo cultureInfo)
{
var customKey = ((EnterpriseContext)HttpContext.Current.Items[EnterpriseContext.EnterpriseContextKey]).ResourcePrefix + key;
return Resources.Enterprise.ResourceManager.GetString(customKey, cultureInfo)
?? Resources.Enterprise.ResourceManager.GetString(key, cultureInfo);
}
}
Also to assist in the views we create a html helper for this.
public static class EnterpriseResourceHelper
{
/// <summary>
/// Gets a customizable resource
/// </summary>
/// <param name="helper">htmlHelper</param>
/// <param name="key">Key of the resource</param>
/// <returns>Either enterprise customized resource or base resource for current culture.</returns>
public static string EnterpriseResource(this HtmlHelper helper, string key)
{
return CustomEnterpriseResource.GetString(key);
}
}
The requirement you have is not very common. I have worked in projects where localization is done purely using satellite assemblies and in projects where localization is done purely using database tables. In .NET, the recommended approach is RESX files compiled into satellite assemblies. It is a real good approach if you adopt it fully.
Your requirements are some where in between. Though the approach you plan to take, at this point sounds good on paper, I have a feeling over the course of time, maintenance will be difficult, since some of the strings will be in RESX and some will be in database. Even if the distribution is 90% - 10%, people will have difficulty figuring out where it takes the strings when you have all the strings loaded up in production. You will get queries from your users why a particular string is not showing up correctly and at that time it can get difficult for a developer (other than you) to figure out. You will be the best judge for your needs but I believe either you embrace RESX approach fully (which is not possible in your case) or go the full database route. If I have every thing in tables, all I need to do is to run a query for a given profile and I will see all the strings. This will be easier to support.
Even with database, you can follow a RESX-style approach of storing the full string against a key for a culture. The older approach of storing word by word is definitely not a good solution and will not work for different languages, since only sentences can be translated and not individual words. Your idea of caching is definitely needed for performance. So, basically having every thing in a bunch of tables, caching the same in memory and pulling the strings from cache based on the current culture is something I will go for. Of course, my opinion is based on what I could understand by reading your question :).
Also, check this out.
Very interesting question, thanks for bringing it up.
I have localized applications in very different ways, and your case is very specific. Let's start from the fact that everything comes down to localizing the labels/titles of the UI. Therefore, these elements must become localizable. On many platforms (such as WinForms, ASP.NET) they are localizable by design, and all it takes is extending the resource management model. I would say, this is the most natural way of localization if you are writing for such a platform.
In case of ASP.NET MVC, even though it's built on top of ASP.NET engine, we are not recommended to use the ASP.NET's server side tags and therefore the solution does not work. Why I provided it above is to give the clarity to my solution which I'm describing below.
Step 1 - Modularity
All labels and titles are part of some particular screen of the application. Since the screen is what groups them, I often use it for this exact purpose when describing localization resources. BTW, this is why we have one resx file per screen for the applications. So, we are following the consistent standard here.
To express modularity, define classes that correspond to each screen, and have properties defined on it that correspond to each localizable label or title on the screen.
Pseudo example:
class ProductPageResources
{
public string PageTitle { get; set; }
public string ProductNameLabel { get; set; }
}
Step 2 - Localization
When designing your application screens, stick to the modular resource classes defined above. Use localized strings from the modular resource class to display the labels and titles. If there's a need to add a label to the screen, don't forget to add a new property to the modular resource class too. Since it's ASP.NET MVC, we need to pass the resources alongside with the model. Conceptually it would not be necessary, but doing so gives us the flexibility to replace the resource implementation in the future (e.g. from MS SQL to some other source).
Usage example:
#{
ViewBag.Title = string.format(Model.Resources.PageTitle, Model.Product.Name);
}
...
<label>#Model.Resources.ProductNameLabel</label>
Note that the resource class property returns the localized string for the current culture, or the fallback value (described below) if not found. For the default value to appear as a value, I prepare the resource object by iterating the properties and assigning default values to them if they are empty (because the override was not found).
Step 3 - Customization
[Very nice and descriptive term you've got here, so I will use it.]
I personally don't think that the resource management should be data-driven. Main reason is that it's not dynamic enough. Just recall, that we have modular classes, and we start adding properties to it when we need to display something new on the screen. On the other hand, if you add something to the database, it's not appearing on the screen just so.
Therefore, we have a strongly-typed localization here, and it's very natural way of localizing things. The rest comes from this conclusion.
On your customization/resource administration screen you can use reflection to detect all the modular resource classes and display their properties on the screen for customization. To find the resource classes, you can put them under the same namespace, or you could mark them with some attributes to easier find them in the assembly. Either way works.
To make the modular resource class more display-friendly, you can use attributes to assign descriptions that should display instead of their Pascal-Case names on the screen.
So, our modular resource class becomes something like this:
[Description("Product Page")]
class ProductPageResources
{
[Description("Page Title")]
[DefaultValue("Product Details: {0}")
public string PageTitle { get; set; }
[Description("Product Name (label)")]
[DefaultValue("Name:")]
public string ProductNameLabel { get; set; }
}
Basically, on the customization screen we will see default values for Product Page, and each available localized value. For the last part, you can enumerate all the active cultures of the application and extract the value from the property again. Alternatively, you can use some other way depending on the implementation.
This seems to be an extensive reflection, but after all, Visual Studio does something very similar by allowing us to edit the resource files in the special editor. Bottom line is that you have a precisely working framework.
In the project I have some custom WebUserControls for form elements (they encapsulate some standard validators and other system specific functions). My user controls are "DropDownListField" and "TextBoxField". In the code behind of a page I have this code:
string parameterValue = null;
foreach (object control in myMultiView.Views[myMultiView.ActiveViewIndex].Controls)
{
if (control.GetType() == typeof(DropDownListField))
parameterValue = ((DropDownListField)control).Value;
if (control.GetType() == typeof(TextBoxField))
parameterValue = ((TextBoxField)control).Value;
}
For some reason the "if" statements always return false even when I step through the code and see that "control" is getting assigned my web user control. This code is in another place in the project exactly the same except in the other location the standard .net controls "TextBox" and "DropDownList" are used and in the other location the code works.
Does anybody know why this wouldn't work with web user controls?
UPDATE:
Hmm so in debugging I found this:
?control.GetType();
BaseType: {Name = "DropDownListField" FullName = "WebUI.UserControls.Fields.DropDownListField"}
?typeof(DropDownListField);
BaseType: {Name = "UserControl" FullName = "System.Web.UI.UserControl"}
So typeof is just recognizing they are user controls not the full type it seems.
Does anybody know how I would check for a specific user control type?
I'm guessing they aren't the same type, use debugging to find out the actual type.
Also, try using the 'is' keyword instead.
PS: It might be cleaner for you to say if (control is DropDownListField)
I don't recall if a view directly includes its children in Controls, but I wouldn't be surprised if Controls contained only one element, which would be a container of some sorts. Therefore, your controls are potentially in Controls[0].Controls or even further down. I would advise you create a method that finds the child recursively.
Actually, your controls should all implement a common interface (example:
interface ICustomFieldWithValue { string Value {get; set; }}
). Your resulting code would be much cleaner.
c2.GetType().ToString() == "System.Web.UI.WebControls.Label"