Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 96a67bd

Browse files
authored
Add sample T4 text template samples (#447)
* Update README.md * Update README.md * Create EntityType.t4 * Create EntityType.t4 * Create README.md
1 parent bda0887 commit 96a67bd

File tree

5 files changed

+410
-3
lines changed

5 files changed

+410
-3
lines changed

‎README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ Please consult the [security guide](./SECURITY.md) for our responsible security
4141

4242
## License
4343

44-
Copyright (c) 2015, 2023 Oracle and/or its affiliates.
44+
Copyright (c) 2015, 2025 Oracle and/or its affiliates.
4545

4646
Released under the MIT License

‎samples/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Running ODP.NET Core Samples from Command Line
1616
1) Install .NET Core SDK from Microsoft's website: https://dotnet.microsoft.com/download
1717
2) Open a terminal such as PowerShell, command prompt, or bash. Enter the following commands to create and setup your ODP.NET Core sample: <br>
1818
A) dotnet new console --output (Sample Name) <br>
19-
B) dotnet add package Oracle.ManagedDataAccess.Core --version (e.g. 23.5.0)
19+
B) dotnet add package Oracle.ManagedDataAccess.Core --version (e.g. 23.8.0)
2020
4) Replace the contents of Program.cs with the GitHub sample code of interest.
2121
5) Insert your user id, password, and data source. The sample will have its own README or comments to indicate additional configuration that may be required.
2222
6) Run using the following command: dotnet run --project (Sample Name)
@@ -109,7 +109,8 @@ Entity Framework Core
109109
* Getting Started Sample: Demonstrates a basic Oracle EF Core scenario using migrations and scaffolding. <br>
110110
* JSON Columns Sample: Demonstrates how to create an owned entity, insert, query, update, and delete JSON column data. <br>
111111
* Keyless Entity Types Sample: Demonstrates Oracle EF Core keyless entity types with relational and materialized views. <br>
112-
* Stored Procedure Result Set Samples: Demonstrates using PL/SQL that returns either an explicitly or implicitly bound REF Cursor.
112+
* Stored Procedure Result Set Samples: Demonstrates using PL/SQL that returns either an explicitly or implicitly bound REF Cursor. <br>
113+
* T4 Text Template Samples: Demonstrates data type mapping customization when scaffolding.
113114

114115
Event Handler
115116
-------------

‎samples/ef-core/t4-templates/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Customize Oracle EF Core Data Type Mappings with T4 Text Templates
2+
3+
Oracle EF Core data type mapping between entity properties and database columns can be customized with T4 text templates. This repository includes sample T4 templates that can be used as is or customized with an alternative set of .NET data type mappings. The samples demonstrate the following mapping scenarios:
4+
5+
* All Numeric Types - Customizes all database numeric column type mappings to .NET properties
6+
* Single Numeric Type - Customizes one database column type mapping to a specific .NET property
7+
8+
The [ODP.NET Scaffolding documentation](https://docs.oracle.com/en/database/oracle/oracle-database/23/odpnt/EFCoreREDataTypeMapping.html) provides more information and a step-by-step usage guide.
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
<# // Sample Oracle T4 template to customize mapping all database numeric column types to .NET properties. The mapped .NET properties store a superset of values of their mapped database types. #>
2+
3+
<#@ template hostSpecific="true" #>
4+
<#@ assembly name="Microsoft.EntityFrameworkCore" #>
5+
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
6+
<#@ assembly name="Microsoft.EntityFrameworkCore.Relational" #>
7+
<#@ assembly name="Microsoft.Extensions.DependencyInjection.Abstractions" #>
8+
<#@ parameter name="EntityType" type="Microsoft.EntityFrameworkCore.Metadata.IEntityType" #>
9+
<#@ parameter name="Options" type="Microsoft.EntityFrameworkCore.Scaffolding.ModelCodeGenerationOptions" #>
10+
<#@ parameter name="NamespaceHint" type="System.String" #>
11+
<#@ import namespace="System.Collections.Generic" #>
12+
<#@ import namespace="System.ComponentModel.DataAnnotations" #>
13+
<#@ import namespace="System.Linq" #>
14+
<#@ import namespace="System.Text" #>
15+
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
16+
<#@ import namespace="Microsoft.EntityFrameworkCore.Design" #>
17+
<#@ import namespace="Microsoft.Extensions.DependencyInjection" #>
18+
<#
19+
if (EntityType.IsSimpleManyToManyJoinEntityType())
20+
{
21+
// Don't scaffold these
22+
return "";
23+
}
24+
25+
var services = (IServiceProvider)Host;
26+
var annotationCodeGenerator = services.GetRequiredService<IAnnotationCodeGenerator>();
27+
var code = services.GetRequiredService<ICSharpHelper>();
28+
29+
var usings = new List<string>
30+
{
31+
"System",
32+
"System.Collections.Generic"
33+
};
34+
35+
if (Options.UseDataAnnotations)
36+
{
37+
usings.Add("System.ComponentModel.DataAnnotations");
38+
usings.Add("System.ComponentModel.DataAnnotations.Schema");
39+
usings.Add("Microsoft.EntityFrameworkCore");
40+
}
41+
42+
if (!string.IsNullOrEmpty(NamespaceHint))
43+
{
44+
#>
45+
namespace <#= NamespaceHint #>;
46+
47+
<#
48+
}
49+
50+
if (!string.IsNullOrEmpty(EntityType.GetComment()))
51+
{
52+
#>
53+
/// <summary>
54+
/// <#= code.XmlComment(EntityType.GetComment()) #>
55+
/// </summary>
56+
<#
57+
}
58+
59+
if (Options.UseDataAnnotations)
60+
{
61+
foreach (var dataAnnotation in EntityType.GetDataAnnotations(annotationCodeGenerator))
62+
{
63+
#>
64+
<#= code.Fragment(dataAnnotation) #>
65+
<#
66+
}
67+
}
68+
#>
69+
public partial class <#= EntityType.Name #>
70+
{
71+
<#
72+
var firstProperty = true;
73+
foreach (var property in EntityType.GetProperties().OrderBy(p => p.GetColumnOrder() ?? -1))
74+
{
75+
if (!firstProperty)
76+
{
77+
WriteLine("");
78+
}
79+
80+
if (!string.IsNullOrEmpty(property.GetComment()))
81+
{
82+
#>
83+
/// <summary>
84+
/// <#= code.XmlComment(property.GetComment(), indent: 1) #>
85+
/// </summary>
86+
<#
87+
}
88+
89+
if (Options.UseDataAnnotations)
90+
{
91+
var dataAnnotations = property.GetDataAnnotations(annotationCodeGenerator)
92+
.Where(a => !(a.Type == typeof(RequiredAttribute) && Options.UseNullableReferenceTypes && !property.ClrType.IsValueType));
93+
foreach (var dataAnnotation in dataAnnotations)
94+
{
95+
#>
96+
<#= code.Fragment(dataAnnotation) #>
97+
<#
98+
}
99+
}
100+
101+
// Make changes here to customize type mapping for all properties of certain column types.
102+
Type clrType;
103+
string columnType = property.GetColumnType(); // Get the store type for which we want custom mapping.
104+
105+
if (columnType == "NUMBER(1)")
106+
{
107+
clrType = Options.UseNullableReferenceTypes && property.IsNullable ? typeof(byte?) : typeof(byte); // Map NUMBER(1) to byte.
108+
}
109+
else if (columnType == "NUMBER(2)" || columnType == "NUMBER(3)" || columnType == "NUMBER(4)")
110+
{
111+
clrType = Options.UseNullableReferenceTypes && property.IsNullable ? typeof(Int16?) : typeof(Int16); // Map NUMBER(2) to NUMBER(4) to Int16.
112+
}
113+
else if (columnType == "NUMBER(5)")
114+
{
115+
clrType = Options.UseNullableReferenceTypes && property.IsNullable ? typeof(Int32?) : typeof(Int32); // Map NUMBER(5) to Int32.
116+
}
117+
else if (columnType == "NUMBER(6)" || columnType == "NUMBER(7)" || columnType == "NUMBER(8)" ||
118+
columnType == "NUMBER(9)" || columnType == "NUMBER(10)")
119+
{
120+
clrType = Options.UseNullableReferenceTypes && property.IsNullable ? typeof(Int64?) : typeof(Int64); // Map NUMBER(6) to NUMBER(10) to Int64.
121+
}
122+
else if (columnType == "NUMBER(11)" || columnType == "NUMBER(12)" || columnType == "NUMBER(13)" ||
123+
columnType == "NUMBER(14)" || columnType == "NUMBER(15)" || columnType == "NUMBER(16)" ||
124+
columnType == "NUMBER(17)" || columnType == "NUMBER(18)" || columnType == "NUMBER(19)")
125+
{
126+
clrType = Options.UseNullableReferenceTypes && property.IsNullable ? typeof(Decimal?) : typeof(Decimal); // Map NUMBER(11) to NUMBER(19) to Decimal.
127+
}
128+
// Add more column types as required.
129+
else
130+
{
131+
clrType = property.ClrType; // Keep the default CLR Type.
132+
}
133+
134+
135+
usings.AddRange(code.GetRequiredUsings(clrType));
136+
137+
var needsNullable = Options.UseNullableReferenceTypes && property.IsNullable && !clrType.IsValueType;
138+
var needsInitializer = Options.UseNullableReferenceTypes && !property.IsNullable && !clrType.IsValueType;
139+
#>
140+
public <#= code.Reference(clrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
141+
<#
142+
firstProperty = false;
143+
}
144+
145+
foreach (var navigation in EntityType.GetNavigations())
146+
{
147+
WriteLine("");
148+
149+
if (Options.UseDataAnnotations)
150+
{
151+
foreach (var dataAnnotation in navigation.GetDataAnnotations(annotationCodeGenerator))
152+
{
153+
#>
154+
<#= code.Fragment(dataAnnotation) #>
155+
<#
156+
}
157+
}
158+
159+
var targetType = navigation.TargetEntityType.Name;
160+
if (navigation.IsCollection)
161+
{
162+
#>
163+
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; set; } = new List<<#= targetType #>>();
164+
<#
165+
}
166+
else
167+
{
168+
var needsNullable = Options.UseNullableReferenceTypes && !(navigation.ForeignKey.IsRequired && navigation.IsOnDependent);
169+
var needsInitializer = Options.UseNullableReferenceTypes && navigation.ForeignKey.IsRequired && navigation.IsOnDependent;
170+
#>
171+
public virtual <#= targetType #><#= needsNullable ? "?" : "" #> <#= navigation.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
172+
<#
173+
}
174+
}
175+
176+
foreach (var skipNavigation in EntityType.GetSkipNavigations())
177+
{
178+
WriteLine("");
179+
180+
if (Options.UseDataAnnotations)
181+
{
182+
foreach (var dataAnnotation in skipNavigation.GetDataAnnotations(annotationCodeGenerator))
183+
{
184+
#>
185+
<#= code.Fragment(dataAnnotation) #>
186+
<#
187+
}
188+
}
189+
#>
190+
public virtual ICollection<<#= skipNavigation.TargetEntityType.Name #>> <#= skipNavigation.Name #> { get; set; } = new List<<#= skipNavigation.TargetEntityType.Name #>>();
191+
<#
192+
}
193+
#>
194+
}
195+
<#
196+
var previousOutput = GenerationEnvironment;
197+
GenerationEnvironment = new StringBuilder();
198+
199+
foreach (var ns in usings.Distinct().OrderBy(x => x, new NamespaceComparer()))
200+
{
201+
#>
202+
using <#= ns #>;
203+
<#
204+
}
205+
206+
WriteLine("");
207+
208+
GenerationEnvironment.Append(previousOutput);
209+
#>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /