37
Native WinRT EDM T4 C++/CX Auto Generated Code 2013-03-20 Prepared by: Eduardo Sobrino File Name: 2013-03-20 Native WinRT EDM T4 C++ Auto Generated Code.docx Location: (great) Vernon, CT Weather: Bad (very cold and windy, I hate winter; but already expecting next Christmas) Keywords: C++/CX, Native Code, EDM, Win Store App, JSON, WinRT, C++/CX Auto Generated Code Source URL: esobrino.wordpress.com Copyright: Open Knowledge © 2013 Licensing: GNU GPL Last Update: 2013-03-24 Today it is silly to think that you will do any kind of sizable project on your own and even less create any (also sizable) code base library or framework without a tool that help you automate the process. I rather spend time working with the graphical model and let the tool generate the code since it is a tedious task and if not properly done you end up with a hand coded mess or crappy code. From experience very few coders will generate clean, maintainable and readable code and be consistent doing this. The code generation process allows you to standardize on the “Coding Style” and structure. I do encourage the reader to adopt such posture and model more and code less. Since I am focusing in Native WinRT C++/CX in my blog and I have not found any single reference of C++/CX T4 here is my take on the subject, hope you find it useful. GOALS AND OBJECTIVES Just one goal, automate the C++/CX code generation by creating necessary T4 scripts using EDM in Visual Studio 2012 tools. The developer will draw entities (classes), add enums, relationships and associations, document those as necessary and the code in C++/CX will automatically be generated for him. Generated code should include needed entities interfaces, classes that implement those and basic support methods to clear and nullify properties. STRATEGIES & EXPERIENCES A class should have a way to set its own default state or undefined it. For that purpose you will see that I do use “enum”s making sure that the first enumerator do state the undefined state (-1) or its unknown (0) state where the defaults should be set. To help setting up those 2 states I am including 2 related methods per class, ClearFields to setup the unknown state and Nullify to setup it as undefined. For this first experiment I am scanning the EDM schema twice, one to generate the interfaces and the second the classes into “*.h” files. At a later time I will do a third pass to generate the “*.cpp” files. If a class should include a unique id and a reference day the first to uniquely identify the item (being it a name, phone number, URL, other) and have a reference day since when the item is relevant. Instances should also include the type they represent using an enumerator to categorize the item, e.g. for a phone the enumerator could be “Home Phone”, “Work Phone”, other. I did spend just a few hours (8 at most) and got the results I am showing ahead. It is relatively easy to modify the EDM T4 (*.tt) files to get the expected C++/CX code so I guess others can further modify the one I am providing for their own purposes, or just do their own.

Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

Native WinRT EDM T4 C++/CX Auto Generated Code

2013-03-20

Prepared by: Eduardo Sobrino File Name: 2013-03-20 Native WinRT EDM T4 C++ Auto Generated Code.docx Location: (great) Vernon, CT Weather: Bad (very cold and windy, I hate winter; but already expecting next Christmas) Keywords: C++/CX, Native Code, EDM, Win Store App, JSON, WinRT, C++/CX Auto Generated Code Source URL: esobrino.wordpress.com Copyright: Open Knowledge © 2013 Licensing: GNU GPL Last Update: 2013-03-24

Today it is silly to think that you will do any kind of sizable project on your own and even less create any (also sizable) code base library or framework without a tool that help you automate the process. I rather spend time working with the graphical model and let the tool generate the code since it is a tedious task and if not properly done you end up with a hand coded mess or crappy code. From experience very few coders will generate clean, maintainable and readable code and be consistent doing this. The code generation process allows you to standardize on the “Coding Style” and structure. I do encourage the reader to adopt such posture and model more and code less. Since I am focusing in Native WinRT C++/CX in my blog and I have not found any single reference of C++/CX T4 here is my take on the subject, hope you find it useful.

GOALS AND OBJECTIVES

Just one goal, automate the C++/CX code generation by creating necessary T4 scripts using EDM in Visual Studio 2012 tools. The developer will draw entities (classes), add enums, relationships and associations, document those as necessary and the code in C++/CX will automatically be generated for him. Generated code should include needed entities interfaces, classes that implement those and basic support methods to clear and nullify properties.

STRATEGIES & EXPERIENCES

A class should have a way to set its own default state or undefined it. For that purpose you will see that I do use “enum”s making sure that the first enumerator do state the undefined state (-1) or its unknown (0) state where the defaults should be set. To help setting up those 2 states I am including 2 related methods per class, ClearFields to setup the unknown state and Nullify to setup it as undefined. For this first experiment I am scanning the EDM schema twice, one to generate the interfaces and the second the classes into “*.h” files. At a later time I will do a third pass to generate the “*.cpp” files.

If a class should include a unique id and a reference day the first to uniquely identify the item (being it a name, phone number, URL, other) and have a reference day since when the item is relevant. Instances should also include the type they represent using an enumerator to categorize the item, e.g. for a phone the enumerator could be “Home Phone”, “Work Phone”, other.

I did spend just a few hours (8 at most) and got the results I am showing ahead. It is relatively easy to modify the EDM T4 (*.tt) files to get the expected C++/CX code so I guess others can further modify the one I am providing for their own purposes, or just do their own.

Page 2: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

CREATE THE MODEL

For this exercise I draw the model show in Figure 1. It contains 4 classes as follows:

Class Description EntityInfo Main Entity class that will represent a Person, Company, Organization,

Institution, or whatever. The class will contain the list of Names, Phone Numbers, and Urls (Emails, Web Portals, other) relevant to the entity.

There is a lot of misuse of a type (is a) qualifying an object and the worst of all is creating a class that implies a particular type such as the “Customer” or “Vendor” classes or tables in a database. Although Legacy databases are plagued with such, I strongly suggest to not model your entities in such a way. The issue is that an Entity does have a role within an organization but that role is within a context such as an event. An entity will have the role of vendor in a purchasing event still the same entity can also be the customer in different purchasing event. If you don’t recognize this you will end-up with multiple unrelated identities and duplicated and worst inconsistent records for the same entity.

The purpose of having an enumerator qualifying the “type” of entity is to indicate the “main role” of the entity within your organization but never implies that this entity can’t have other roles in other contexts.

EntityNameInfo A name use for the entity in a given context. The using an enumerator the name type is identified.

EntityPhoneNumberInfo A phone number for the entity. An enumerator indicate the type/kind of number it is.

EntityUrlInfo A relevant URL of the entity such as an email, a web address (portal, home page, other). An enumerator indicate the kind.

The above is a better and a real-world representation of a Person, Company, Customer, Organization, Institution or whatever than anything you will do using Legacy “Customer”, “Vendor”, and such database table. In the real world an entity can have or may be known by multiple names, definitively will have various Phone Number and URLs. Although many of you do constraint yourself to “relational representation” of the data and physically store things in tables with hardcoded name (again, “Customer” and such) I encourage you to review other alternatives such as (No-SQL) hierarchical models.

PREPARE AND CUSTOMIZE A T4

To start just create a new project or in an existing one select the “Add New Item” then the “ADO.NET Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will be prompted to choose a “Model Contents” so select the “Empty Model” since we want to create a new one that don’t have any source content. The empty model will display a few artifacts including:

File Description

MyModel.Designer.cs This is an “auto-generated” file and it should contain the contexts and entities that are in your model. For our Native WinRT C++/CX code generation this is useless and after running our T4 it will be empty.

MyModel.Content.tt DbContext derive (container) class to allow you access the modeled entities instances. Also useless for our purposes.

MyModel.edmx.diagram Your graphical modeling - drawing surface in where you will add your entities and from the underlying XML model document is derived. This is an XML file containing the modeled entities shapes and general graphical characteristics. You can open this file with any XML or text editor and modify the XML yourself if that excites you.

Page 3: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

MyModel.tt This is the T4 that we are interested in. To generate the classes that you will see ahead just replace the content of this file with the T4 file of your MyModel.tt with the one provided here (see the Attachment section ahead).

The next step is to create your model using the designer (diagram) surface by dragging and dropping shapes to define your design / model entities (see Figure 1).

Figure 1. The Entity Model graphic representation using EDM (VS2012).

Page 4: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

While you are adding items into your diagram you are building an XML, I mean it will end-up in an XML file, if you prefer you can use NotePad to or your favorite text editor for such task. If you edit it by hand and you like that it appears in the diagram you will need to also edit the “*.edmx.diagram” in where each shape or links between them (associations) are declared. Hand coding the XML is a great way to gain a bit more intimacy that you may need to “master” EDM.

After you are done with your model you will end up with an XML (*.edmx) for the associated schema documentation see [1]. Although you will always prefer to work with the graphical editor, just take a pick at the XML. Next, move in to code generation.

GENERATE THE CODE

Out of the box EDM will give you the *.tt default (T4) template files to generate C# or VB.Net the POCO (Plain-Old-CLR-Objects) classes based on the language you selected for your project in where you included the EDM. Yes C++/CX neither C++/CLR or any other C++ project do have any kind of Graphical or modeling tool in VS.2012 or any other VS.XXXX; ever. To jump into the C++/CX code generation just replace the content of “MyModel.tt” with the “CppTemplate.tt” provided here. After replacing the file you will note that the “*.cs” POCOs child’s of MyModel.tt (if you selected C# for your project) will suddenly be “*.h”. If those are not generated then “Run Custom Tool” to get those.

Since we have Enumerators, Classes, and associations you will end-up with a file for each Enumerator, one for the interface declaration and another for the corresponding class. I do make two passes over the EDMX model instance to be able to generate the interface first then the class, take a look at the CppTemplate.tt to get acquainted with those details.

Enumerator Classes

Base on my model and EDMX (see Figure 1) I get one of the following per each defined enumerator:

//------------------------------------------------------------------------------ // <auto-generated> // This code was generated from a template. // // Manual changes to this file may cause unexpected behavior in your application. // Manual changes to this file will be overwritten if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ // EDM C++/CX Native Code Generator. Last Update: 2013-03-16 (Eduardo Sobrino) #pragma once namespace Kix { namespace Test { namespace JsonSerializationTest { public enum class EntityType : int { Undefined = -1, Unknown = 0, Persona = 1, Corporation = 2, GovernmentAgency = 3 } ;

Page 5: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

} } }

Note that enums are not represented graphically you need to create those in the “Model Browser” or directly in the “*.edmx” as you prefer. Just left-click in top of the “Enum Types” folder to get the context menu in where you will find the “Add New Enum Type…” option.

Interfaces Classes

Again, based on my model and EDMX (see Figure 1) I get one of the following per each class (Entity Shape) represented in my model:

//------------------------------------------------------------------------------ // <auto-generated> // This code was generated from a template. // // Manual changes to this file may cause unexpected behavior in your application. // Manual changes to this file will be overwritten if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ // EDM C++/CX Native Code Generator. Last Update: 2013-03-16 (Eduardo Sobrino) #pragma once #include "CommonString.h" #include "EntityType.h" using namespace Windows::Data::Json; using namespace Platform::Collections; using namespace Windows::Foundation::Collections; namespace Kix { namespace Test { namespace JsonSerializationTest { /// <summary>Entities, Person, Companies or Organizations...</summary> public interface class IEntityInfo { property Platform::String^ Id; property Windows::Foundation::DateTime ReferenceDate; property EntityType Type; property Platform::String^ AlternateId; property IVector<EntityNameInfo^>^ EntityNameInfoes; property IVector<EntityPhoneNumberInfo^>^ EntityPhoneNumberInfoes; property IVector<EntityUrlInfo^>^ EntityUrlInfoes; void ClearFields(); void Nullify(); } ; }

Page 6: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

} }

Note that there are two (2) include statements, the first “CommonString.h” that include macros eventually used to help in the initialization of strings, the second “EntityType.h” that is a reference to the required enumerator used in the interface, in this case and as you expected the “EntityType” enum.

If you noted the two (2) method declarations (ClearFields and Nullify) are useful for the initialization of default values or just to null out all of those to their undefined state. In future posts I hope to be able to show why you really need those and in particular for the JSON serialization and deserialization (for each class) that I hope to provide through the same T4 at a later time.

The Classes

Finally, and gain, based on my model and EDMX (see Figure 1) I get one of the following per each class (Entity Shape) represented in my model:

//------------------------------------------------------------------------------ // <auto-generated> // This code was generated from a template. // // Manual changes to this file may cause unexpected behavior in your application. // Manual changes to this file will be overwritten if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ // EDM C++/CX Native Code Generator. Last Update: 2013-03-16 (Eduardo Sobrino) #pragma once #include "IEntityInfo.h" using namespace Windows::Data::Json; using namespace Platform::Collections; using namespace Windows::Foundation::Collections; namespace Kix { namespace Test { namespace JsonSerializationTest { /// <summary>Entities, Person, Companies or Organizations...</summary> public ref class EntityInfo sealed : public IEntityInfo { public: virtual property Platform::String^ Id; virtual property Windows::Foundation::DateTime ReferenceDate; virtual property EntityType Type; virtual property Platform::String^ AlternateId; virtual property IVector<EntityNameInfo^>^ EntityNameInfoes; virtual property IVector<EntityPhoneNumberInfo^>^ EntityPhoneNumberInfoes; virtual property IVector<EntityUrlInfo^>^ EntityUrlInfoes; EntityInfo() { ClearFields();

Page 7: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

} /// <summary>Set fields to default values.</summary> virtual void ClearFields() { Id = STRING_EMPTY; ReferenceDate = Kix::DateTime::Now(); Type = EntityType::Unknown; AlternateId = STRING_EMPTY; EntityNameInfoes = ref new Vector<EntityNameInfo^>(); EntityPhoneNumberInfoes = ref new Vector<EntityPhoneNumberInfo^>(); EntityUrlInfoes = ref new Vector<EntityUrlInfo^>(); } /// <summary>Set fields to their null (undefined) values.</summary> virtual void Nullify() { Id = nullptr; ReferenceDate = Kix::DateTime::NullDate(); Type = EntityType::Undefined; AlternateId = nullptr; EntityNameInfoes = nullptr; EntityPhoneNumberInfoes = nullptr; EntityUrlInfoes = nullptr; } } ; } } }

Since each class should implement their corresponding interface, then I need to include the file so the interface definition is available at compile time.

Note that I am initializing the dates using the “Kix::DateTime” helper class defined somewhere else (see previous posts). See Appendix B to get the DateTime helper code.

CONCLUSIONS

As you can see it is fairly simple to create a T4 for C++/CX that matches your coding style, or needs. Hopefully future VS releases provide better tools for those interested in or in need of Native code generation. I am not holding my breath over this.

WHAT IS NEXT?

As mentioned, I like to move on and add a couple of additional methods to the Interfaces, one for serialization and the other for deserialization, to and from a JSON string with the help of the JsonObject supported in the Windows API (see [2]).

LICENSING

Source code in this article is license to you under GNU GPL (see [3]).

REFERENCES AND LINKS

[1] EDMX Schemas (v1 through v3), http://msdn.microsoft.com/en-us/data/jj650889.aspx.

Page 8: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

[2] Json Object Support, http://msdn.microsoft.com/library/windows/apps/windows.data.json.jsonobject.aspx.

[3] GNU GPL, http://wordpress.org/about/gpl/.

Page 9: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

APPENDIX A. The T4 Template for C++/CX Code Generation.

The T4 referenced above repeatedly follows:

<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="EF.Utility.CS.ttinclude"#><#@ output extension=".h"#><# //------------------------------------------------------------------------------ const string inputFile = @"EntityBaseModel.edmx"; var textTransform = DynamicTextTransformation.Create(this); var code = new CodeGenerationTools(this); var ef = new MetadataTools(this); var typeMapper = new TypeMapper(code, ef, textTransform.Errors); var fileManager = EntityFrameworkTemplateFileManager.Create(this); var itemCollection = new EdmMetadataLoader( textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness( typeMapper.GetAllGlobalItems(itemCollection), inputFile)) { return string.Empty; } WriteHeader(codeStringGenerator, fileManager); // ----------------------------------------------------------------------------- // Write Description Definitions String fileName; String[] declarations = new String[2] {"I","H"}; ArtifactInfo artifact = new ArtifactInfo(codeStringGenerator, typeMapper); foreach (var extension in declarations) { foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) { artifact.SetType(extension); artifact.SetEntity(entity); if (artifact.IsInterface) fileName = "I" + entity.Name + artifact.FileExtension; else fileName = entity.Name + artifact.FileExtension; fileManager.StartNewFile(fileName); #> #pragma once <# var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity); var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity); var complexProperties = typeMapper.GetComplexProperties(entity); var simpleProperties = typeMapper.GetSimpleProperties(entity); if (artifact.IsInterface)

Page 10: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

{ #> #include "CommonString.h" <# } else { #> #include "<#="I"+entity.Name#>.h" <# } if (simpleProperties.Any() && artifact.IsInterface) { foreach (var edmProperty in simpleProperties) { if (edmProperty.TypeUsage.EdmType.BuiltInTypeKind == System.Data.Metadata.Edm.BuiltInTypeKind.EnumType) { #> #include "<#=edmProperty.TypeUsage.EdmType.Name#>.h" <# } } } #> <#=artifact.UsingDirectives(inHeader: false)#> <# BeginNamespace(code); #> /// <summary><#=artifact.EntitySummary#></summary> <#=codeStringGenerator.EntityClassOpening(artifact)#> { <# if (!artifact.IsInterface) { #> public: <# } #> <# if (simpleProperties.Any()) { foreach (var edmProperty in simpleProperties) { #> <#=codeStringGenerator.NativeProperty(edmProperty, artifact.IsInterface)#> <# } } if (complexProperties.Any()) {

Page 11: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

#> <# foreach(var complexProperty in complexProperties) { #> <#=codeStringGenerator.NativeProperty(complexProperty, artifact.IsInterface)#> <# } } var navigationProperties = typeMapper.GetNavigationProperties(entity); if (navigationProperties.Any()) { #> <# foreach (var navigationProperty in navigationProperties) { #> <#=codeStringGenerator.NativeNavigationProperty(navigationProperty, artifact.IsInterface)#> <# } } if (artifact.IsInterface) { #> void ClearFields(); void Nullify(); JsonObject ^ToJsonObject(JsonObject ^jsonObject); void FromJsonString(Platform::String ^jsonString); <# goto finishDeclaration; } #> <# if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any() || simpleProperties.Any()) { #> <#=code.Escape(entity)#>() { ClearFields(); } /// <summary>Free allocated Resources.</summary> virtual void ClearFields() { <# foreach (var edmProperty in simpleProperties) // propertiesWithDefaultValues) { #>

Page 12: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

<#=ArtifactInfo.GetClearFieldAssignment(edmProperty,false)#>; <# } foreach (var navigationProperty in collectionNavigationProperties) { #> <#=code.Escape(navigationProperty)#> = ref new Vector<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>^>(); <# } foreach (var complexProperty in complexProperties) { #> <#=code.Escape(complexProperty)#> = ref new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); <# } #> } /// <summary>Set fields to default values.</summary> virtual void ClearFields() { <# foreach (var edmProperty in simpleProperties) // propertiesWithDefaultValues) { #> <#=ArtifactInfo.GetClearFieldAssignment(edmProperty,false)#>; <# } foreach (var navigationProperty in collectionNavigationProperties) { #> <#=code.Escape(navigationProperty)#> = ref new Vector<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>^>(); <# } foreach (var complexProperty in complexProperties) { #> <#=code.Escape(complexProperty)#> = ref new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); <# } #> } /// <summary>Set fields to their null (undefined) values.</summary> virtual void Nullify() { <# foreach (var edmProperty in simpleProperties) // propertiesWithDefaultValues) { #>

Page 13: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

<#=ArtifactInfo.GetClearFieldAssignment(edmProperty,true)#>; <# } foreach (var navigationProperty in collectionNavigationProperties) { #> <#=code.Escape(navigationProperty)#> = nullptr; <# } foreach (var complexProperty in complexProperties) { #> this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); <# } #> } virtual JsonObject ^ToJsonObject(JsonObject ^jsonObject); virtual void FromJsonString(Platform::String ^jsonString); <# } finishDeclaration: #> } ; <# EndNamespace(code); } } // end of "Write Description Definitions" // ----------------------------------------------------------------------------- // Generate Complex Types Declarations... foreach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection)) { fileManager.StartNewFile(complex.Name + ".cs"); BeginNamespace(code); #> <#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> <#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> { <# var complexProperties = typeMapper.GetComplexProperties(complex); var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex); if (propertiesWithDefaultValues.Any() || complexProperties.Any()) { #> public <#=code.Escape(complex)#>() { <# foreach (var edmProperty in propertiesWithDefaultValues)

Page 14: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

{ #> this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; <# } foreach (var complexProperty in complexProperties) { #> this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); <# } #> } <# } var simpleProperties = typeMapper.GetSimpleProperties(complex); if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> <#=codeStringGenerator.Property(edmProperty)#> <# } } if (complexProperties.Any()) { #> <# foreach(var edmProperty in complexProperties) { #> <#=codeStringGenerator.Property(edmProperty)#> <# } } #> } <# EndNamespace(code); } // ----------------------------------------------------------------------------- // Generate Enumerator Declarations... foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection)) { fileManager.StartNewFile(enumType.Name + ".h"); #> #pragma once

Page 15: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

<# BeginNamespace(code); #> <#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> <# if (typeMapper.EnumIsFlags(enumType)) { #> [Flags] <# } #> <#=codeStringGenerator.EnumOpening(enumType)#> { <# var foundOne = false; foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType)) { foundOne = true; #> <#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>, <# } if (foundOne) { this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1); } #> } ; <# EndNamespace(code); } fileManager.Process(); #> <#+ // ----------------------------------------------------------------------------- public enum ArtifactType { Interface = 1, IncludeFile = 2, Class = 3 } public class ArtifactTypeInfo { private EdmProperty m_Property; private String m_ValueText; private String m_NullValueText; private String m_AssignmentValueText; private String m_FieldName;

Page 16: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

private Boolean m_IsDateTime; private Boolean m_IsNumeric; private Boolean m_IsInteger; private Boolean m_IsDecimal; private Boolean m_IsBinary; private Boolean m_IsBoolean; private Boolean m_IsEnum; public ArtifactTypeInfo(EdmProperty property, Boolean nullify) { m_Property = property; String value = null; String nullValue = null; m_IsDateTime = false; m_IsNumeric = false; m_IsInteger = false; m_IsDecimal = false; m_IsBinary = false; m_IsBoolean = false; m_IsEnum = false; if (property.TypeUsage.EdmType.Name == "String") { value = "STRING_EMPTY"; nullValue = "nullptr"; } else if (property.TypeUsage.EdmType.Name == "Int16") m_IsInteger = m_IsNumeric = true; else if (property.TypeUsage.EdmType.Name == "Int32") m_IsInteger = m_IsNumeric = true; else if (property.TypeUsage.EdmType.Name == "Int64") m_IsInteger = m_IsNumeric = true; else if (property.TypeUsage.EdmType.Name == "Double") m_IsDecimal = m_IsNumeric = true; else if (property.TypeUsage.EdmType.Name == "Decimal") m_IsDecimal = m_IsNumeric = true; else if (property.TypeUsage.EdmType.Name == "DateTime") { value = "Kix::DateTime::Now()"; nullValue = "Kix::DateTime::NullDate()"; m_IsDateTime = true; } else if (property.TypeUsage.EdmType.Name == "Binary") { value = "nullptr"; nullValue = "nullptr"; m_IsBinary = true; } else

Page 17: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

if (property.TypeUsage.EdmType.Name == "Boolean") { value = "false"; nullValue = "false"; m_IsBoolean = true; } else if (property.TypeUsage.EdmType.BuiltInTypeKind == System.Data.Metadata.Edm.BuiltInTypeKind.EnumType) { value = property.TypeUsage.EdmType.Name + "::Unknown"; nullValue = property.TypeUsage.EdmType.Name + "::Undefined"; } else { value = "nullptr"; nullValue = "nullptr"; } if (m_IsNumeric) { value = (m_IsInteger) ? "0" : "0.0"; nullValue = value; } if (value == null) value = "STRING_EMPTY"; if (nullValue == null) nullValue = "STRING_EMPTY"; m_FieldName = property.Name; m_AssignmentValueText = nullify ? nullValue : (value != null) ? value : "STRING_EMPTY"; if (!nullify && (property.DefaultValue != null)) m_AssignmentValueText = property.DefaultValue.ToString(); m_ValueText = value; m_NullValueText = nullValue; } public String AsAssignment() { return m_FieldName + " = " + m_AssignmentValueText; } } public class ArtifactInfo { private EntityType m_Entity; static private TypeMapper m_TypeMapper; private CodeStringGenerator m_CodeGenerator; private String m_FileExtension; private ArtifactType m_Type; public IEnumerable< System.Data.Metadata.Edm.EdmProperty> m_PropertiesWithDefaultValues; private IEnumerable<System.Data.Metadata.Edm.NavigationProperty>

Page 18: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

m_CollectionNavigationProperties; private IEnumerable< System.Data.Metadata.Edm.EdmProperty> m_ComplexProperties; //if (propertiesWithDefaultValues.Any() || // collectionNavigationProperties.Any() || complexProperties.Any()) public String FileExtension { get { return m_FileExtension; } } public ArtifactType Type { get { return m_Type; } } public EntityType Entity { get { return m_Entity; } } public Boolean IsInterface { get { return m_Type == ArtifactType.Interface; } } public Boolean IsClass { get { return m_Type == ArtifactType.Class; } } public String EntitySummary { get { if (m_Entity == null) return "TODO: Include some Documentation please!!"; if (m_Entity.Documentation == null) return "TODO: Include a Summary here please!!"; if (m_Entity.Documentation.Summary == null) return "TODO: Include a Summary Text here please!!"; return m_Entity.Documentation.Summary; } } public ArtifactInfo(CodeStringGenerator codeGenerator, TypeMapper mapper) { m_CodeGenerator = codeGenerator; m_TypeMapper = mapper; } public void SetEntity(EntityType entity) { m_Entity = entity; } public void SetType(String artifactName) {

Page 19: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

if (artifactName == "I") m_Type = ArtifactType.Interface; else if (artifactName == "H") m_Type = ArtifactType.IncludeFile; else if (artifactName == "C") m_Type = ArtifactType.Class; else m_Type = ArtifactType.IncludeFile; switch(m_Type) { case ArtifactType.Class: m_FileExtension = ".cpp"; break; case ArtifactType.Interface: m_FileExtension = ".h"; break; default: m_FileExtension = ".h"; break; } } public string UsingDirectives(bool inHeader, bool includeCollections = true) { return m_CodeGenerator.UsingDirectives(inHeader,includeCollections); } public static string GetNativeTypeName(string typeName) { if (typeName == "String") return "Platform::String^"; if (typeName == "Boolean") return "Platform::Boolean"; if (typeName == "DateTime") return "Windows::Foundation::DateTime"; return typeName; } public static string GetTypeName(EdmProperty property) { string tname = GetNativeTypeName(property.TypeUsage.EdmType.Name); if (property.Nullable) tname += "^"; return tname; } public static string GetClearFieldAssignment(EdmProperty property, Boolean nullify) { ArtifactTypeInfo t = new ArtifactTypeInfo(property, nullify); return t.AsAssignment(); } } public class NamespaceList {

Page 20: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

private String [] m_Namespaces; public Int32 Count { get { return m_Namespaces.Length; } } public String this[Int32 index] { get { return m_Namespaces[index]; } } public NamespaceList(String namespaceList) { if (namespaceList == null) namespaceList = String.Empty; m_Namespaces = namespaceList.Split('.'); } } // ----------------------------------------------------------------------------- public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager) { fileManager.StartHeader(); #> //------------------------------------------------------------------------------ // <auto-generated> // <#=GetResourceString("Template_GeneratedCodeCommentLine1")#> // // <#=GetResourceString("Template_GeneratedCodeCommentLine2")#> // <#=GetResourceString("Template_GeneratedCodeCommentLine3")#> // </auto-generated> //------------------------------------------------------------------------------ // EDM C++/CX Native Code Generator. Last Update: 2013-03-16 (Eduardo Sobrino) <#=codeStringGenerator.UsingDirectives(inHeader: true)#> <#+ fileManager.EndBlock(); } public void BeginNamespace(CodeGenerationTools code) { var codeNamespace = code.VsNamespaceSuggestion(); if (!String.IsNullOrEmpty(codeNamespace)) { NamespaceList l = new NamespaceList(codeNamespace); for(Int32 i=0; i<l.Count; i++) { #> namespace <#=code.EscapeNamespace(l[i])#> { <#+ } #> <#+ PushIndent(" "); } }

Page 21: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

public void EndNamespace(CodeGenerationTools code) { var codeNamespace = code.VsNamespaceSuggestion(); if (!String.IsNullOrEmpty(codeNamespace)) { PopIndent(); NamespaceList l = new NamespaceList(codeNamespace); if (l.Count > 0) { #> <#+ } for(Int32 i=0; i<l.Count; i++) { #> } <#+ } } } public const string TemplateId = "CSharp_DbContext_Types_EF5"; public class CodeStringGenerator { private readonly CodeGenerationTools _code; private readonly TypeMapper _typeMapper; private readonly MetadataTools _ef; public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) { ArgumentNotNull(code, "code"); ArgumentNotNull(typeMapper, "typeMapper"); ArgumentNotNull(ef, "ef"); _code = code; _typeMapper = typeMapper; _ef = ef; } public string NativeProperty(EdmProperty edmProperty, Boolean isInterface) { String formatText = "property {0} {1};"; if (!isInterface) formatText = "virtual " + formatText; return string.Format( CultureInfo.InvariantCulture, formatText, ArtifactInfo.GetTypeName(edmProperty), _code.Escape(edmProperty)); } public string Property(EdmProperty edmProperty) { return string.Format( CultureInfo.InvariantCulture,

Page 22: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

"{0} {1} {{ {2}get; {3}set; }}", _typeMapper.GetTypeName(edmProperty.TypeUsage), _code.Escape(edmProperty), _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); } public string NativeNavigationProperty( NavigationProperty navigationProperty, Boolean isInterface) { String formatText = "property {0}^ {1};"; if (!isInterface) formatText = "virtual " + formatText; EntityType etype = navigationProperty.ToEndMember.GetEntityType(); var endType = _typeMapper.GetTypeName(etype); return string.Format( CultureInfo.InvariantCulture, formatText, navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("IVector<" + endType + "^>") : endType, _code.Escape(navigationProperty)); } public string NavigationProperty(NavigationProperty navigationProperty) { EntityType etype = navigationProperty.ToEndMember.GetEntityType(); var endType = _typeMapper.GetTypeName(etype); return string.Format( CultureInfo.InvariantCulture, "{0} {1} {2} {{ {3}get; {4}set; }}", AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)), navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, _code.Escape(navigationProperty), _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)), _code.SpaceAfter(Accessibility.ForSetter(navigationProperty))); } public string AccessibilityAndVirtual(string accessibility) { return accessibility + (accessibility != "private" ? " virtual" : ""); } public string EntityClassOpening(EntityType entity) { return string.Format( CultureInfo.InvariantCulture, "{0} {1}partial class {2}{3}", Accessibility.ForType(entity), _code.SpaceAfter(_code.AbstractOption(entity)), _code.Escape(entity), _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); }

Page 23: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

public string EntityClassOpening(ArtifactInfo artifact) { if (artifact.IsInterface) { return string.Format( CultureInfo.InvariantCulture, "{0} {1}interface class I{2}", Accessibility.ForType(artifact.Entity), _code.SpaceAfter(_code.AbstractOption(artifact.Entity)), _code.Escape(artifact.Entity)); } String entityName = _code.Escape(artifact.Entity); return string.Format( CultureInfo.InvariantCulture, "{0} {1}ref class {2} sealed{3}", Accessibility.ForType(artifact.Entity), _code.SpaceAfter(_code.AbstractOption(artifact.Entity)), entityName, _code.StringBefore(" : public I", entityName)); } public string EnumOpening(SimpleType enumType) { return string.Format( CultureInfo.InvariantCulture, "{0} enum class {1} : {2}", Accessibility.ForType(enumType), _code.Escape(enumType), _code.Escape(_typeMapper.UnderlyingClrType(enumType))); } public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter) { var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) { var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null"; var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")"; var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + parameter.RawClrTypeName + "))"; writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); } } public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) { var parameters = _typeMapper.GetParameters(edmFunction); return string.Format( CultureInfo.InvariantCulture, "{0} IQueryable<{1}> {2}({3})",

Page 24: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), _code.Escape(edmFunction), string.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray())); } public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) { var parameters = _typeMapper.GetParameters(edmFunction); return string.Format( CultureInfo.InvariantCulture, "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});", _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), edmFunction.NamespaceName, edmFunction.Name, string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); } public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) { var parameters = _typeMapper.GetParameters(edmFunction); var returnType = _typeMapper.GetReturnType(edmFunction); var paramList = String.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray()); if (includeMergeOption) { paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; } return string.Format( CultureInfo.InvariantCulture, "{0} {1} {2}({3})", AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", _code.Escape(edmFunction), paramList); } public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) { var parameters = _typeMapper.GetParameters(edmFunction); var returnType = _typeMapper.GetReturnType(edmFunction); var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); if (includeMergeOption)

Page 25: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

{ callParams = ", mergeOption" + callParams; } return string.Format( CultureInfo.InvariantCulture, "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});", returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", edmFunction.Name, callParams); } public string DbSet(EntitySet entitySet) { return string.Format( CultureInfo.InvariantCulture, "{0} DbSet<{1}> {2} {{ get; set; }}", Accessibility.ForReadOnlyProperty(entitySet), _typeMapper.GetTypeName(entitySet.ElementType), _code.Escape(entitySet)); } public string UsingDirectives(bool inHeader, bool includeCollections = true) { return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) ? string.Format( CultureInfo.InvariantCulture, "{0}{1}", inHeader ? Environment.NewLine : "", includeCollections ? ( "using namespace Windows::Data::Json;\r\n" + "using namespace Platform::Collections;\r\n" + "using namespace Windows::Foundation::Collections;\r\n") : "", inHeader ? "" : Environment.NewLine) : ""; } } public class TypeMapper { private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; private readonly System.Collections.IList _errors; private readonly CodeGenerationTools _code; private readonly MetadataTools _ef; public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) { ArgumentNotNull(code, "code"); ArgumentNotNull(ef, "ef"); ArgumentNotNull(errors, "errors"); _code = code; _ef = ef;

Page 26: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

_errors = errors; } public string GetTypeName(TypeUsage typeUsage) { return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); } public string GetTypeName(EdmType edmType) { return GetTypeName(edmType, isNullable: null, modelNamespace: null); } public string GetTypeName(TypeUsage typeUsage, string modelNamespace) { return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); } public string GetTypeName(EdmType edmType, string modelNamespace) { return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); } public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) { if (edmType == null) { return null; } var collectionType = edmType as CollectionType; if (collectionType != null) { return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace)); } var typeName = _code.Escape(edmType.MetadataProperties .Where(p => p.Name == ExternalTypeNameAttributeName) .Select(p => (string)p.Value) .FirstOrDefault()) ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ? _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) : _code.Escape(edmType)); if (edmType is StructuralType) { return typeName; } if (edmType is SimpleType) { var clrType = UnderlyingClrType(edmType); if (!IsEnumType(edmType)) {

Page 27: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

typeName = _code.Escape(clrType); } return clrType.IsValueType && isNullable == true ? String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) : typeName; } throw new ArgumentException("edmType"); } public Type UnderlyingClrType(EdmType edmType) { ArgumentNotNull(edmType, "edmType"); var primitiveType = edmType as PrimitiveType; if (primitiveType != null) { return primitiveType.ClrEquivalentType; } if (IsEnumType(edmType)) { return GetEnumUnderlyingType(edmType).ClrEquivalentType; } return typeof(object); } public object GetEnumMemberValue(MetadataItem enumMember) { ArgumentNotNull(enumMember, "enumMember"); var valueProperty = enumMember.GetType().GetProperty("Value"); return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); } public string GetEnumMemberName(MetadataItem enumMember) { ArgumentNotNull(enumMember, "enumMember"); var nameProperty = enumMember.GetType().GetProperty("Name"); return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null); } public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) { ArgumentNotNull(enumType, "enumType"); var membersProperty = enumType.GetType().GetProperty("Members"); return membersProperty != null ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null) : Enumerable.Empty<MetadataItem>(); } public bool EnumIsFlags(EdmType enumType) {

Page 28: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

ArgumentNotNull(enumType, "enumType"); var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null); } public bool IsEnumType(GlobalItem edmType) { ArgumentNotNull(edmType, "edmType"); return edmType.GetType().Name == "EnumType"; } public PrimitiveType GetEnumUnderlyingType(EdmType enumType) { ArgumentNotNull(enumType, "enumType"); return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); } public string CreateLiteral(object value) { if (value == null || value.GetType() != typeof(TimeSpan)) { return _code.CreateLiteral(value); } return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks); } public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile) { ArgumentNotNull(types, "types"); ArgumentNotNull(sourceFile, "sourceFile"); var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase); if (types.Any(item => !hash.Add(item))) { _errors.Add( new CompilerError(sourceFile, -1, -1, "6023", String.Format(CultureInfo.CurrentCulture, GetResourceString("Template_CaseInsensitiveTypeConflict")))); return false; } return true; } public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection) { return GetItemsToGenerate<SimpleType>(itemCollection) .Where(e => IsEnumType(e)); }

Page 29: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType { return itemCollection .OfType<T>() .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) .OrderBy(i => i.Name); } public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection) { return itemCollection .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) .Select(g => GetGlobalItemName(g)); } public string GetGlobalItemName(GlobalItem item) { if (item is EdmType) { return ((EdmType)item).Name; } else { return ((EntityContainer)item).Name; } } public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); } public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); } public IEnumerable<EdmProperty> GetComplexProperties(EntityType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); } public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); } public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);

Page 30: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

} public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); } public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type) { return type.NavigationProperties.Where(np => np.DeclaringType == type); } public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type) { return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); } public FunctionParameter GetReturnParameter(EdmFunction edmFunction) { ArgumentNotNull(edmFunction, "edmFunction"); var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); return returnParamsProperty == null ? edmFunction.ReturnParameter : ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); } public bool IsComposable(EdmFunction edmFunction) { ArgumentNotNull(edmFunction, "edmFunction"); var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null); } public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction) { return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); } public TypeUsage GetReturnType(EdmFunction edmFunction) { var returnParam = GetReturnParameter(edmFunction); return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); } public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) { var returnType = GetReturnType(edmFunction); return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType;

Page 31: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

} } public class EdmMetadataLoader { private readonly IDynamicHost _host; private readonly System.Collections.IList _errors; public EdmMetadataLoader(IDynamicHost host, System.Collections.IList errors) { ArgumentNotNull(host, "host"); ArgumentNotNull(errors, "errors"); _host = host; _errors = errors; } public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath) { ArgumentNotNull(sourcePath, "sourcePath"); if (!ValidateInputPath(sourcePath)) { return new EdmItemCollection(); } var schemaElement = LoadRootElement(_host.ResolvePath(sourcePath)); if (schemaElement != null) { using (var reader = schemaElement.CreateReader()) { IList<EdmSchemaError> errors; var itemCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(new[] { reader }, out errors); ProcessErrors(errors, sourcePath); return itemCollection; } } return new EdmItemCollection(); } public string GetModelNamespace(string sourcePath) { ArgumentNotNull(sourcePath, "sourcePath"); if (!ValidateInputPath(sourcePath)) { return string.Empty; } var model = LoadRootElement(_host.ResolvePath(sourcePath)); if (model == null) { return string.Empty; }

Page 32: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

var attribute = model.Attribute("Namespace"); return attribute != null ? attribute.Value : ""; } private bool ValidateInputPath(string sourcePath) { if (sourcePath == "$" + "edmxInputFile" + "$") { _errors.Add( new CompilerError(_host.TemplateFile ?? sourcePath, 0, 0, string.Empty, GetResourceString("Template_ReplaceVsItemTemplateToken"))); return false; } return true; } public XElement LoadRootElement(string sourcePath) { ArgumentNotNull(sourcePath, "sourcePath"); var root = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); return root.Elements() .Where(e => e.Name.LocalName == "Runtime") .Elements() .Where(e => e.Name.LocalName == "ConceptualModels") .Elements() .Where(e => e.Name.LocalName == "Schema") .FirstOrDefault() ?? root; } private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath) { foreach (var error in errors) { _errors.Add( new CompilerError( error.SchemaLocation ?? sourceFilePath, error.Line, error.Column, error.ErrorCode.ToString(CultureInfo.InvariantCulture), error.Message) { IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning }); } } public bool IsLazyLoadingEnabled(EntityContainer container) { string lazyLoadingAttributeValue; var lazyLoadingAttributeName = MetadataConstants.EDM_ANNOTATION_09_02 + ":LazyLoadingEnabled"; bool isLazyLoading; return !MetadataTools.TryGetStringMetadataPropertySetting(container, lazyLoadingAttributeName, out lazyLoadingAttributeValue)

Page 33: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

|| !bool.TryParse(lazyLoadingAttributeValue, out isLazyLoading) || isLazyLoading; } } public static void ArgumentNotNull<T>(T arg, string name) where T : class { if (arg == null) { throw new ArgumentNullException(name); } } private static readonly Lazy<System.Resources.ResourceManager> ResourceManager = new Lazy<System.Resources.ResourceManager>( () => new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(MetadataItemCollectionFactory).Assembly), isThreadSafe: true); public static string GetResourceString(string resourceName) { ArgumentNotNull(resourceName, "resourceName"); return ResourceManager.Value.GetString(resourceName, null); } #>

Page 34: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

APPENDIX B. The DateTime Helper.

The include file for the DateTime helper follows:

// ----------------------------------------------------------------------------- // File : DateTimeHelper.h // Date : 2013-01-26 (ESob) // Copyright : Open Knowledge (c) 2013 #pragma once using namespace Windows::Globalization::DateTimeFormatting; // ----------------------------------------------------------------------------- namespace Kix { #define DATETIME_SHORTDATE L"{year.full}-{month.integer(2)}-{day.integer(2)}" #define DATETIME_SHORTTIME \ L"{hour.integer(2)}:{minute.integer(2)}:{second.integer(2)}" // TODO: yes review the ISO-8601 parsing issue... #define DATETIME_ISO8601 \ L"{year.full}-{month.integer(2)}-{day.integer(2)} " \ L"{hour.integer(2)}:{minute.integer(2)}:{second.integer(2)}" public enum class DateTimeValueType { Unknown = 0, GoodDateTime = 1, BadDateTime = 2 } ; /// <summary>DateTime Helper</summary> public ref class DateTime sealed { private: DateTimeValueType m_Type; Windows::Foundation::DateTime m_Value; DateTime ^ParseText(Platform::String ^dateText); public: property Windows::Foundation::DateTime Value { Windows::Foundation::DateTime get() { return m_Value; } } property DateTimeValueType Type { DateTimeValueType get() { return m_Type; } } property Platform::Boolean IsValid { Platform::Boolean get() { return m_Type == DateTimeValueType::GoodDateTime;

Page 35: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

} } DateTime() { m_Type = DateTimeValueType::Unknown; } DateTime(Platform::String ^dateText) { m_Type = DateTimeValueType::Unknown; ParseText(dateText); } static inline Windows::Foundation::DateTime Now() { Windows::Globalization::Calendar^ cal = ref new Windows::Globalization::Calendar(); Windows::Foundation::DateTime date = cal->GetDateTime(); delete cal; return date; } static inline Windows::Foundation::DateTime NullDate() { Windows::Foundation::DateTime date; date.UniversalTime = 0; return date; } static inline Platform::String ^ToShortDate( Windows::Foundation::DateTime dateTime) { auto frmt = ref new DateTimeFormatter(DATETIME_SHORTDATE); return frmt->Format(dateTime); } static inline Platform::String ^ToShortTime( Windows::Foundation::DateTime dateTime) { auto frmt = ref new DateTimeFormatter(DATETIME_SHORTTIME); return frmt->Format(dateTime); } /// <summary>Format given DateTime into an ISO-8601 dateTime String /// </summary> /// <param name="dateTime">date-time to format</param> /// <returns>formated date-time string is returned</returns> static inline Platform::String ^ToString( Windows::Foundation::DateTime dateTime) { auto frmt = ref new DateTimeFormatter(DATETIME_ISO8601); return frmt->Format(dateTime); }

Page 36: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

inline Platform::String ^ToString() { return ToString(m_Value); } static inline DateTime ^Parse(Platform::String ^dateText) { DateTime ^dt = ref new DateTime(dateText); return dt; } } ; // ----------------------------------------------------------------------------- }

The corresponding cpp file follows:

// ----------------------------------------------------------------------------- // File : DateTimeHelper.cpp // Date : 2013-02-09 (E. Sobrino) // Copyright : Open Knowledge (c) 2013 #include "pch.h" #include "DateTimeHelper.h" using namespace Platform; #include "windows.h" #include "stdio.h" #include "ATLComTime.h" // ----------------------------------------------------------------------------- /// <summary>Parse given DateTime in text format and return a DateTime /// stating that the parsing result is good or bad. Let the app decide what to /// do with the bad day.</summary> /// <param name="dateText">date time in text format</param> /// <examples> /// Kix::DateTime::Parse(L"02-12-2013 12:11:10"); /// Kix::DateTime::Parse(L"03-14-2015"); /// Kix::DateTime::Parse(L"02/12/2013 12:11:10"); /// Kix::DateTime::Parse(L"03/14/2015"); /// Kix::DateTime::Parse(L"2015-02-13 13:14:15"); /// </exmaples> /// <issues> /// - Issues related to "Daylight Saving Time..." are not solved here yet /// (2013-02-09) /// </issues> /// <returns>instance of DateTime is returned always check the "IsValid" /// property to make sure the parsing was ok...</returns> Kix::DateTime ^Kix::DateTime::ParseText(Platform::String ^dateText) { String^ currentLanguage = Windows::Globalization::ApplicationLanguages::Languages->GetAt(0); LCID lcid = LocaleNameToLCID( currentLanguage->Data(), LOCALE_ALLOW_NEUTRAL_NAMES);

Page 37: Native WinRT EDM T4 C++/CX Auto Generated Code...Mar 20, 2013  · Entity Data Model” (EDM) option. For this exercise I will assume you call your model “MyModel.edmx”. You will

const wchar_t *dText = dateText->Data(); COleDateTime dtime; bool isGoodDateTime = dtime.ParseDateTime(dText, 0, lcid); if (!isGoodDateTime) { m_Type = DateTimeValueType::BadDateTime; return this; } SYSTEMTIME sysTime; dtime.GetAsSystemTime(sysTime); SYSTEMTIME utcTime; TzSpecificLocalTimeToSystemTime(nullptr, &sysTime, &utcTime); FILETIME fileTime; SystemTimeToFileTime(&utcTime, &fileTime); _ULARGE_INTEGER ulint = {fileTime.dwLowDateTime, fileTime.dwHighDateTime}; m_Value.UniversalTime = ulint.QuadPart; m_Type = DateTimeValueType::GoodDateTime; return this; }