Generate resouce file (resx) from MVC model definition - c#

I've got a bunch of models to localize and I'm looking for a way to keep my effort as small as possible :)
Essentially, I have a model classes where all properties(as needed) are decrated with the DisplayNameAttribute.
What I'd like to do is, build a tool which looks for a property on the class definition (e.g. "Views.Contact") and then generates (updates) the default resx file (e.g. "Views.Contact.resx"). This implementation would follow a convention over configuration approach.
Are there any tools/examples out there, which I've missed, which will make this job easier? Is there any framework (.NET4) support for this?

Easier than I thought. Here is my (improvable) code:
public void GenerateResx()
{
Type model = typeof(BuyCABModel);
List<Type> member = new List<Type>();
member.Add(typeof(RegisterModel));
member.Add(typeof(AddOpenAuthAccountModel));
member.Add(typeof(LoginModel));
CreateRes(member, "Views.Member.resx");
}
private void CreateRes(List<Type> models, string resxFile)
{
using (ResXResourceWriter writer = new ResXResourceWriter("c:\\temp\\"+resxFile))
{
foreach(Type model in models)
{
PropertyInfo[] ps= model.GetProperties();
foreach (PropertyInfo p in ps)
{
foreach (Attribute a in p.GetCustomAttributes(true))
{
if (a.GetType() == typeof(DisplayNameAttribute))
{
DisplayNameAttribute d = (DisplayNameAttribute)a;
writer.AddResource(p.Name + "_DisplayName", d.DisplayName);
}
else if (a.GetType() == typeof(DisplayAttribute))
{
DisplayAttribute d = (DisplayAttribute)a;
writer.AddResource(p.Name + "_DisplayName", d.Name);
}
}
}
}
writer.Generate();
writer.Close();
}
}
Tip: If you need the designer.cs code generated in Visual Studio just open the .resx in VS and toggle the Access Modifier DropDown

Related

How do I find the data type of an AD attribute not having a value

I am struggling to find the data type of AD attributes that are not having a value already.
Thus far a resulting DirecoryEntry only contains a property for attributes that already have a value. I can't find a method to obtain information about all other attributes.
Adding a value to the 'PropertiesToLoad' doesn't seem to do anything. The returned DirectoryEntry object contains all attributes (with values) regardless of what is added here.
Code used:
public void Test(string ldapPath)
{
Type orgType;
try
{
using (DirectoryEntry searchRoot = GetSearchRoot(ldapPath))
{
using (DirectorySearcher search = new DirectorySearcher(searchRoot))
{
search.Filter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=coen))";
search.PropertiesToLoad.Add("msExchHomeServerName");
SearchResult searchResult = search.FindOne();
{
using (DirectoryEntry entry = searchResult.GetDirectoryEntry())
{
if (entry != null)
{
if (entry.Properties.Contains("msExchHomeServerName"))
{
orgType = entry.Properties["msExchHomeServerName"].GetType();
}
else // The value is empty and NOT present in the current 'entry.Properties' object.
{
throw new Exception("Unknown type");
}
}
}
}
}
}
}
catch (Exception e)
{
<handle exception>
}
}
Is there a way to find the data type of the 'msExchHomeServerName' (or any of the 'empty' attributes) attribute?
Any help would be highly appreciated!
Coen
Active Directory has a schema. Obvious to say, not obvious to think about since, by default, you cannot explore it.
However, you can Register the Active Directory Schema MMC Snap-In1 and then, in a fresh instance of MMC, add that Snap-In.
This then allows you to explore the Classes and Attributes that make up your current AD schema (and add new classes/attributes if you know what you're doing and choose to do so).
msExchHomeServerName can then be discovered to be a "Unicode String", which means a plain string from C# should be acceptable. Note also that some types (particularly numeric ones) may also specify Minimums and Maximums which should be observed.
You can also explore the schema programatically via the ActiveDirectorySchema class, by e.g. calling ActiveDirectorySchema.GetCurrentSchema(); and then explore from there.
1I cannot remember if you need to have installed the general Domain Admin tools (such as Users and Computers) in order for that DLL to exist on your system.
Thanks to Damien_The_Unbeliever who pointed me in the right direction, I managed to create the following method:
public Dictionary<string, ActiveDirectorySyntax> GetAttributeSyntaxes(List<string> lstAttributeNames)
{
Dictionary<string, ActiveDirectorySyntax> dictRes = new Dictionary<string, ActiveDirectorySyntax>();
if (lstAttributeNames.Count > 0)
{
DirectoryContext directoryContext = new DirectoryContext(DirectoryContextType.DirectoryServer,
m_Server, m_UserName, m_Password);
using (ActiveDirectorySchema currentSchema = ActiveDirectorySchema.GetSchema(directoryContext))
{
using (ActiveDirectorySchemaClass objClass = currentSchema.FindClass("user"))
{
if (objClass != null)
{
ReadOnlyActiveDirectorySchemaPropertyCollection propcol = objClass.GetAllProperties();
foreach (ActiveDirectorySchemaProperty schemaProperty in propcol)
{
foreach (string attrName in lstAttributeNames)
{
if (schemaProperty.Name.Equals(attrName))
{
dictRes.Add(attrName, schemaProperty.Syntax);
break;
}
}
}
}
}
}
}
return dictRes;
}
The returned 'schemaProperty.Syntax' contains sufficient information to determine the actual data type.
Thanks Damien!

C# Sitecore get inherited templates

I'm trying to find all templates that inherit a particular template through code. I have the ID of the base template (Base Web Page), and I'm iterating through all of the template items in Sitecore looking for items that inherit Base Web Page.
foreach (var item in templateItems)
{
var baseTemplates = item.Template.BaseTemplates.ToList();
foreach (var baseTemplate in baseTemplates)
{
if (baseTemplate.ID == templateItem.ID)
{
inheritors.Add(item.ID.ToString());
}
}
}
However, item.Template.BaseTemplates gives me a list of the root level base templates; instead of giving me Base Web Page, it gives me the templates that Base Web Page inherits from (Advanced, Appearance, Help, etc)
Therefore I don't know if the item is actually inheriting Base Web Page or not.
Is there a method to get directly inherited templates? How can I find all templates that inherit Base Web Page?
The part item.Template in the
var baseTemplates = item.Template.BaseTemplates.ToList();
line is incorrect.
item.Template returns /sitecore/templates/System/Templates/Template here, so you're checking for a BaseTemplate of Template template always.
Your code should be:
foreach (var item in templateItems)
{
var baseTemplates = new TemplateItem(item).BaseTemplates.ToList();
foreach (var baseTemplate in baseTemplates)
{
if (baseTemplate.ID == templateItem.ID)
{
inheritors.Add(item.ID.ToString());
}
}
}
And the better approach may be using LinkDatabase to find all the referrers of your item which link from BaseTemplate field like that:
var links = Sitecore.Globals.LinkDatabase.GetItemReferrers(templateItem, false);
foreach (var link in links)
{
if (link.SourceFieldID == Sitecore.FieldIDs.BaseTemplate)
{
inheritors.Add(link.SourceItemID.ToString());
}
}

How to get the value of a property of a class in a non executing assembly through reflection

I'm having trouble while getting the value of the text property in a non-executing assembly; I read an assembly from disk via reflection, then i get all classes in the assembly to search for the Text property in a windows form class which is initialized by win forms designer. So far i have the following code:
static void Main(string[] args)
{
Assembly asm = Assembly.LoadFrom(Path.Combine(path, "Assembly.exe"));
PropertyInfo[] props;
foreach (Type t in asm.GetTypes())
{
var value = t.GetProperty("Text").GetValue(/*Not sure what to put here*/)
}
}
And this is how the designer generated the form
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None
Me.BackColor = System.Drawing.Color.FromArgb(CType(CType(0, Byte), Integer), CType(CType(128, Byte), Integer), CType(CType(128, Byte), Integer))
Me.ClientSize = New System.Drawing.Size(234, 181)
Me.Cursor = System.Windows.Forms.Cursors.Default
Me.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
Me.ForeColor = System.Drawing.SystemColors.WindowText
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
Me.Location = New System.Drawing.Point(581, 222)
Me.MaximizeBox = False
Me.MinimizeBox = False
Me.Name = "winform"
Me.RightToLeft = System.Windows.Forms.RightToLeft.No
Me.StartPosition = System.Windows.Forms.FormStartPosition.Manual
Me.Text = "Title"
Me.fraDías.ResumeLayout(False)
Me.ResumeLayout(False)
Keep in mind that the assembly is on disk and non-executing and that I want to retrieve the value of the Text property of every winform (I guess it should be somewhere hardcoded in the assembly since it was generated by the winforms designer)
Please tell me if this is possible, thanks!
Your requirements are contradictory, when you load an aseembly via reflection, and instantiate an object or try to get a property value, what happens is that some code begins to run, there is no way around that.
Remember that properties are just "syntax sugar" for a pair of methods, the getter and setter. Their current value is nothing but the value returned by the getter method, and when you change its value, you're in fact calling its setter method. So, to retrieve property values, you must make some code to run, even if it's a trivial get method.
I think maybe your confusion comes from the fact that you're using a designer to create the form. Particularly with the WinForms designer (WPF for instance is substantially different), all it does is to autogenerate some code for you. Setting properties, placing and moving controls around, what's happening under the hood is that it writes code that replicate your actions at runtime, specifically, it codes the InitializeComponent method. The real property value is set when the constructor is called (that in turn calls InitializeComponent), and then you may read/change using many properties.
What you would need to read those designer attributes is that those were hardcoded in some form of metadata, so that it's simply read as data and not as the result of code execution. That's not the case with WinForms, as it "saves" the form as code.
You cannot read a property of a class that has not been instantiated! The parameter you are missing is an instance of your type.
You must create an instance of the type with object o = Activator.CreateInstance(type); before accessing its members (unless they are static).
Your problem is related to how add-ins (plug-ins) can be loaded at runtime.
Here is how I made an Add-In Loader. Below, I will explain how you can adapt it to your problem. Add-Ins have to implement the IAddIn interface in my example. You are totally free in the definition of IAddIn. You could define it like this:
public interface IAddIn
{
bool OnLoad();
string Version { get; set; }
string Text { get; set; }
}
This allows you to access members without reflection.
public class AddInLoader
{
// Loads all non abstract types implementing IAddIn in all DLLs found in a folder.
public IList<IAddIn> Load(string folder)
{
var addIns = new List<IAddIn>();
string[] files = Directory.GetFiles(folder, "*.dll");
foreach (string file in files) {
addIns.AddRange(LoadFromAssembly(file));
}
return addIns;
}
// Loads all non abstract types implementing IAddIn found in a file.
private static IEnumerable<IAddIn> LoadFromAssembly(string fileName)
{
Assembly asm = Assembly.LoadFrom(fileName);
string addInInterfaceName = typeof(IAddIn).FullName;
foreach (Type type in asm.GetExportedTypes()) {
Type interfaceType = type.GetInterface(addInInterfaceName);
if (interfaceType != null &&
(type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract){
IAddIn addIn = (IAddIn)Activator.CreateInstance(type);
addIn.Version = asm.GetName().Version.ToString();
yield return addIn;
}
}
}
}
Now you can load and access the add-ins like this:
var loader = new AddInLoader();
IList<IAddIn> addIns = loader.Load(folderPath);
foreach (IAddIn addIn in addIns) {
if (addIn.OnLoad()) {
Console.WriteLine("Version = {0}, Text = {1}", addIn.Version, addIn.Text);
}
}
Reading the titles of Forms at runtime:
You can easily adapt this example. Instead of searching for types implementing an interface, search for types deriving from System.Windows.Forms.Form.
private static IEnumerable<Form> LoadFormsFromAssembly(string fileName)
{
Assembly asm = Assembly.LoadFrom(fileName);
foreach (Type type in asm.GetExportedTypes()) {
if (typeof(Form).IsAssignableFrom(type) &&
(type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract) {
Form form = (Form)Activator.CreateInstance(type);
yield return form;
}
}
}
Now you can get the texts of the forms like this:
var forms = LoadFormsFromAssembly(path);
foreach (Form frm in forms) {
Console.WriteLine(frm.Text);
}
Note: You must instantiate the forms, however you do not need to open (show) them. The code works only if the forms have a default constructor, i.e. a constructor without parameters.
You need an instance object for that type to get the value of a property.
It looks like you just want to check if a type has a "Text" property or not. You can to it by checking
bool hasTextProperty = t.GetProperty("Text") !=null;

Is it possible to execute C# code represented as string?

On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.

Custom control generate code at design time

Is it possible to write a control which can create/ modify a code file in the same project - (Along the lines of the how the form designer works). I can't see the actual creating / modification of files being troublesome - however I would like to avoid the 'xyz file has been modified - would you like to reload it?' dialogue.
To be honest I'm not expecting that I can without writing a plug in or something of the like.
Absolutley, take a look at the CodeDom: -
http://msdn.microsoft.com/en-us/library/y2k85ax6.aspx
Alternatively look into creating a Visual Studio Add-in: -
http://www.c-sharpcorner.com/UploadFile/mgold/AddIns11292005015631AM/AddIns.aspx
Cheers,
Phil.
This is indeed possible to do. After referencing EnvDTE
using EnvDTE;
var host = this.Container as IDesignerHost;
var dte = host.GetService(typeof(DTE)) as DTE;
var activeDoc = dte.ActiveDocument;
var project = activeDoc.ProjectItem.Collection.Parent as Project;
project.ProjectItems.AddFromFile("\\Test.cs");
As Plip stated, use CodeDom and more specifically - CodeDocSerialier.
Here`s a short example:
[Serializer(typeof(MySerializer))]
class MyControl : Control {}
class MySerializer : CodeDomSerializer
{
public override object Serialize(IDesignerSerializationManager manager, object value)
{
CodeDomSerializer baseSerializer;
CodeStatementCollection statements;
CodeExpression targetObject;
if(manager == null || value == null)
{
return null;
}
baseSerializer = (CodeDomSerializer)manager.GetSerializer(typeof(MyControl).BaseType, typeof(CodeDomSerializer));
statements = baseSerializer.Serialize(manager, value) as CodeStatementCollection;
if(statements == null)
{
statements = new CodeStatementCollection();
}
targetObject = GetExpression(manager, value);
if(targetObject != null)
{
// add 'myControl.Visible = true;' statement.
statements.Add(
new CodeAssignStatement(
new CodeFieldReferenceExpression(targetObject, "Visible"),
new CodePrimitiveExpression(true)));
}
return statements;
}
}

Categories

Resources