Skip to content

Instantly share code, notes, and snippets.

@BryanWilhite
Created July 24, 2017 17:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BryanWilhite/cc0c9b967059708832791bf17924ac6f to your computer and use it in GitHub Desktop.
Save BryanWilhite/cc0c9b967059708832791bf17924ac6f to your computer and use it in GitHub Desktop.
Text Templating for EF Code-First over Oracle
using Songhay.DataAccess.Oracle.Models;
using System.Collections.Generic;
namespace Songhay.DataAccess.Oracle.TextTemplating
{
public partial class OracleEntityGenerator
{
public OracleEntityGenerator(IEnumerable<OracleTableMetadata> metadata)
{
this.TableOrViewMetadata = metadata;
}
public IEnumerable<OracleTableMetadata> TableOrViewMetadata { get; private set; }
}
}
<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="Songhay.DataAccess.Oracle" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Songhay.DataAccess.Oracle.Extensions" #>
<#@ import namespace="System.Linq" #>
<#@ output extension=".cs" #>
<#
var className = this.TableOrViewMetadata.First().TableName.ToCamelCaseFromUnderscores();
#>
using System;
using System.ComponentModel.DataAnnotations;
public class <#= className #>
{
<# foreach (var p in this.TableOrViewMetadata) { #>
<#= p.ToDataAnnotationsOrEmpty() #>public <#= p.ToDotNetTypeName() #> <#= p.ColumnName.ToCamelCaseFromUnderscores() #> { get; set; }
<# } #>
}
using Songhay.DataAccess.Oracle.Models;
using System.Collections.Generic;
namespace Songhay.DataAccess.Oracle.TextTemplating
{
public partial class OracleEntityMappingGenerator
{
public OracleEntityMappingGenerator(IEnumerable<OracleTableMetadata> metadata)
{
this.TableOrViewMetadata = metadata;
}
public IEnumerable<OracleTableMetadata> TableOrViewMetadata { get; private set; }
}
}
<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="Songhay.DataAccess.Oracle" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Songhay.DataAccess.Oracle.Extensions" #>
<#@ import namespace="System.Linq" #>
<#@ output extension=".cs" #>
<#
var tableOrViewName = this.TableOrViewMetadata.First().TableName;
var classNameForEntity = tableOrViewName.ToCamelCaseFromUnderscores();
var className = $"{classNameForEntity}Map";
#>
using System.Data.Entity.ModelConfiguration;
public class <#= className #> : EntityTypeConfiguration<<#= classNameForEntity #>>
{
public <#= className #>()
{
this.ToTable("<#= tableOrViewName #>");
//TODO: set Key: this.HasKey(???);
<# foreach (var p in this.TableOrViewMetadata) { #>
this.Property(m => m.<#= p.ColumnName.ToCamelCaseFromUnderscores() #>).HasColumnName("<#= p.ColumnName #>")<# if((p.ToDotNetTypeName() == "string") && (p.DataLength > 0)) { #>.HasMaxLength(<#= p.DataLength #>).IsUnicode(false)<# } #>;
<# } #>
}
}
namespace Songhay.DataAccess.Oracle.Models
{
/// <summary>
/// Represents output from <c>SYS.ALL_TAB_COLUMNS</c>.
/// </summary>
public partial class OracleTableMetadata
{
/// <summary>
/// The Oracle <c>SYS.ALL_TAB_COLUMNS</c> parameterized SQL.
/// </summary>
/// <remarks>
/// Note that <c>TABLE_NAME</c> in the SQL below can specify a View name as well.
/// </remarks>
public const string OracleSysAllTabColumnsSql = @"
select
COLUMN_ID
, COLUMN_NAME
, NULLABLE
, DATA_TYPE
, DATA_PRECISION
, DATA_LENGTH
, DATA_SCALE
, DATA_DEFAULT
, TABLE_NAME
from
SYS.ALL_TAB_COLUMNS
where
TABLE_NAME = :tableOrViewName
";
}
}
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Songhay.DataAccess.Oracle.Models
{
/// <summary>
/// Represents output from <c>SYS.ALL_TAB_COLUMNS</c>.
/// </summary>
public partial class OracleTableMetadata
{
/// <summary>
/// Gets or sets the column identifier.
/// </summary>
/// <value>
/// The column identifier.
/// </value>
public int? ColumnId { get; set; }
/// <summary>
/// Gets or sets the name of the column.
/// </summary>
/// <value>
/// The name of the column.
/// </value>
public string ColumnName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is nullable.
/// </summary>
/// <value>
/// <c>true</c> if this instance is nullable; otherwise, <c>false</c>.
/// </value>
public bool? IsNullable { get; set; }
/// <summary>
/// Gets or sets the type of the data.
/// </summary>
/// <value>
/// The type of the data.
/// </value>
public string DataType { get; set; }
/// <summary>
/// Gets or sets the data precision.
/// </summary>
/// <value>
/// The data precision.
/// </value>
public int? DataPrecision { get; set; }
/// <summary>
/// Gets or sets the length of the data.
/// </summary>
/// <value>
/// The length of the data.
/// </value>
[Required]
public int DataLength { get; set; }
/// <summary>
/// Gets or sets the data scale.
/// </summary>
/// <value>
/// The data scale.
/// </value>
public int? DataScale { get; set; }
/// <summary>
/// Gets or sets the data default.
/// </summary>
/// <value>
/// The data default.
/// </value>
public string DataDefault { get; set; }
/// <summary>
/// Gets or sets the name of the table.
/// </summary>
/// <value>
/// The name of the table.
/// </value>
[Required]
public string TableName { get; set; }
/// <summary>
/// The string representation of this instance.
/// </summary>
public override string ToString()
{
var sb = new StringBuilder();
if (!string.IsNullOrEmpty(this.TableName)) sb.AppendFormat("TableName: {0}\n", this.TableName);
if (this.ColumnId != null) sb.AppendFormat("ColumnId: {0}\n", this.ColumnId);
if (!string.IsNullOrEmpty(this.ColumnName)) sb.AppendFormat("ColumnName: {0}\n", this.ColumnName);
if (this.IsNullable != null) sb.AppendFormat("IsNullable: {0}\n", this.IsNullable);
return (sb.Length > 0) ? sb.ToString() : base.ToString();
}
}
}
using Songhay.DataAccess.Oracle.Models;
using System;
namespace Songhay.DataAccess.Oracle.Extensions
{
/// <summary>
/// Extensions of <see cref="OracleTableMetadata"/>
/// </summary>
/// <remarks>
/// For research details, see “Oracle Data Types”
/// [https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm]
/// </remarks>
public static class OracleTableMetadataExtensions
{
/// <summary>
/// Converts the <see cref="OracleTableMetadata"/> into a data annotations or <see cref="string.Empty"/>.
/// </summary>
/// <param name="metadata">The metadata.</param>
public static string ToDataAnnotationsOrEmpty(this OracleTableMetadata metadata)
{
var annotations = string.Empty;
if (metadata == null) return annotations;
if (string.IsNullOrEmpty(metadata.DataType)) return annotations;
var dbTypeName = metadata.DataType.ToLowerInvariant();
var isNullable = metadata.IsNullable.HasValue && metadata.IsNullable.GetValueOrDefault();
var columnName = metadata.ColumnName.ToCamelCaseFromUnderscores();
var newLinePlus4 = "\n ";
var maxLengthTemplate = "[MaxLength({0}, ErrorMessage = \"{1} cannot exceed {0} characters.\")]";
var minLengthTemplate = "[MinLength({0}, ErrorMessage = \"{1} cannot have less than {0} characters.\")]";
if (dbTypeName.Contains("varchar"))
{
annotations = isNullable ?
string.Format(string.Concat(maxLengthTemplate, newLinePlus4), metadata.DataLength, columnName)
:
string.Format(string.Concat("[Required]", newLinePlus4, maxLengthTemplate, newLinePlus4), metadata.DataLength, columnName);
}
else if (dbTypeName.Contains("char"))
{
annotations = isNullable ?
string.Format(string.Concat(maxLengthTemplate, newLinePlus4, minLengthTemplate, newLinePlus4), metadata.DataLength, columnName)
:
string.Format(string.Concat("[Required]", newLinePlus4, maxLengthTemplate, newLinePlus4, minLengthTemplate, newLinePlus4), metadata.DataLength, columnName);
}
return annotations;
}
/// <summary>
/// Converts the <see cref="OracleTableMetadata"/> into a .NET type name.
/// </summary>
/// <param name="metadata">The metadata.</param>
/// <remarks>
/// Reference: “Oracle to .NET type mapping” [https://www.devart.com/dotconnect/oracle/docs/DataTypeMapping.html]
/// </remarks>
public static string ToDotNetTypeName(this OracleTableMetadata metadata)
{
if (metadata == null) return null;
if (string.IsNullOrEmpty(metadata.DataType)) return "object";
var typeName = "string";
switch (metadata.DataType.ToLowerInvariant())
{
case "date":
typeName = string.Concat(nameof(DateTime), metadata.IsNullable.GetValueOrDefault() ? "?" : string.Empty);
break;
case "number":
if (metadata.DataScale.GetValueOrDefault().Equals(0) || (metadata.DataScale == null))
{
typeName = (metadata.DataLength > 22) ? "long" : "int";
}
else
{
typeName = (metadata.DataPrecision.GetValueOrDefault() > 16) ? "double" : "decimal";
}
typeName = string.Concat(typeName, metadata.IsNullable.GetValueOrDefault() ? "?" : string.Empty);
break;
}
return typeName;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment