patterncsharpMinor
Class to DataTable or Datatable to class mapper
Viewed 0 times
datatablemapperclass
Problem
My code goes from a class to
I still plan on adding more feature here and there, but overall I'm pretty satisfied with its functionality. I am submitting my code for review here, and I'm trying to polish it now for release. I welcome any and all criticism and comments, questions, etc.
One area in particular that I have a question about is organization. Right now, I have the functions broke up into static classes that group them by their functionality. This Is the best I have thought of so far, and the naming is pretty intuitive. The current classes/groups are
The name of this product (so far) is EntityJustWorks. I have kept all these classes under the namespace
If you would like to go straight to the code download, you can access my GitHub. I also keep a copy of my code, as well as comments and explanations of certain important sections on my C# programming blog.
```
namespace EntityJustWorks.SQL
{
public static class Convert
{
///
/// Generates a C# class code file from a Database given an SQL connection string and table name.
///
public static string SQLToCSharp(string ConnectionString, string TableName)
{
DataTable table = Query.QueryToDataTable(ConnectionString, "SELECT TOP 1 * FROM [{0}]", TableName);
DataTable, and back again. It populates the class's public properties, or creates DataColumns whose names and types match that of the the class's public properties. It also has methods that allow the developer to go from a query to a populated class, or from a class to an SQL script or SQL query to C# class code files.I still plan on adding more feature here and there, but overall I'm pretty satisfied with its functionality. I am submitting my code for review here, and I'm trying to polish it now for release. I welcome any and all criticism and comments, questions, etc.
One area in particular that I have a question about is organization. Right now, I have the functions broke up into static classes that group them by their functionality. This Is the best I have thought of so far, and the naming is pretty intuitive. The current classes/groups are
Map, Query, Script, Code, Convert and Helper.The name of this product (so far) is EntityJustWorks. I have kept all these classes under the namespace
EntityJustWorks.SQL, because these are all (more or less) SQL specific and I think that I may want to add another library that deals with a different repository. Does this seem like a sound naming convention? Would this do good to hide behind an 'live record'? Again I welcome all and any criticisms/comments.If you would like to go straight to the code download, you can access my GitHub. I also keep a copy of my code, as well as comments and explanations of certain important sections on my C# programming blog.
```
namespace EntityJustWorks.SQL
{
public static class Convert
{
///
/// Generates a C# class code file from a Database given an SQL connection string and table name.
///
public static string SQLToCSharp(string ConnectionString, string TableName)
{
DataTable table = Query.QueryToDataTable(ConnectionString, "SELECT TOP 1 * FROM [{0}]", TableName);
Solution
Intro
This is really a nice to have question. The class seems to be good structured and well commented. But, it is a lot of code to review, so let us start.
General
-
based on the naming guidelines input parameter should be named using
-
you should use braces
-
comments should describe why something is done. What is done should be described by the code itself by using meaningful names for methods, properties etc.
So comments like
Convert
-
Now we can refactor the methods, but I prefer to pass a
The
Code
-
The creation of the
-
if we add a
By using a
```
private static string FromCodeNamespace(CodeNamespace codeNamespace)
{
// CodeGeneratorOptions so the output is clean and easy to read
CodeGeneratorOptions codeOptions = GetDef
This is really a nice to have question. The class seems to be good structured and well commented. But, it is a lot of code to review, so let us start.
General
-
based on the naming guidelines input parameter should be named using
camelCase casing. -
you should use braces
{} for single if statements also. This will make your code less errorprone. If you don't want to use them, you should be consistent with your style. In your code you are using them sometimes but most of the time you aren't using them. -
comments should describe why something is done. What is done should be described by the code itself by using meaningful names for methods, properties etc.
So comments like
// Create the class are just noise which should be removed. Convert
- It would be better to name the methods using the conventions of the NET
System.Convertclass, likeToXXX()or/andFromXXX().
-
SQLToCSharp() returns a string and by the name of the method one could assume that he will get the string representation of a C# class, but this method will instead return on success the filename of the generated and written class. To solve this issue you should consider to add a class CSharpCode which is returned. This class should have a static Empty property to reflect the case that the returned object isn't a good one. public class CSharpCode
{
public string Name { get; private set; }
public string NameSpace { get; private set; }
public string Content { get; private set; }
public CSharpCode(string name, string nameSpace, string content)
{
Name = name;
NameSpace = nameSpace;
Content = content;
}
private static CSharpCode instance = new CSharpCode();
public static CSharpCode Empty { get { return instance; } }
private CSharpCode() { }
public override bool Equals(object obj)
{
if (obj == null) return false;
if (this.GetType() != obj.GetType()) return false;
// safe because of the GetType check
CSharpCode other = (CSharpCode)obj;
if (!Object.Equals(Name, other.Name)) return false;
if (!Object.Equals(NameSpace, other.NameSpace)) return false;
if (!Object.Equals(Content, other.Content)) return false;
return true;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
if (Name != null) { hash = hash * 23 + Name.GetHashCode(); }
if (NameSpace != null) { hash = hash * 23 + NameSpace.GetHashCode(); }
if (Content != null) { hash = hash * 23 + Content.GetHashCode(); }
return hash;
}
}
public override string ToString()
{
IList values = new List();
if (!String.IsNullOrWhiteSpace(NameSpace))
{
values.Add(NameSpace);
}
if (!String.IsNullOrWhiteSpace(Name))
{
values.Add(Name);
}
if (values.Count != 0)
{
return String.Join(".", values);
}
return base.ToString();
}
}Now we can refactor the methods, but I prefer to pass a
DataTable over a connectionstring and tablename. So, we will just do both. public static CSharpCode ToCSharpCode(string connectionString, string tableName)
{
DataTable table = Query.QueryToDataTable(connectionString, "SELECT TOP 1 * FROM [{0}]", tableName);
return ToCSharpCode(table);
}
public static CSharpCode ToCSharpCode(DataTable dataTable)
{
return Code.DatatableToCSharp(dataTable);
}The
ClassToSQL() method does not belong to Convert class, because it doesn't convert the classCollection but saves them in a database. It would be better to change it to return a DataTable. This DataTable could then be saved using another method which should live inside the Query class. public static DataTable FromType(params T[] classCollection) where T : class
{
return Map.ClassToDatatable(classCollection);
}Code
-
CreateCodeFile()The creation of the
CodeGeneratorOptions should be extracted to a separate method. This improves the readability of the CreateCodeFile() method. private static CodeGeneratorOptions GetDefaultOptions()
{
CodeGeneratorOptions codeOptions = new CodeGeneratorOptions();
codeOptions.BlankLinesBetweenMembers = false;
codeOptions.VerbatimOrder = true;
codeOptions.BracingStyle = "C";
codeOptions.IndentString = "\t";
return codeOptions;
}-
if we add a
string FromCodeNameSpace() method, we can simplify the CreateCodeFile() method and if we want to, we can just remove it. By using a
MemoryStream instead of a FileStream we will speed up the creation of the code. ```
private static string FromCodeNamespace(CodeNamespace codeNamespace)
{
// CodeGeneratorOptions so the output is clean and easy to read
CodeGeneratorOptions codeOptions = GetDef
Code Snippets
public class CSharpCode
{
public string Name { get; private set; }
public string NameSpace { get; private set; }
public string Content { get; private set; }
public CSharpCode(string name, string nameSpace, string content)
{
Name = name;
NameSpace = nameSpace;
Content = content;
}
private static CSharpCode instance = new CSharpCode();
public static CSharpCode Empty { get { return instance; } }
private CSharpCode() { }
public override bool Equals(object obj)
{
if (obj == null) return false;
if (this.GetType() != obj.GetType()) return false;
// safe because of the GetType check
CSharpCode other = (CSharpCode)obj;
if (!Object.Equals(Name, other.Name)) return false;
if (!Object.Equals(NameSpace, other.NameSpace)) return false;
if (!Object.Equals(Content, other.Content)) return false;
return true;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
if (Name != null) { hash = hash * 23 + Name.GetHashCode(); }
if (NameSpace != null) { hash = hash * 23 + NameSpace.GetHashCode(); }
if (Content != null) { hash = hash * 23 + Content.GetHashCode(); }
return hash;
}
}
public override string ToString()
{
IList<string> values = new List<string>();
if (!String.IsNullOrWhiteSpace(NameSpace))
{
values.Add(NameSpace);
}
if (!String.IsNullOrWhiteSpace(Name))
{
values.Add(Name);
}
if (values.Count != 0)
{
return String.Join(".", values);
}
return base.ToString();
}
}public static CSharpCode ToCSharpCode(string connectionString, string tableName)
{
DataTable table = Query.QueryToDataTable(connectionString, "SELECT TOP 1 * FROM [{0}]", tableName);
return ToCSharpCode(table);
}
public static CSharpCode ToCSharpCode(DataTable dataTable)
{
return Code.DatatableToCSharp(dataTable);
}public static DataTable FromType<T>(params T[] classCollection) where T : class
{
return Map.ClassToDatatable<T>(classCollection);
}private static CodeGeneratorOptions GetDefaultOptions()
{
CodeGeneratorOptions codeOptions = new CodeGeneratorOptions();
codeOptions.BlankLinesBetweenMembers = false;
codeOptions.VerbatimOrder = true;
codeOptions.BracingStyle = "C";
codeOptions.IndentString = "\t";
return codeOptions;
}private static string FromCodeNamespace(CodeNamespace codeNamespace)
{
// CodeGeneratorOptions so the output is clean and easy to read
CodeGeneratorOptions codeOptions = GetDefaultOptions();
string code = String.Empty;
using (MemoryStream memoryStream = new MemoryStream())
using (TextWriter textWriter = new StreamWriter(memoryStream, new UTF8Encoding(false, true)))
using (CSharpCodeProvider codeProvider = new CSharpCodeProvider())
{
codeProvider.GenerateCodeFromNamespace(codeNamespace, textWriter, codeOptions);
code = Encoding.UTF8.GetString(memoryStream.ToArray());
}
// Correct our little auto-property 'hack'
return code.Replace("//;", "");
}Context
StackExchange Code Review Q#77514, answer score: 6
Revisions (0)
No revisions yet.