Default values for arguments in rehosted WF4 designer fail to parse - c#

The results from a rehosted designer (WF4) have an issue when adding a default value to an argument. Every other case seems to work fine. This is the (abridged) xaml of a (nearly) empty workflow.
<Activity mc:Ignorable="sap" x:Class="{x:Null}" this:_b40c.NewArg="test" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation"
xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:this="clr-namespace:" xmlns:twc="clr-namespace:Telogis.Workflow.CustomerApi;assembly=Telogis.Workflow.Activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:Members>
<x:Property Name="AuthenticationHeader" Type="InArgument(twc:AuthenticationHeader)" />
<x:Property Name="BaseTdeUri" Type="InArgument(x:Uri)" />
<x:Property Name="NewArg" Type="InArgument(x:String)" />
</x:Members>
<sap:VirtualizedContainerService.HintSize>654,676</sap:VirtualizedContainerService.HintSize>
<mva:VisualBasic.Settings>Assembly references and imported namespaces serialized as XML namespaces</mva:VisualBasic.Settings>
<Flowchart />
</Activity>
Specifically when default value is added, the following additions are made to the definition: this:_b40c.NewArg="test" and xmlns:this="clr-namespace:"
xmlns:this="clr-namespace:" is invalid as it doesn't point anywhere and can't be parsed with ActivityXamlServices.Load(stream); (it throws XamlObjectWriterException: "'Cannot set unknown member '{clr-namespace:}_b40c.NewArg'.' ...)
This seems to occur whatever the specified type of the argument is.
Any idea what could be causing this?
Update
I was using an ActivityBuilder to utilise the activity in the first place. This was fine, but as I hadn't provided it with a name, it had to generate a key, in the example above _b40c. ActivityXamlServices.Load has some kind off issue processing these keys. However, simply defining a name for ActivityBuilder seems to do the trick.
This still doesn't answer why it creates xmlns:this="clr-namespace:" without an actual namespace.

Your workflow xaml is invalid. I'm not sure where you got it or how it got into this state.
I can tell this because
<Activity
x:Class="{x:Null}"
this:_b40c.NewArg="test"
xmlns:this="clr-namespace:"
the clr-style namespace declaration is invalid. It should read
clr-namespace:Some.Namespace.In.The.Current.Assembly
or
clr-namespace:Some.Namespace;assembly=SomeAssemblyWithSomeNamespace
As your declaration is malformed, the this xml namespace cannot be parsed by the XamlObjectWriter in order to determine what namespace/assembly your _b40c type exists in. Also, that looks highly suspicious as well. And I've never seen an x:Class set to null before. That also strikes me as malformed.

If I understand well - this is WF Designer bug.
I've faced with this problem when I had had to support default value definition for InArgument<T> in my custom WF designer. I still surprised lack of support for this basic procedure.
There are 2 reason of failure:
Definition of {x:Null} in x:Class attribute
Invalid definition of xmlns:this attribute
And the main problem is invalid definition of Argument's default value: this:_effe.MyArgument="asd". The definition of default value for argument should be equal to this:MyXamlClassName.MyArgument="asd". For example, if your x:Cass definition is x:Class="MyNamespace.MyClass", Argument definition should be this:MyClass.MyArgument="asd".
I resolved it by intervention into XAML saving process:
After calling of
_workflowDesigner.Save(_editedFile);
I added these two lines:
#region x:Class and Argument<T> default value issues solution
await CreateAttributeValue(_editedFile, ConstXClassAttributeName, typeof(App).Namespace + "." + Path.GetFileNameWithoutExtension(_editedFile));
//should finish first operation before second operation begins to avoid I/O exception
await CreateAttributeValue(_editedFile, ConstNamespaceAttributeName, ConstXamlClrNamespace + typeof(App).Namespace);
await RepairArgsAttributes(_editedFile);
#endregion
This is the methods definition:
/// <summary>
/// Reason of using of this method: bug in workflow designer. When you save your xaml file, WF Designer assign "{x:Null}" to x:Class attribute
/// Bug: In addition, if you want to set default value for your InArgument<T>, it defines attribute "this" (namespace declaration) with empty value. When you try to open your file, designer fails to parse XAML.
/// </summary>
/// <param name="editedFile"></param>
/// <param name="attribteName"></param>
/// <param name="attributeValueToReplace"></param>
private static async Task CreateAttributeValue(string editedFile, string attribteName, string attributeValueToReplace)
{
XmlDocument xmlDoc = new XmlDocument();
await Task.Run(() => xmlDoc.Load(editedFile));
await Task.Run(() =>
{
var attributteToReplace = xmlDoc.FirstChild.Attributes?[attribteName];
if (null != attributteToReplace)
{
xmlDoc.FirstChild.Attributes[attribteName].Value = attributeValueToReplace;
xmlDoc.Save(editedFile);
}
});
}
/// <summary>
/// Bug in Workflow designer: workflow designer saves declaration for In/Out Arguments in invalid format. Means, that it is unable to open the same file it saved itself. This method fixes the Arguments declaration in XAML xmlns
/// </summary>
/// <param name="editedFile"></param>
/// <returns></returns>
private async Task RepairArgsAttributes(string editedFile)
{
XmlDocument xmlDoc = new XmlDocument();
await Task.Run(() => xmlDoc.Load(editedFile));
await Task.Run(() =>
{
for (int i = 0; i < xmlDoc.FirstChild.Attributes.Count; i++)
{
if (xmlDoc.FirstChild.Attributes[i].Name.StartsWith(ConstInvalidArgStarts))
{
string innvalidAttrName = xmlDoc.FirstChild.Attributes[i].Name;//extraction of full argument declaration in xmlns
string[] oldStrings = innvalidAttrName.Split('.');//extraction of arguemnt name string
string localName = Path.GetFileNameWithoutExtension(editedFile) + "." + oldStrings[1];//build valid argment declaration without perfix
string valueBackup = xmlDoc.FirstChild.Attributes[i].Value;//saving of default value of Arguemnt<T>
xmlDoc.FirstChild.Attributes.RemoveNamedItem(xmlDoc.FirstChild.Attributes[i].Name);//removal of invalid Arguemnt declaration with default value. WARNING: when you remove attribue, at this moment you have another item at the place xmlDoc.FirstChild.Attributes[i]
//definition of new valid attribute requries: set separelly attribute prefix, localName (not "name" - it causes invalid attribute definition) and valid namespace url (in our case it's namespace deifinition in "this")
XmlAttribute attr = xmlDoc.CreateAttribute(ConstArgPrefix, localName, xmlDoc.FirstChild.Attributes[ConstNamespaceAttributeName].Value);
attr.Value = valueBackup;
xmlDoc.FirstChild.Attributes.InsertBefore(attr, xmlDoc.FirstChild.Attributes[i]);//define new correct Argument declaration attribute at the same place where was invalid attribute. When you put valid attribute at the same place your recover valid order of attributes that was changed while removal of invalid attribute declaration
}
}
xmlDoc.Save(editedFile);
});
}
The constants definition are:
#region Constants
private const string ConstXClassAttributeName = "x:Class";
private const string ConstXamlClrNamespace = "clr-namespace:";
private const string ConstNamespaceAttributeName = "xmlns:this";
private const string ConstInvalidArgStarts = #"this:_";
private const string ConstArgPrefix = #"this";
#endregion
This solution should resolve your issue too.

Related

Swashbuckle parameter descriptions

I'm using SwaggerResponse attributes to decorate my api controller actions, this all works fine, however when I look at the generated documentation the description field for parameters is empty.
Is a there an attribute based approach to describe action parameters (rather than XML comments)?
With the latest Swashbuckle, or better said at least the Swashbuckle.AspNetCore variant which I'm using, the Description field for parameters can now be displayed correctly as output.
It does require the following conditions to be met:
XML comments must be enabled and configured with Swagger
Parameters should be explicitly decorated with either [FromRoute], [FromQuery], [FromBody] etc.
The same for the method type (get/post/put etc.), which should be decorated with [Http...]
Describe the parameter as usual with a <param ...> xml comment
A full sample looks like this:
/// <summary>
/// Short, descriptive title of the operation
/// </summary>
/// <remarks>
/// More elaborate description
/// </remarks>
/// <param name="id">Here is the description for ID.</param>
[ProducesResponseType(typeof(Bar), (int)HttpStatusCode.OK)]
[HttpGet, Route("{id}", Name = "GetFoo")]
public async Task<IActionResult> Foo([FromRoute] long id)
{
var response = new Bar();
return Ok(response);
}
Which produces the following output:
You should confirm you are allowing Swagger to use XML comments
httpConfig.EnableSwagger(c => {
if (GetXmlCommentsPath() != null) {
c.IncludeXmlComments(GetXmlCommentsPath());
}
...
...
);
protected static string GetXmlCommentsPath() {
var path = HostingEnvironment.MapPath("path to your xml doc file");
return path;
}
You should also check you are generating XML doc for your desired project. Under your desired project Properties (Alt + Enter on top of the project or Right Click -> Properties) -> Build -> Check XML documentation file
For completeness sake, when using latest version of Swashbuckle.AspNetCore (2.1.0) and Swashbuckle.SwaggerGen/Ui (6.0.0), enable Xml documentation file generation in your project's Build
Then the following to your ConfigureServices() method:
services.ConfigureSwaggerGen(options =>
{
options.SingleApiVersion(new Info
{
Version = "v1",
Title = "My API",
Description = "API Description"
});
options.DescribeAllEnumsAsStrings();
var xmlDocFile = Path.Combine(AppContext.BaseDirectory, $"{_hostingEnv.ApplicationName}.xml");
if (File.Exists(xmlDocFile))
{
var comments = new XPathDocument(xmlDocFile);
options.OperationFilter<XmlCommentsOperationFilter>(comments);
options.ModelFilter<XmlCommentsModelFilter>(comments);
}
});

Displaying the full name with Enums and resource file in c# MVC

Giving this one more try
I have an Enums.cs file where I have the following code
public enum AuthorizingLevels
{
[Display(Name = "AuthorizingLevels_SysSupport", ResourceType = typeof(Resources.Enums))]
SysSupport
}
And when I try calling it to display the Name, it doesn't work
ViewBag.AuthGroupFullName = Enums.AuthorizingLevels.SysSupport.ToString();
It just displays the SysSupport instead of the full name Systems Support
I went to a link provided in my previous question (How to get the Display Name Attribute of an Enum member via MVC razor code?) and added the code by Peter Kerr
/// <summary>
/// A generic extension method that aids in reflecting
/// and retrieving any attribute that is applied to an `Enum`.
/// </summary>
public static string GetDisplayName(this Enum enumValue)
{
var displayAttrib = enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
var name = displayAttrib.Name;
var resource = displayAttrib.ResourceType;
return String.IsNullOrEmpty(name) ? enumValue.ToString()
: resource == null ? name
: new ResourceManager(resource).GetString(name);
}
However, I get an error on the line
: new ResourceManager(resource).GetString(name);
An exception of type 'System.Resources.MissingManifestResourceException' occurred in mscorlib.dll but was not handled in user code
Additional information: Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "Resources.Enums.resources" was correctly embedded or linked into assembly "FWS" at compile time, or that all the satellite assemblies required are loadable and fully signed.
The resource file is the right one minus the .resources at the end... not sure if that should be there or not.
What am I doing wrong? I'm learning MVC and c# as I go so any help would be greatly appreciated.
Thank you
Try this. It is fairly hacky but it should hopefully give you what you need. I use Assembly.GetExecutingAssembly().GetManifestResourceNames() to get the names of all the resources in the executing assembly and then I attempt to match the file up correctly using some linq. If it finds a file that looks ok, a new ResourceManager is created as you were using in your original code.
/// <summary>
/// A generic extension method that aids in reflecting
/// and retrieving any attribute that is applied to an `Enum`.
/// </summary>
public static string GetDisplayName(this Enum enumValue)
{
var displayAttrib = enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
var name = displayAttrib.Name;
if (String.IsNullOrEmpty(name))
{
return enumValue.ToString();
}
else
{
var resource = displayAttrib.ResourceType;
if (resource != null)
{
var resources = Assembly.GetExecutingAssembly().GetManifestResourceNames()
.Where(x => x.EndsWith(String.Format("{0}.resources", resource.Name)))
.Select(x => x.Replace(".resources", string.Empty)).ToList();
if (resources.Any())
{
return new ResourceManager(resources.First(), Assembly.GetExecutingAssembly()).GetString(name);
}
}
return name;
}
}

Entity Framework - Capitalizing first property name letter

In general, I tend to name my sql database columns using the following camel case convention:
camelCase (notice that the first letter is in lower case).
But when working with C#, I like to name my object's public properties in the following convention:
PascalCase (notice the first is in uppwer case).
Entity Framework's default behaviour is to name the created classes' properties to match their relative column names as they are in the database.
Is there any property in the project/solution level which can be changed in order to solve this issue?
Yes there is. Here you can see the full example:
using System;
using System.Data.Entity;
namespace ConsoleApplication1
{
class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Properties().Configure(c =>
{
var name = c.ClrPropertyInfo.Name;
var newName = char.ToLower(name[0]) + name.Substring(1);
c.HasColumnName(newName);
});
}
public MyDbCondenxt(string cs) : base(cs)
{
}
public DbSet<MyModel> MyModels { get; set; }
}
class Program
{
static void Main(string[] args)
{
var context = new MyDbContext ("DefaultConnection");
context.MyModels.Add(new MyModel{SomeText = "hello"});
context.SaveChanges();
Console.ReadLine();
}
}
class MyModel
{
public int Id { get; set; }
public string SomeText { get; set; }
}
}
The property name is "SomeText" and the column name is "someText".
I dont know of a solution level but you can set an attribute on your entity
[Table("myEntity")]
public class MyEntity{}
Achieving this is not impossible, but it's not going to be easy. Some of it depends which type of ef model you're working with, either code first or database/model first (they are similar in this regard), or if you're using the old ObjectContext based methods.
In general, EF uses T4 templating to create the actual classes and models in all but code first, so it's possible to edit the T4 templates and generate whatever you want, such as automatically generating properties with PascalCasing.
If you're using Code first (which doesn't really require that you code first, it's a terrible name) then you can use the Entity Framework power tools to reverse engineer your database to code first models, and again it uses T4 to do this.
If you're using actual code first (ie you create your models and generate your database from the model), then it may not be possible in the existing EF5 or lower. EF6 (currently in alpha) has something known as custom conventions that you could probably use for this, but it's still a long way from production quality.
Earlier I also had that kind of problem. So I simply write a tool in c# to rename the existing edmx file and then after the renaming each section of edmx file,Next using T4 template regenerate the Poco classes. It resolved my problem. It generates the expected POCO classes with Camel Case properties. Basically in the edmx we have 3 layers. So we need to modify 2 layers of them.
MappingsSection
ConceptualModelsSection
Please find the following class to do that.
namespace Edmx_Manager_V1._0
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
public static class RenameManager
{
public static XmlDocument Document = new XmlDocument();
public static string FilePath;
public static XmlNamespaceManager nsmgr;
/// <summary>
/// Updates the conceptual models section.
/// </summary>
public static void UpdateConceptualModelsSection()
{
///////////////////////update ConceptualModels section//////////////////////////////////////////////////////////
XmlNodeList Schema = Document.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/edm:Schema", nsmgr);
XmlNode SchemaNode = Schema[0];
XmlElement SchemaNodeXmlElement = SchemaNode as XmlElement;
//get all EntitySet nodes under EntityContainer node
XmlNodeList EntitySetlist = SchemaNodeXmlElement.GetElementsByTagName("EntitySet");
//get all EntityType nodes under SchemaNode
XmlNodeList EntityTypelist = SchemaNodeXmlElement.GetElementsByTagName("EntityType");
foreach (XmlNode EntityTypenode in EntityTypelist)
{
//to call GetElementsByTagName we need XmlElement object
XmlElement EntityTypenodeelement = EntityTypenode as XmlElement;
//get all PropertyRef nodes under EntityType node
XmlNodeList PropertyReflist = EntityTypenodeelement.GetElementsByTagName("PropertyRef");
foreach (XmlNode PropertyRefnode in PropertyReflist)
{
//update name attribute of Key/PropertyRef nodes
XmlAttribute PropertyRef_nameAttribute = PropertyRefnode.Attributes["Name"];
PropertyRef_nameAttribute.Value = UppercaseFirst(PropertyRef_nameAttribute.Value);
}
//get all Property nodes under EntityType node
XmlNodeList Propertylist = EntityTypenodeelement.GetElementsByTagName("Property");
foreach (XmlNode Propertynode in Propertylist)
{
//update name attribute of PropertyRef nodes
XmlAttribute Property_nameAttribute = Propertynode.Attributes["Name"];
Property_nameAttribute.Value = UppercaseFirst(Property_nameAttribute.Value);
}
//get all NavigationProperty nodes under EntityType node
XmlNodeList NavigationPropertylist = EntityTypenodeelement.GetElementsByTagName("NavigationProperty");
foreach (XmlNode NavigationPropertynode in NavigationPropertylist)
{
//update name attribute of NavigationProperty nodes
XmlAttribute NavigationPropertynode_nameAttribute = NavigationPropertynode.Attributes["Name"];
NavigationPropertynode_nameAttribute.Value = UppercaseFirst(NavigationPropertynode_nameAttribute.Value) + "s";// we append "s" for nav properties
}
}
//get Association node under Schema node
XmlNodeList Associationlist = SchemaNodeXmlElement.GetElementsByTagName("Association");
//get all Association nodes and process
foreach (XmlNode AssociationNode in Associationlist)
{
if (AssociationNode != null)
{
XmlElement AssociationNodeXmlElement = AssociationNode as XmlElement;
//get all end nodes under Association
XmlNodeList EndNodelist2 = AssociationNodeXmlElement.GetElementsByTagName("End");
//get all PropertyRef nodes under Association
XmlNodeList PropertyReflist2 = AssociationNodeXmlElement.GetElementsByTagName("PropertyRef");
foreach (XmlNode PropertyRefNode2 in PropertyReflist2)
{
//update Type attribute
XmlAttribute PropertyRefNode2Attribute = PropertyRefNode2.Attributes["Name"];
PropertyRefNode2Attribute.Value = UppercaseFirst(PropertyRefNode2Attribute.Value);
}
}
}
Console.WriteLine("ConceptualModelSection updated..");
}
/// <summary>
/// Updates the mappings section.
/// </summary>
public static void UpdateMappingsSection()
{
///////////////////////update edmx:Mappings section//////////////////////////////////////////////////////////
XmlNodeList EntityContainerMapping = Document.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:Mappings/cs:Mapping", nsmgr);
XmlNode EntityContainerMapping_Node = EntityContainerMapping[0];
XmlElement EntityContainerMappingNode_XmlElement = EntityContainerMapping_Node as XmlElement;
// update name attribute of all EntitySetMapping nodes
//get all EntitySetMapping nodes
XmlNodeList EntitySetMappinglist = EntityContainerMappingNode_XmlElement.GetElementsByTagName("EntitySetMapping");
//get all EntityTypeMapping nodes
XmlNodeList EntityTypeMappinglist = EntityContainerMappingNode_XmlElement.GetElementsByTagName("EntityTypeMapping");
//get all ScalarProperty nodes
XmlNodeList ScalarPropertyist = EntityContainerMappingNode_XmlElement.GetElementsByTagName("ScalarProperty");
foreach (XmlNode ScalarPropertyNode in ScalarPropertyist)
{
XmlAttribute nameAttribute = ScalarPropertyNode.Attributes["Name"];
nameAttribute.Value = UppercaseFirst(nameAttribute.Value);
}
Console.WriteLine("MappingSection updated..");
}
/// <summary>
/// Uppercases the first.
/// </summary>
/// <param name="name">The name.</param>
/// <returns></returns>
private static string UppercaseFirst(string name)
{
return char.ToUpper(name[0]) + name.Substring(1);
}
}
}
Usage :
RenameManager.FilePath = #"C:\Users\therath\testApp\Model1.edmx";
// Path of edmx file in the your solution
RenameManager.Document.Load(#RenameManager.FilePath);
RenameManager.nsmgr = new XmlNamespaceManager(RenameManager.Document.NameTable);
RenameManager.nsmgr.AddNamespace("edmx", "http://schemas.microsoft.com/ado/2008/10/edmx");
RenameManager.nsmgr.AddNamespace("edm", "http://schemas.microsoft.com/ado/2008/09/edm");
//nsmgr.AddNamespace("ssdl", "http://schemas.microsoft.com/ado/2009/02/edm/ssdl");
RenameManager.nsmgr.AddNamespace("cs", "http://schemas.microsoft.com/ado/2008/09/mapping/cs");
try
{
RenameManager.UpdateConceptualModelsSection();
RenameManager.UpdateMappingsSection();
RenameManager.Document.Save(#RenameManager.FilePath);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
If you generate the edmx again you may need to run this tool again.
there are ways to do that , some of are already pointed out by others..
i found one class which does that ...
namespace System.Data.Entity.ModelConfiguration.Conventions
{
/// <summary>
/// Convention to convert any data types that were explicitly specified, via data annotations or <see cref="T:System.Data.Entity.DbModelBuilder"/> API,
/// to be lower case. The default SqlClient provider is case sensitive and requires data types to be lower case. This convention
/// allows the <see cref="T:System.ComponentModel.DataAnnotations.ColumnAttrbiute"/> and <see cref="T:System.Data.Entity.DbModelBuilder"/> API to be case insensitive.
///
/// </summary>
public sealed class ColumnTypeCasingConvention : IDbConvention<DbTableColumnMetadata>, IConvention
{
internal ColumnTypeCasingConvention()
{
}
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
void IDbConvention<DbTableColumnMetadata>.Apply(DbTableColumnMetadata tableColumn, DbDatabaseMetadata database)
{
if (string.IsNullOrWhiteSpace(tableColumn.TypeName))
return;
tableColumn.TypeName = tableColumn.TypeName.ToLowerInvariant();
}
}
}
explicit implementation of idbconvertion does that thing which you can implement
another one is to
go to solution => and find folder obj/debug/edmxresourcestoembed
there are three files db.csdl , db.msl , db.ssdl
edit msl file => you 'll see mapping for each table like as under.
<EntitySetMapping Name="Address">
<EntityTypeMapping TypeName="IsTypeOf(AdventureWorksLTModel.Address)">
<MappingFragment StoreEntitySet="Address">
<ScalarProperty Name="AddressID" ColumnName="AddressID" />
<ScalarProperty Name="AddressLine1" ColumnName="AddressLine1" />
<ScalarProperty Name="AddressLine2" ColumnName="AddressLine2" />
<ScalarProperty Name="City" ColumnName="City" />
<ScalarProperty Name="StateProvince" ColumnName="StateProvince" />
<ScalarProperty Name="CountryRegion" ColumnName="CountryRegion" />
<ScalarProperty Name="PostalCode" ColumnName="PostalCode" />
<ScalarProperty Name="rowguid" ColumnName="rowguid" />
<ScalarProperty Name="ModifiedDate" ColumnName="ModifiedDate" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
You can change it in the .edmx file. Just click on the property name and rename it to camel case and the same will be reflected whenever you try to access it using the object.
You can use external tools like
http://www.devart.com/entitydeveloper/
With the inbuild EDMX desinger this isnt possible, since the "update from database" routine doesnt have such a function.
The class code is generated from a T4 template. Depending on your configuration, this may already be in your project, or the EDMX may be using a built-in one, in which case you would need to add your own and set the "code generation strategy" to "None" in the EDMX properties (through the model browser). From this file it is fairly easy to find and modify the class and property name generation.
The default is calling an Escape function, which is defined in include files at "IDE/Extensions/Microsoft/Entity Framework Tools/Templates/Includes" under the Visual Studio folder, and ultimately just calls CreateEscapedIdentifier. Simply call this with the capitalized version of the string.
FYI: These names are coming from the EntityType and NavigationProperty objects as defined in the EDMX, not directly from the database. If you are using the "generate from database" feature for the EDMX, the names may have already gone through a transformation and the original table names are not retained in the model. This probably won't be an issue, though.
Well you can edit the names in the edmx actually, but everytime you refresh from database it's back to doing it again.
The only viable approach when using edmx type datatypes is to have the correct names (with capital letters) in the tables of the database or it will be to tedious.
You can of cause use link to sql instead, in which case you define your data classes and just supply a name property. But be warned this approach is significantly more manual and is being aborted most places because it requires much more thinking to set up that edmx autogeneration which is a click, click, next approach.
So yes you can edit the names in the edmx, but consider abandoning your camelCasing for tables instead, in hommage to edmx which saves you for a ton on work in return, or your .net autogenerated proxy classes will look wierd, as you know.

How do I programmatically get the GUID of an application in C# with .NET?

I need to access the assembly of my project in C#.
I can see the GUID in the 'Assembly Information' dialog in under project properties, and at the moment I have just copied it to a const in the code. The GUID will never change, so this is not that bad of a solution, but it would be nice to access it directly. Is there a way to do this?
Try the following code. The value you are looking for is stored on a GuidAttribute instance attached to the Assembly
using System.Runtime.InteropServices;
static void Main(string[] args)
{
var assembly = typeof(Program).Assembly;
var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute),true)[0];
var id = attribute.Value;
Console.WriteLine(id);
}
Or, just as easy:
string assyGuid = Assembly.GetExecutingAssembly().GetCustomAttribute<GuidAttribute>().Value.ToUpper();
It works for me...
Another way is to use Marshal.GetTypeLibGuidForAssembly.
According to MSDN:
When assemblies are exported to type libraries, the type library is assigned a LIBID. You can set the LIBID explicitly by applying the System.Runtime.InteropServices.GuidAttribute at the assembly level, or it can be generated automatically. The Tlbimp.exe (Type Library Importer) tool calculates a LIBID value based on the identity of the assembly. GetTypeLibGuid returns the LIBID that is associated with the GuidAttribute, if the attribute is applied. Otherwise, GetTypeLibGuidForAssembly returns the calculated value. Alternatively, you can use the GetTypeLibGuid method to extract the actual LIBID from an existing type library.
You should be able to read the GUID attribute of the assembly via reflection. This will get the GUID for the current assembly:
Assembly asm = Assembly.GetExecutingAssembly();
object[] attribs = asm.GetCustomAttributes(typeof(GuidAttribute), true);
var guidAttr = (GuidAttribute) attribs[0];
Console.WriteLine(guidAttr.Value);
You can replace the GuidAttribute with other attributes as well, if you want to read things like AssemblyTitle, AssemblyVersion, etc.
You can also load another assembly (Assembly.LoadFrom and all) instead of getting the current assembly - if you need to read these attributes of external assemblies (for example, when loading a plugin).
For an out-of-the-box working example, this is what I ended up using based on the previous answers.
using System.Reflection;
using System.Runtime.InteropServices;
label1.Text = "GUID: " + ((GuidAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(GuidAttribute), false)).Value.ToUpper();
Alternatively, this way allows you to use it from a static class:
/// <summary>
/// public GUID property for use in static class </summary>
/// <returns>
/// Returns the application GUID or "" if unable to get it. </returns>
static public string AssemblyGuid
{
get
{
object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(GuidAttribute), false);
if (attributes.Length == 0) { return String.Empty; }
return ((System.Runtime.InteropServices.GuidAttribute)attributes[0]).Value.ToUpper();
}
}
There wasn't any luck here with the other answers, but I managed to work it out with this nice one-liner:
((GuidAttribute)(AppDomain.CurrentDomain.DomainManager.EntryAssembly).GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value
To get the appID you could use the following line of code:
var applicationId = ((GuidAttribute)typeof(Program).Assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;
For this you need to include the System.Runtime.InteropServices;
Use:
string AssemblyID = Assembly.GetEntryAssembly().GetCustomAttribute<GuidAttribute>().Value;
Or in VB.NET:
Dim AssemblyID As String = Assembly.GetEntryAssembly.GetCustomAttribute(Of GuidAttribute).Value

How can I discover the "path" of an embedded resource?

I am storing a PNG as an embedded resource in an assembly. From within the same assembly I have some code like this:
Bitmap image = new Bitmap(typeof(MyClass), "Resources.file.png");
The file, named "file.png" is stored in the "Resources" folder (within Visual Studio), and is marked as an embedded resource.
The code fails with an exception saying:
Resource MyNamespace.Resources.file.png cannot be found in class MyNamespace.MyClass
I have identical code (in a different assembly, loading a different resource) which works. So I know the technique is sound. My problem is I end up spending a lot of time trying to figure out what the correct path is. If I could simply query (eg. in the debugger) the assembly to find the correct path, that would save me a load of headaches.
This will get you a string array of all the resources:
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
I find myself forgetting how to do this every time as well so I just wrap the two one-liners that I need in a little class:
public class Utility
{
/// <summary>
/// Takes the full name of a resource and loads it in to a stream.
/// </summary>
/// <param name="resourceName">Assuming an embedded resource is a file
/// called info.png and is located in a folder called Resources, it
/// will be compiled in to the assembly with this fully qualified
/// name: Full.Assembly.Name.Resources.info.png. That is the string
/// that you should pass to this method.</param>
/// <returns></returns>
public static Stream GetEmbeddedResourceStream(string resourceName)
{
return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
}
/// <summary>
/// Get the list of all emdedded resources in the assembly.
/// </summary>
/// <returns>An array of fully qualified resource names</returns>
public static string[] GetEmbeddedResourceNames()
{
return Assembly.GetExecutingAssembly().GetManifestResourceNames();
}
}
I'm guessing that your class is in a different namespace. The canonical way to solve this would be to use the resources class and a strongly typed resource:
ProjectNamespace.Properties.Resources.file
Use the IDE's resource manager to add resources.
I use the following method to grab embedded resources:
protected static Stream GetResourceStream(string resourcePath)
{
Assembly assembly = Assembly.GetExecutingAssembly();
List<string> resourceNames = new List<string>(assembly.GetManifestResourceNames());
resourcePath = resourcePath.Replace(#"/", ".");
resourcePath = resourceNames.FirstOrDefault(r => r.Contains(resourcePath));
if (resourcePath == null)
throw new FileNotFoundException("Resource not found");
return assembly.GetManifestResourceStream(resourcePath);
}
I then call this with the path in the project:
GetResourceStream(#"DirectoryPathInLibrary/Filename");
The name of the resource is the name space plus the "pseudo" name space of the path to the file. The "pseudo" name space is made by the sub folder structure using \ (backslashes) instead of . (dots).
public static Stream GetResourceFileStream(String nameSpace, String filePath)
{
String pseduoName = filePath.Replace('\\', '.');
Assembly assembly = Assembly.GetExecutingAssembly();
return assembly.GetManifestResourceStream(nameSpace + "." + pseduoName);
}
The following call:
GetResourceFileStream("my.namespace", "resources\\xml\\my.xml")
will return the stream of my.xml located in the folder-structure resources\xml in the name space: my.namespace.

Categories