I'm using Dotfuscator CE with Visual Studio 2015 Update 3 to obfuscate my .Net assemblies.
We know that Public types and members are not be obfuscated by default.
I'm curious to know how can we add Friend Classes in Exclusion list so that those should not be obfuscated?
Here is the config file file I'm using to obfuscate my DLL.
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE dotfuscator SYSTEM "http://www.preemptive.com/dotfuscator/dtd/dotfuscator_v2.3.dtd">
<dotfuscator version="2.3">
<propertylist>
<property name="SourceDirectory" value="This Path Will Be Replaced By Visual Studio" />
<property name="SourceFile" value="This Filename Will Be Replaced By Visual Studio" />
</propertylist>
<global>
<option>quiet</option>
</global>
<input>
<asmlist>
<inputassembly refid="e4ca1ab5-26cb-4ab7-9621-87063f75a38f">
<option>honoroas</option>
<option>stripoa</option>
<option>library</option>
<option>transformxaml</option>
<file dir="${SourceDirectory}" name="${SourceFile}" />
</inputassembly>
</asmlist>
</input>
<output>
<file dir="${SourceDirectory}" />
</output>
<renaming>
<option>xmlserialization</option>
<mapping>
<mapoutput overwrite="true">
<file dir="${SourceDirectory}\Dotfuscated" name="Map.xml" />
</mapoutput>
</mapping>
<referencerulelist>
<referencerule rulekey="{6655B10A-FD58-462d-8D4F-5B1316DFF0FF}" />
<referencerule rulekey="{7D9C8B02-2383-420f-8740-A9760394C2C1}" />
<referencerule rulekey="{229FD6F8-5BCC-427b-8F72-A7A413ECDF1A}" />
<referencerule rulekey="{2B7E7C8C-A39A-4db8-9DFC-6AFD38509061}" />
<referencerule rulekey="{494EA3BA-B947-44B5-BEE8-A11CC85AAF9B}" />
<referencerule rulekey="{89769974-93E9-4e71-8D92-BE70E855ACFC}" />
<referencerule rulekey="{4D81E604-A545-4631-8B6D-C3735F793F80}" />
</referencerulelist>
</renaming>
<sos mergeruntime="true">
<option>version:v4</option>
<option>sendanalytics</option>
<option>dontsendtamper</option>
</sos>
<smartobfuscation>
<smartobfuscationreport verbosity="all" overwrite="false" />
</smartobfuscation>
</dotfuscator>
Actually I've a Model class with Friend access specifier. I post its object via PostAsJsonAsync method e.g.
Dim result As HttpResponseMessage = client.PostAsJsonAsync(APIEndPoints.LOGIN, _LoginModel).Result
Here is the Friend Class:
Friend Class LoginModel
Public AccessKey As String
Public Password As String
End Class
API method that receives the request and model:
[HttpPost]
[Route("authenticate")]
public async Task<JsonResult> Authenticate([FromBody] LoginViewModel lvm)
// Here lvm.Accesskey is null
When API receives the request and LoginModel too, its fields are null. If I make my LoginModel public then it works.
Note: this only happens when I obfuscate my DLL, otherwise the implementation works with Friend class too.
Also note: Friend classes are common in VB.Net. They works like public classes when accessed within an assembly but they are private outside the assembly.
Based on your clarification, it sounds like you want to exclude not only the names of Friend types, but also the names of Public fields within those types. I had interpreted your original question as wanting to exclude anything marked Friend, no matter the context.
An important point here is that, in terms of Dotfuscator's rules, excluding a type does not automatically exclude its members.
Here's an exclusion rule set that excludes top-level Friend types and Public and Friend fields of those types:
<excludelist>
<type name=".*" regex="true" speclist="+notpublic">
<comment>Exclude top-level types that are only accessible to the assembly ("Friend" in VB, "internal" in C#, or "private" in IL).</comment>
<field name=".*" speclist="+public" regex="true">
<comment>Exclude public fields of types the parent rule matches</comment>
</field>
</type>
</excludelist>
You can also just exclude types and members you know will cause trouble when renamed, rather than excluding a large number of names using rules based on accessibility. Here's an example, assuming LoginModel is defined in assembly YourAssembly and namespace YourNamespace.Here:
<excludelist>
<type name="YourAssembly.YourNamespace.Here.LoginModel">
<field name="AccessKey" signature="string" />
<field name="Password" signature="string" />
</type>
</excludelist>
(I noticed you're using this same configuration for multiple input assemblies, but this rule is still safe because if the input assembly doesn't contain the specified type, then the rule will be ignored.)
For reference, the Professional Edition documentation on Exclusion Rules (and sub-topics of that page) might be useful - Community Edition and Professional Edition share the same configuration file format, for features that are supported by both editions.
Disclosure: I work on the Dotfuscator team for PreEmptive Solutions.
If you are trying to exclude your input assembly's Friend types and members because your assembly has a Friend Assembly, then be aware that Dotfuscator will automatically exclude such code elements from renaming (the only kind of obfuscation provided by Dotfuscator CE) and will issue the following warning:
WARNING: NameOfYourInputAsssembly has non-input Friend Assemblies and is in Library Mode; internal members will not be renamed or pruned. Consider adding Friend Assemblies as input for increased obfuscation.
(The term "internal" here is the C# equivalent of VB's "Friend" keyword).
As the warning suggests, you can instead include the Friend Assembly as another Input to Dotfuscator.
If you do so, Dotfuscator can then rename the Friend types and members, and update the Friend Assembly to refer to those types and members by the new names.
If you still would like to exclude Friend types and members, you can do so with the following renaming exclusion rule set, added as a child of the <renaming> tag in the config file:
<excludelist>
<type name=".*" regex="true" speclist="+notpublic">
<comment>Exclude types that are only accessible to the assembly ("Friend" in VB, "internal" in C#, or "private" in IL).</comment>
</type>
<type name=".*" regex="true" speclist="+nestedassembly">
<comment>Exclude nested types that are only accessible to the assembly ("Friend" in VB, "internal" in C#, or "private" in IL).</comment>
</type>
<type name=".*" regex="true" excludetype="false">
<comment>Select, but do not exclude, all types.</comment>
<method name=".*" speclist="+assembly" regex="true">
<comment>Exclude methods that are only accessible to the assembly ("Friend" in VB, "internal" in C#, or "assembly" in IL).</comment>
</method>
<field name=".*" speclist="+assembly" regex="true">
<comment>Exclude fields that are only accessible to the assembly ("Friend" in VB, "internal" in C#, or "assembly" in IL).</comment>
</field>
<propertymember name=".*" speclist="+assembly" regex="true">
<comment>Exclude properties that are only accessible to the assembly ("Friend" in VB, "internal" in C#, or "assembly" in IL).</comment>
</propertymember>
<eventmember name=".*" speclist="+assembly" regex="true">
<comment>Exclude events that are only accessible to the assembly ("Friend" in VB, "internal" in C#, or "assembly" in IL).</comment>
</eventmember>
</type>
</excludelist>
Edit: I had missed nested types in the previous revision of this answer.
Disclosure: I work on the Dotfuscator team for PreEmptive Solutions.
Related
I need your help.
I read whole internet about Registration-Free COM/DLLs but my problem is more complex.
I'm preparing an application in VB.NET which will be used in an environment in which users don't have admin rights, so I can't simpy install it or register COM. This COM is a LogParser library designed by microsoft.
DLL also doesn't have to be embeded - would be nice, but it may be also extracted from exe during startup - i'm ok with this approach
Generally in a main form i've got a button which invokes another form by:
LogParser_Form.Show()
This another Form 'Imports MSUtil', which is a Interop.MSUtil.dll and which is embeeded to exe by Fody Costura add-on.
Form contains also a class which has multiple declarations of variables defined in COM, eg:
Dim IISW3CLOG As New COMIISW3CInputContextClass
(there is more than one)
But this dll refers somewhere to bigger: LogParser.dll which is acutally a COM component which requires registration, so my LogParser_Form doesn't appear when button is clicked, but it throws an exception that COM component is not found...
Unfortunately Fody Costura or Ilmerge don't work for the COM...
I tried multiple tricks wich manifest files, etc, but no luck...
You are my last hope - please help me... How to embed this COM to exe without registering it?
I suppose that properly used manifest files may help, but I didn't find a way to successfully use it ...
Getting Registration-Free COM to work can be tricky, but works when configured properly. The key issue is creating manifests, which document all required dependencies. In your case, you'll need two manifests:
Client manifest for your application
Server manifest for the LogParser library. This part requires a tool for analyzing type libraries, such as the OLE/COM Object Viewer (oleview.exe). It allows looking into the embedded type library inside LogParser.dll.
Let's take the (slightly modified) C# example, which is documented in the LogParser help file. The client is named "logqryclient.exe" in this case, and the Runtime Callable Wrapper has been created via the type library importer (tlbimp).
using System;
using Interop.MSUtil;
namespace logqryclient
{
class Program
{
static void Main(string[] args)
{
try
{
// Instantiate the LogQuery object
ILogQuery oLogQuery = new LogQueryClassClass();
// Create the query
string query = #"SELECT TOP 50 SourceName, EventID, Message FROM System";
// Execute the query
ILogRecordset oRecordSet = oLogQuery.Execute(query, null);
// Browse the recordset
for (; !oRecordSet.atEnd(); oRecordSet.moveNext())
{
ILogRecord rec = oRecordSet.getRecord();
Console.WriteLine(rec.toNativeString(","));
}
// Close the recordset
oRecordSet.close();
}
catch (System.Runtime.InteropServices.COMException exc)
{
Console.WriteLine("Unexpected error: " + exc.Message);
}
}
}
}
To use this code without registering the COM classes, you'll first need to place the LogParser.dll into the same directory as the client executable.
Next, you'll need to create an accompanying server manifest (named "LogParser.manifest" here). This documents all necessary classes and marshalling information for the interfaces (required for thread switching). As mentioned earlier, you'll need a type library analyzer to gain access to the class and interface identifiers.
In the above case, you'll need identifiers for:
ILogQuery interface & LogQueryClass class
ILogRecordset interface
ILogRecord interface
Hence, the server manifest could look as follows:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="LogParser" version="1.0.0.0" />
<file name = "LogParser.dll">
<!-- LogQueryClass -->
<comClass
clsid="{8CFEBA94-3FC2-45CA-B9A5-9EDACF704F66}"
threadingModel = "Apartment" />
<!-- Embedded type library -->
<typelib
tlbid="{A7E75D86-41CD-4B6E-B4BD-CC2ED34B3FB0}"
version="1.0"
helpdir=""/>
</file>
<!-- Marshalling information for interfaces -->
<comInterfaceExternalProxyStub
name="ILogQuery"
iid="{3BDE06BC-89E4-42FD-BE64-832A5F33D7D3}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}"
tlbid = "{A7E75D86-41CD-4B6E-B4BD-CC2ED34B3FB0}" />
<comInterfaceExternalProxyStub
name="ILogRecordset"
iid="{C9452B1B-093C-4842-ABD1-F81410926874}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}"
tlbid = "{A7E75D86-41CD-4B6E-B4BD-CC2ED34B3FB0}" />
<comInterfaceExternalProxyStub
name="ILogRecord"
iid="{185FFF88-E24A-4984-9621-AA41BEAE8513}"
proxyStubClsid32="{00020424-0000-0000-c000-000000000046}"
baseInterface="{00000000-0000-0000-c000-000000000046}"
tlbid = "{A7E75D86-41CD-4B6E-B4BD-CC2ED34B3FB0}" />
</assembly>
To allow the client to find the server manifest and ultimately the LogParser library, embed the following client manifest into the "logqryclient.exe" client:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type = "win32"
name = "logqryclient"
version = "1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="LogParser"
version="1.0.0.0" />
</dependentAssembly>
</dependency>
</assembly>
Now, all required information is located in the manifests, so that you can run the code in registration-free configuration.
I have inherited a web project that uses WCF in a separate project of my solution to get the data. I'm not familiar with WCF but I was asked to see if I could send another parameter. A developer that is responsible for the actual Web Service will make the adjustment for the extra parameter on his side.
I did a search in the solution for the property called crewMemberConMonthField so I could just copy and paste a new one called crewMemberConMonthField2. I found it in the References.cs and in a FH_FTPService.xsd file.
In the .xsd file I copy and pasted the XML and just changed the name. I then went to the refernces.cs file and added another parameter with the new name (crewMemberConMonthField2). Since I'm using VS 2015 it gives me the option to refactor the name but when I do I get a warning message that basically says "the file could not be refactored. The current object is auto-generated by the Wcf Client Generator and cannot be renamed".
I've been searching for information about this but everything I'm finding is try to walk me through created a WCF. When I look through those examples I'm not seeing anything about a Wcf Client Generator.
I was looking to see if anyone could maybe point me in the right direction.
Here is some of what I have in the reference.cs
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://sitename.com/FH_FTPService/")]
public partial class StartRequestType : object, System.ComponentModel.INotifyPropertyChanged {
private StartRequestTypePartition partitionField;
private string crewMemberConMonthField;
private string crewMemberConMonthField2;
private string sequenceConMonth1Field;
private string sequenceConMonth2Field;
and this is in my .xsd file
<schema xmlns:tns="http://sitename.com/FH_FTPService/" elementFormDefault="qualified" targetNamespace="http://sitename.com/FH_FTPService/" xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="StartRequestType">
<sequence>
<element name="CrewMemberConMonth">
<simpleType>
<restriction base="string">
<length value="7" />
<pattern value="^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)\d{4}$" />
</restriction>
</simpleType>
</element>
<element name="CrewMemberConMonth2">
<simpleType>
<restriction base="string">
<length value="7" />
<pattern value="^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)\d{4}$" />
</restriction>
</simpleType>
</element>
I have a problem that i've struggled with for a long time. This problem manifests itself in the test environment, but not in my dev environment.
I have a document library template. In a event handler I attach several content types (which all inherit from a base content type which in turn inherits from Document). The base content type has a custom document template, and event handlers defined in xmldocuments on the contenttype definition.
The Doc-ID feature is switched on in the site.
When uploading a file to the document library, the document is assigned a document ID as expected. When saving a file from Word however, the doc-ID is blank. Apparently the value is set on the item, because when changing the content type of the item afterwards, the doc-ID appears with a number indicating that it was assigned when the file was first saved. When setting the contenttype back to the original value, the Doc-ID remains displayed in the view.
To clarify: This is not the ID field of the Item content type, but the Doc-ID that is created by the doc-ID feature in SharePoint 2010.
Any ideas to why this value is not set when promoting values from Word?
It seems that the solution lied in the base Document content type in the site collection. When the document-id feature is activated, event handlers are added to the content type definition as xmldocuments. When a regular document library is created, the content type in the library inherits from this content type, and thus gets a copy of the event receivers.
My custom document library with custom content types did not inherit from the site collection document content type, but rather from the base document content type (0x0101), and did not get a copy of the event receivers.
Copying the xmldocuments into my base content type seems to do the trick.
<XmlDocuments>
<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events">
<spe:Receivers xmlns:spe="http://schemas.microsoft.com/sharepoint/events">
<Receiver>
<Name>Document ID Generator</Name>
<Synchronization>Synchronous</Synchronization>
<Type>10001</Type>
<SequenceNumber>1000</SequenceNumber>
<Assembly>Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
<Class>Microsoft.Office.DocumentManagement.Internal.DocIdHandler</Class>
<Data>
</Data>
<Filter>
</Filter>
</Receiver>
<Receiver>
<Name>Document ID Generator</Name>
<Synchronization>Synchronous</Synchronization>
<Type>10002</Type>
<SequenceNumber>1001</SequenceNumber>
<Assembly>Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
<Class>Microsoft.Office.DocumentManagement.Internal.DocIdHandler</Class>
<Data>
</Data>
<Filter>
</Filter>
</Receiver>
<Receiver>
<Name>Document ID Generator</Name>
<Synchronization>Synchronous</Synchronization>
<Type>10004</Type>
<SequenceNumber>1002</SequenceNumber>
<Assembly>Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
<Class>Microsoft.Office.DocumentManagement.Internal.DocIdHandler</Class>
<Data>
</Data>
<Filter>
</Filter>
</Receiver>
<Receiver>
<Name>Document ID Generator</Name>
<Synchronization>Synchronous</Synchronization>
<Type>10006</Type>
<SequenceNumber>1003</SequenceNumber>
<Assembly>Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
<Class>Microsoft.Office.DocumentManagement.Internal.DocIdHandler</Class>
<Data>
</Data>
<Filter>
</Filter>
</Receiver>
</spe:Receivers>
</XmlDocument>
</XmlDocuments>
I have a SP project with two features:
a first feature that defines some fields, a content type and a list definition
a second feature that defines a list instance of the first feature definition
in the second feature, I use the ContentTypeRef element to bind to the content type defined in the first feature. I saw in many blog post and forum thread that Fields are not correctly populated to the list, but it's not my issue (maybe it's related ?)
The instantiated list defines a content type, but instead of inheriting from my content type, it inherits the "System" content type.
Is this behavior correct ? how can I actually inherits my content type instead of system content type ?
thx in advance
[Edit] the simpliest workaround I found is to copy past the content type definition into the contenttypes element of my list schema... but it's still a copy/paste operation (as ugly as it can be)
Please make sure that your content type ID is valid, I never managed to bypass the item content type (0x01) which means that your content type will have an ID of 0x0100{A-GUID}.
Anyway, even if you defined properly your content type and this one is working as expected when you bind it to a custom list, you'll still need to re-declare it in your list schema with all its field reference and once again, copy most of the definition of your field (I had issue with less than ID, name, display name, type in this area)...
Eg with the last list I created :
<ContentTypes>
<ContentType ID="0x0100FDCCBFFB0FBF4D5C8E069F582412909602" Name="UniverseTranslation" Group="XYZ" Description="Universe Translation" Version="0">
<FieldRefs>
<FieldRef ID="{39BF387B-C20A-4D30-BD17-CB70E4609FA2}" Name="LookupUniverse" DisplayName="Universe" Required="TRUE" />
<FieldRef ID="{824F7063-6D09-48CD-B5BA-FE9B5EE36D6A}" Name="WCC_Language" DisplayName="Language" Required="TRUE" />
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" DisplayName="Translation" Required="TRUE" />
<FieldRef ID="{EC8E4DB7-B715-430B-9B4A-F222F025EFAB}" Name="RichDescription" DisplayName="Description"/>
</FieldRefs>
</ContentType>
</ContentTypes>
<Fields>
<Field
ID="{39bf387b-c20a-4d30-bd17-cb70e4609fa2}"
Name="LookupUniverse"
DisplayName="Universe"
Type="Lookup"
ShowField="Title"
Required="TRUE"
EnforceUniqueValues="FALSE"
List="Lists/Universes">
</Field>
<Field
ID="{824F7063-6D09-48CD-B5BA-FE9B5EE36D6A}"
Name="WCC_Language"
DisplayName="Language"
Type="VariationLabelsFieldType"
Required="TRUE">
</Field>
<Field
ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}"
Name="Title"
DisplayName="Translation"
Type="Text"
Required="TRUE">
</Field>
<Field
ID="{EC8E4DB7-B715-430B-9B4A-F222F025EFAB}"
Name="RichDescription"
DisplayName="Rich Description"
Type="Note"
NumLines="4"
RichText="TRUE"
RichTextMode="Compatible"
AllowHyperlink="TRUE"
IsolateStyles="FALSE"
AppendOnly="FALSE"
Required="FALSE">
</Field>
</Fields>
If you can post your content type definition and part of your list schema, I'm pretty sure we'll be able to provide a more relevant help.
Kindly
It worked for me by adding the relative folder path for the content type's resource folder. It also worked for adding multiple references for content types to the list as follows:
<ContentTypes>
<ContentType ID="0x01006775E96C04A04F52AC1FCE50F0CB0901" Name="contentType1" Group="Test Content Types" Description="Test Content Type" Inherits="TRUE" Version="0">
<Folder TargetName="contentType1" />
</ContentType>
<ContentType ID="0x0100958BB07B626A494F9201B03E96948F3D" Name="contentType2" Group="Test Content Types" Description="Test Content Type" Inherits="TRUE" Version="0">
<Folder TargetName="contentType2" />
</ContentType>
</ContentTypes>
Is it possible to set type to just date (NOT datetime) via entity framework designer?
I had a look around and the only answer that I've found is a post from MSDN forum from a year ago...
http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/28e45675-f64b-41f0-9f36-03b67cdf2e1b
I'm very new here and I don't really understand the instructions where they talk about structural annotations...
I can go through the generated SQL script and change each line but I rather not do that...
Structural annotation - nice. It is the first time I heard about this feature but it works. I just tried it. I will try to explain it little bit.
Structural annotations are just random xml added to EDMX file. EDMX file is in fact just XML wich has 4 parts - CSDL, MSL, SSDL and part related to positioning elements in the designer.
CSDL describes entities and associations among entities (defined in the designer)
SSDL describes tables and relations
MSL describes mapping between CSDL and SSDL
If you start with model first (you want to generate database from your model), you have only CSDL part and both SSDL and MSL will be generated by some automatic process (T4 templates executed in workflow) once SSDL is created another T4 template will generate SQL script for database creation.
Structural annotation described in linked MSDN forum's thread is a hint. You will place structural annotation into CSDL part of the EDMX (you must open EDMX as XML - click on the file in solution explorer and choose Open with). My test CSDL describes single User entity with three properties (entity is visible on screenshot later in the answer):
<!-- CSDL content -->
<edmx:ConceptualModels>
<Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm"
xmlns:cg="http://schemas.microsoft.com/ado/2006/04/codegeneration"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation"
xmlns:custom="http://tempuri.org/custom"
Namespace="Model" Alias="Self" >
<EntityContainer Name="ModelContainer" annotation:LazyLoadingEnabled="true">
<EntitySet Name="UsersSet" EntityType="Model.User" />
</EntityContainer>
<EntityType Name="User">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Type="String" Name="Login" Nullable="false" />
<Property Type="DateTime" Name="CreatedAt" Nullable="false">
<custom:SqlType edmx:CopyToSSDL="true">Date</custom:SqlType>
</Property>
</EntityType>
</Schema>
</edmx:ConceptualModels>
I have added custom namespace definition in Schema element: xmlns:custom="http://tempuri.org/custom" and defined custom structural annotation for CreatedAt property:
<Property Type="DateTime" Name="CreatedAt" Nullable="false">
<custom:SqlType edmx:CopyToSSDL="true">Date</custom:SqlType>
</Property>
The name of the namespace or element used for structural annotation are not important - it is absolutely up to you what names do you use. The only important thing is edmx:CopyToSSDL="true" attribute. This attribute is recognized by T4 template used for SSDL creation and it just takes this element and places it to SSDL. Generated SSDL looks like:
<Schema Namespace="Model.Store" Alias="Self"
Provider="System.Data.SqlClient" ProviderManifestToken="2008"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<EntityContainer Name="ModelStoreContainer">
<EntitySet Name="UsersSet" EntityType="Model.Store.UsersSet" store:Type="Tables" Schema="dbo" />
</EntityContainer>
<EntityType Name="UsersSet">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="Login" Type="nvarchar(max)" Nullable="false" />
<Property Name="CreatedAt" Type="datetime" Nullable="false">
<custom:SqlType xmlns:custom="http://tempuri.org/custom">Date</custom:SqlType>
</Property>
</EntityType>
</Schema>
The only point was moving the structural annotation to SSDL. All annotations are accessible in metadata through some name value collection. Now you need to modify T4 template responsible for SQL script generation to recognize this annotation and use the value defined in the annotation instead of type defined in the property. You can find the template in:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen\SSDLToSQL10.tt
Copy template file to new location (so that you don't modify the original one) and replace default table creation with this:
-- Creating table '<#=tableName#>'
CREATE TABLE <# if (!IsSQLCE) {#>[<#=schemaName#>].<#}#>[<#=tableName#>] (
<#
for (int p = 0; p < entitySet.ElementType.Properties.Count; p++)
{
EdmProperty prop = entitySet.ElementType.Properties[p];
#>
[<#=Id(prop.Name)#>] <#
if (prop.MetadataProperties.Contains("http://tempuri.org/custom:SqlType"))
{
MetadataProperty annotationProperty = prop.MetadataProperties["http://tempuri.org/custom:SqlType"];
XElement e = XElement.Parse(annotationProperty.Value.ToString());
string value = e.Value.Trim();
#>
<#=value#> <# } else { #> <#=prop.ToStoreType()#> <# } #> <#=WriteIdentity(prop, targetVersion)#> <#=WriteNullable(prop.Nullable)#><#=(p < entitySet.ElementType.Properties.Count - 1) ? "," : ""#>
<#
}
#>
);
GO
Now the last point is changing the template used for SQL script generation. Open EDMX file in the designer and go to model's properties (just click somewhere in the designer while you have properties window opened). Change DDL Generation Template to the template you modified.
Run Generate Database from Model and it will create SQL script containing:
-- Creating table 'UsersSet'
CREATE TABLE [dbo].[UsersSet] (
[Id] int IDENTITY(1,1) NOT NULL,
[Login] nvarchar(max) NOT NULL,
[CreatedAt] Date NOT NULL
);
GO
This is probably the most advanced and hidden feature of EDMX I have seen yet. Annotations together with custom T4 templates can get you a lot of control over both class and SQL generation. I can imagine using this to define for example database indexes or unique keys when using model first or add selectively some custom attributes to generated POCO classes.
The reason why this is so hidden is that there is no tooling support in VS out-of-the box to use this.
From NuGet look for TiraggoEdmx, it serves up all the low level information from your EDMX files in a very nice way. See http://brewdawg.github.io/Tiraggo.Edmx/