#region MIT license
//
// MIT license
//
// Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#endregion
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
#if MONO_STRICT
using System.Data.Linq;
#else
using DbLinq.Data.Linq;
#endif
using DbLinq.Factory;
using DbLinq.Schema;
using DbLinq.Schema.Dbml;
namespace DbLinq.Vendor.Implementation
{
#if !MONO_STRICT
public
#endif
abstract partial class SchemaLoader : ISchemaLoader
{
///
/// Underlying vendor
///
///
public abstract IVendor Vendor { get; }
///
/// Vendor typed DataContext type
///
///
public abstract System.Type DataContextType { get; }
///
/// Connection used to read schema
///
///
public IDbConnection Connection { get; set; }
///
/// Gets or sets the name formatter.
///
/// The name formatter.
public INameFormatter NameFormatter { get; set; }
private TextWriter log;
///
/// Log output
///
public TextWriter Log
{
get { return log ?? Console.Out; }
set { log = value; }
}
///
/// Loads database schema
///
///
///
///
///
///
///
///
public virtual Database Load(string databaseName, INameAliases nameAliases, NameFormat nameFormat,
bool loadStoredProcedures, string contextNamespace, string entityNamespace)
{
// check if connection is open. Note: we may use something more flexible
if (Connection.State != ConnectionState.Open)
Connection.Open();
// get the database name. If we don't have one, take it from connection string...
if (string.IsNullOrEmpty(databaseName))
databaseName = Connection.Database;
// ... and if connection string doesn't provide a name, then throw an error
if (string.IsNullOrEmpty(databaseName))
throw new ArgumentException("A database name is required. Please specify /database=");
databaseName = GetDatabaseNameAliased(databaseName, nameAliases);
var schemaName = NameFormatter.GetSchemaName(databaseName, GetExtraction(databaseName), nameFormat);
var names = new Names();
var schema = new Database
{
Name = schemaName.DbName,
Class = GetRuntimeClassName(schemaName.ClassName, nameAliases),
BaseType = typeof(DataContext).FullName,
ContextNamespace = contextNamespace,
EntityNamespace = entityNamespace,
};
// order is important, we must have:
// 1. tables
// 2. columns
// 3. constraints
LoadTables(schema, schemaName, Connection, nameAliases, nameFormat, names);
LoadColumns(schema, schemaName, Connection, nameAliases, nameFormat, names);
CheckColumnsName(schema);
LoadConstraints(schema, schemaName, Connection, nameFormat, names);
CheckConstraintsName(schema);
if (loadStoredProcedures)
LoadStoredProcedures(schema, schemaName, Connection, nameFormat);
// names aren't checked here anymore, because this confuses DBML editor.
// they will (for now) be checked before .cs generation
// in the end, when probably will end up in mapping source (or somewhere around)
//CheckNamesSafety(schema);
// generate backing fields name (since we have here correct names)
GenerateStorageAndMemberFields(schema);
return schema;
}
///
/// Gets a usable name for the database.
///
/// Name of the database.
///
protected virtual string GetDatabaseName(string databaseName)
{
return databaseName;
}
protected virtual string GetDatabaseNameAliased(string databaseName, INameAliases nameAliases)
{
string databaseNameAliased = nameAliases != null ? nameAliases.GetDatabaseNameAlias(databaseName) : null;
return (databaseNameAliased != null) ? databaseNameAliased : GetDatabaseName(databaseName);
}
///
/// Gets a usable name for the database class.
///
/// Name of the clas.
///
protected virtual string GetRuntimeClassName(string className, INameAliases nameAliases)
{
string classNameAliased = nameAliases != null ? nameAliases.GetClassNameAlias(className) : null;
return (classNameAliased != null) ? classNameAliased : className;
}
///
/// Writes an error line.
///
/// The format.
/// The arg.
protected void WriteErrorLine(string format, params object[] arg)
{
var o = Log;
if (o == Console.Out)
o = Console.Error;
o.WriteLine(format, arg);
}
protected SchemaLoader()
{
NameFormatter = ObjectFactory.Create(); // the Pluralize property is set dynamically, so no singleton
}
///
/// Gets the extraction type from a columnname.
///
/// Name of the db column.
///
protected virtual WordsExtraction GetExtraction(string dbColumnName)
{
bool isMixedCase = dbColumnName != dbColumnName.ToLower() && dbColumnName != dbColumnName.ToUpper();
return isMixedCase ? WordsExtraction.FromCase : WordsExtraction.FromDictionary;
}
///
/// Gets the full name of a name and schema.
///
/// Name of the db.
/// The db schema.
///
protected virtual string GetFullDbName(string dbName, string dbSchema)
{
string fullDbName;
if (dbSchema == null)
fullDbName = dbName;
else
fullDbName = string.Format("{0}.{1}", dbSchema, dbName);
return fullDbName;
}
///
/// Creates the name of the table given a name and schema
///
/// Name of the db table.
/// The db schema.
/// The name aliases.
/// The name format.
/// The extraction.
///
protected virtual TableName CreateTableName(string dbTableName, string dbSchema, INameAliases nameAliases, NameFormat nameFormat, WordsExtraction extraction)
{
// if we have an alias, use it, and don't try to analyze it (a human probably already did the job)
var tableTypeAlias = nameAliases != null ? nameAliases.GetTableTypeAlias(dbTableName, dbSchema) : null;
if (tableTypeAlias != null)
extraction = WordsExtraction.None;
else
tableTypeAlias = dbTableName;
var tableName = NameFormatter.GetTableName(tableTypeAlias, extraction, nameFormat);
// alias for member
var tableMemberAlias = nameAliases != null ? nameAliases.GetTableMemberAlias(dbTableName, dbSchema) : null;
if (tableMemberAlias != null)
tableName.MemberName = tableMemberAlias;
tableName.DbName = GetFullDbName(dbTableName, dbSchema);
return tableName;
}
protected virtual TableName CreateTableName(string dbTableName, string dbSchema, INameAliases nameAliases, NameFormat nameFormat)
{
return CreateTableName(dbTableName, dbSchema, nameAliases, nameFormat, GetExtraction(dbTableName));
}
///
/// Creates the name of the column.
///
/// Name of the db column.
/// Name of the db table.
/// The db schema.
/// The name aliases.
/// The name format.
///
protected virtual ColumnName CreateColumnName(string dbColumnName, string dbTableName, string dbSchema, INameAliases nameAliases, NameFormat nameFormat)
{
var columnNameAlias = nameAliases != null ? nameAliases.GetColumnMemberAlias(dbColumnName, dbTableName, dbSchema) : null;
WordsExtraction extraction;
if (columnNameAlias != null)
{
extraction = WordsExtraction.None;
}
else
{
extraction = GetExtraction(dbColumnName);
columnNameAlias = dbColumnName;
}
var columnName = NameFormatter.GetColumnName(columnNameAlias, extraction, nameFormat);
// The member name can not be the same as the class
// we add a "1" (just like SqlMetal does)
var tableName = CreateTableName(dbTableName, dbSchema, nameAliases, nameFormat);
if (columnName.PropertyName == tableName.ClassName)
columnName.PropertyName = columnName.PropertyName + "1";
columnName.DbName = dbColumnName;
return columnName;
}
///
/// Creates the name of the procedure.
///
/// Name of the db procedure.
/// The db schema.
/// The name format.
///
protected virtual ProcedureName CreateProcedureName(string dbProcedureName, string dbSchema, NameFormat nameFormat)
{
var procedureName = NameFormatter.GetProcedureName(dbProcedureName, GetExtraction(dbProcedureName), nameFormat);
procedureName.DbName = GetFullDbName(dbProcedureName, dbSchema);
return procedureName;
}
///
/// Creates the name of the association.
///
/// Name of the db many.
/// The db many schema.
/// Name of the db one.
/// The db one schema.
/// Name of the db constraint.
/// Name of the foreign key.
/// The name format.
///
protected virtual AssociationName CreateAssociationName(string dbManyName, string dbManySchema,
string dbOneName, string dbOneSchema, string dbConstraintName, string foreignKeyName, NameFormat nameFormat)
{
var associationName = NameFormatter.GetAssociationName(dbManyName, dbOneName,
dbConstraintName, foreignKeyName, GetExtraction(dbManyName), nameFormat);
associationName.DbName = GetFullDbName(dbManyName, dbManySchema);
return associationName;
}
///
/// Creates the name of the schema.
///
/// Name of the database.
/// The connection.
/// The name format.
///
protected virtual SchemaName CreateSchemaName(string databaseName, IDbConnection connection, NameFormat nameFormat)
{
if (string.IsNullOrEmpty(databaseName))
{
databaseName = connection.Database;
if (string.IsNullOrEmpty(databaseName))
throw new ArgumentException("Could not deduce database name from connection string. Please specify /database=");
}
return NameFormatter.GetSchemaName(databaseName, GetExtraction(databaseName), nameFormat);
}
protected virtual ParameterName CreateParameterName(string dbParameterName, NameFormat nameFormat)
{
var parameterName = NameFormatter.GetParameterName(dbParameterName, GetExtraction(dbParameterName), nameFormat);
return parameterName;
}
protected class Names
{
public IDictionary TablesNames = new Dictionary();
public IDictionary> ColumnsNames = new Dictionary>();
public void AddColumn(string dbTableName, ColumnName columnName)
{
IDictionary columns;
if (!ColumnsNames.TryGetValue(dbTableName, out columns))
{
columns = new Dictionary();
ColumnsNames[dbTableName] = columns;
}
columns[columnName.DbName] = columnName;
}
}
///
/// Loads the tables in the given schema.
///
/// The schema.
/// Name of the schema.
/// The conn.
/// The name aliases.
/// The name format.
/// The names.
protected virtual void LoadTables(Database schema, SchemaName schemaName, IDbConnection conn, INameAliases nameAliases, NameFormat nameFormat, Names names)
{
var tables = ReadTables(conn, schemaName.DbName);
foreach (var row in tables)
{
var tableName = CreateTableName(row.Name, row.Schema, nameAliases, nameFormat);
names.TablesNames[tableName.DbName] = tableName;
var table = new Table();
table.Name = tableName.DbName;
table.Member = tableName.MemberName;
table.Type.Name = tableName.ClassName;
schema.Tables.Add(table);
}
}
///
/// Loads the columns.
///
/// The schema.
/// Name of the schema.
/// The conn.
/// The name aliases.
/// The name format.
/// The names.
protected void LoadColumns(Database schema, SchemaName schemaName, IDbConnection conn, INameAliases nameAliases, NameFormat nameFormat, Names names)
{
var columnRows = ReadColumns(conn, schemaName.DbName);
foreach (var columnRow in columnRows)
{
var columnName = CreateColumnName(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema, nameAliases, nameFormat);
names.AddColumn(columnRow.TableName, columnName);
//find which table this column belongs to
string fullColumnDbName = GetFullDbName(columnRow.TableName, columnRow.TableSchema);
DbLinq.Schema.Dbml.Table tableSchema = schema.Tables.FirstOrDefault(tblSchema => fullColumnDbName == tblSchema.Name);
if (tableSchema == null)
{
WriteErrorLine("ERROR L46: Table '" + columnRow.TableName + "' not found for column " + columnRow.ColumnName);
continue;
}
var column = new Column();
column.Name = columnName.DbName;
column.Member = columnName.PropertyName;
column.DbType = columnRow.FullType;
if (columnRow.PrimaryKey.HasValue)
column.IsPrimaryKey = columnRow.PrimaryKey.Value;
if (columnRow.Generated.HasValue)
column.IsDbGenerated = columnRow.Generated.Value;
// the Expression can originate from two sources:
// 1. DefaultValue
// 2. Expression
// we use any valid source (we can't have both)
if (column.IsDbGenerated && columnRow.DefaultValue != null)
column.Expression = columnRow.DefaultValue;
column.CanBeNull = columnRow.Nullable;
string columnTypeAlias = nameAliases != null ? nameAliases.GetColumnForcedType(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null;
var columnType = MapDbType(columnName.DbName, columnRow);
var columnEnumType = columnType as EnumType;
if (columnEnumType != null)
{
var enumType = column.SetExtendedTypeAsEnumType();
enumType.Name = columnEnumType.Name;
foreach (KeyValuePair enumValue in columnEnumType.EnumValues)
{
enumType[enumValue.Key] = enumValue.Value;
}
}
else if (columnTypeAlias != null)
column.Type = columnTypeAlias;
else
column.Type = columnType.ToString();
tableSchema.Type.Columns.Add(column);
}
}
}
}