Subsonic を使用して生成された素敵な DAL があります。BLL のスケルトンを生成する方法はありますか? SS レイヤーを GUI に直接接続したくありません。
私は両方の SS フォーラムをたどりましたが、誰もがSSS で生成されたレイヤーを DAL と呼んでいるようですが、彼らはそれを BLL として使用しています。
SS を使用して、BLL を最初から手動でコーディングせずに DAL 層と BLL 層を分離しましたか?
Subsonic を使用して生成された素敵な DAL があります。BLL のスケルトンを生成する方法はありますか? SS レイヤーを GUI に直接接続したくありません。
私は両方の SS フォーラムをたどりましたが、誰もがSSS で生成されたレイヤーを DAL と呼んでいるようですが、彼らはそれを BLL として使用しています。
SS を使用して、BLL を最初から手動でコーディングせずに DAL 層と BLL 層を分離しましたか?
はい、プロジェクトが非常に小さく、ビジネス ロジックがあまりない場合を除いて、DAL と BLL を別々に使用します。
DAL プロパティの BAL ですべての実装を行うのは本当に面倒であり、SS のコード生成の利点を無効にします。DAL を経由してスケルトン BLL を大量生産する小さなコンソール アプリを作成しました。
これがそのコードです。それは非常に粗雑なものであることを覚えておいてください(SOフェローに精査され、改善されることを期待してここに貼り付けています):
class Program
{
static void Main(string[] args)
{
InitTypesDictionary();
ProcessFile("c:\\temp\\myBll", "YOUR_BLL_NAMESPACE");//args[0], args[1]);
Console.ReadLine();
}
private static void InitTypesDictionary()
{
typesMap = new Dictionary<string, string>();
typesMap.Add("System.String", "string");
typesMap.Add("System.Int32", "int");
typesMap.Add("System.Decimal", "decimal");
typesMap.Add("System.Double", "double");
typesMap.Add("System.Guid", "Guid");
typesMap.Add("System.DateTime", "DateTime");
typesMap.Add("System.Boolean", "bool");
typesMap.Add("System.Byte", "byte");
typesMap.Add("System.Short", "short");
typesMap.Add("System.Nullable`1[System.Int32]", "int?");
typesMap.Add("System.Nullable`1[System.DateTime]", "DateTime?");
typesMap.Add("System.Nullable`1[System.Decimal]", "decimal?");
typesMap.Add("System.Nullable`1[System.Double]", "double?");
typesMap.Add("System.Nullable`1[System.Boolean]", "bool?");
}
private static void WriteError(string msg)
{
WriteInfo(msg, ConsoleColor.Red);
}
private static void WriteTypeName(string name)
{
WriteInfo(name, ConsoleColor.Blue);
}
private static void WriteInfo(string info, ConsoleColor cc)
{
ConsoleColor clr = Console.ForegroundColor;
Console.ForegroundColor = cc;
Console.WriteLine(info);
Console.ForegroundColor = clr;
}
private static void ProcessFile(string savePath, string _namespace)
{
Assembly asm = Assembly.GetAssembly(typeof(ROMS.DAL.RomsAdBusiness));
//Assembly asm = Assembly.ReflectionOnlyLoad("ROMS.DAL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
Type[] types = asm.GetTypes();
foreach (Type t in types)
{
if (t.BaseType.Name.Contains("ActiveRecord"))
{
WriteTypeName("Processing " + t.Name);
ProcessType(t, savePath, _namespace);
}
}
}
private static void ProcessType(Type t, string path, string nsp)
{
string className = t.Name.Substring(4);
StringBuilder sbCode = new StringBuilder();
sbCode.Append(imports);
//sbCode.Append(Environment.NewLine);
sbCode.AppendFormat(comments, t.Name, DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"));
sbCode.AppendFormat(namespaceStart, nsp);
//sbCode.Append(Environment.NewLine);
sbCode.AppendFormat(_class, className);
//sbCode.Append(Environment.NewLine);
sbCode.AppendFormat(dalObject, t.Name);
sbCode.AppendFormat(regionStart, "ctor");
//sbCode.Append(Environment.NewLine);
sbCode.AppendFormat(ctorString, className, t.Name);
sbCode.AppendFormat(ctorStringPK, className, t.Name);
sbCode.AppendFormat(ctorStringObject, className, t.Name);
sbCode.AppendFormat(regionEnd, "");
//sbCode.Append(Environment.NewLine);
PropertyInfo[] properties = t.GetProperties();
//if (properties.Count() > 0)
//{
sbCode.AppendFormat(regionStart, "Properties");
//sbCode.Append(Environment.NewLine);
//}
foreach (PropertyInfo p in properties)
{
if (SkipProperties.Contains(p.Name)) continue;
if (p.Name == className)
sbCode.AppendFormat(propertyValue, GetPropertyTypeName(p.PropertyType.ToString()), p.Name + "Value", p.Name);
else if (p.Name.ToLower().Contains("roms"))
sbCode.AppendFormat(propertyInternal, GetPropertyTypeName(p.PropertyType.ToString()), p.Name);
else
sbCode.AppendFormat(property, GetPropertyTypeName(p.PropertyType.ToString()), p.Name);
//sbCode.Append(Environment.NewLine);
}
sbCode.Append(isNew_Validate_Properties);
//if (properties.Count() > 0)
//{
sbCode.AppendFormat(regionEnd, "");
//sbCode.Append(Environment.NewLine);
//}
sbCode.AppendFormat(regionStart, "methods");
sbCode.Append(saveMethod);
sbCode.Append(deleteMethod.Replace("_CLASSNAME_", className));
sbCode.AppendFormat(regionEnd, "");
//Add Fetch as Collection Methods
sbCode.Append(getCollectionsMethods.Replace("_CLASSNAME_", className).Replace("_DOUBLEQUOTE_", "\""));
//Define Columns Structure
Type cols = t.GetNestedType("Columns");
if (cols != null)
{
StringBuilder sbCols = new StringBuilder(columnsStructStart);
MemberInfo[] fields = cols.GetMembers();
foreach (MemberInfo mi in fields)
{
if (mi.MemberType == MemberTypes.Field)
sbCols.AppendFormat(columnDeclaration, mi.Name);
}
sbCols.Append(columnsStructEnd);
sbCode.Append(sbCols.ToString());
}
sbCode.Append("}_NL_}_NL_");
var fileName = WriteFile(path, nsp, className, sbCode);
WriteInfo("Written file: " + fileName, ConsoleColor.Yellow);
}
private static string GetPropertyTypeName(string s)
{
if (typesMap.ContainsKey(s)) return typesMap[s];
if (s.Contains("Nullable`"))
{
s = s.Substring(s.IndexOf("[") + 1);
s = s.Substring(0, s.IndexOf("]"));
if (typesMap.ContainsKey(s))
return typesMap[s] + "?";
else
return "Nullable<" + s + ">";
}
if (s.StartsWith("System."))
return s.Substring(7);
if (s.LastIndexOf(".") > 0)
return s.Substring(s.LastIndexOf(".") + 1);
return s;
}
private static string WriteFile(string path, string nsp, string typeName, StringBuilder sbCode)
{
string filename = GetFilePath(path, nsp, typeName);
TextWriter tw = new StreamWriter(filename);
StringBuilder sb = sbCode.Replace("_BS_", "{")
.Replace("_BE_", "}")
.Replace("_NL_", Environment.NewLine)
.Replace("_DOUBLEQUOTE_", "\"");
tw.Write(sb.ToString());
tw.Flush();
tw.Close();
return filename;
}
private static string GetFilePath(string path, string nsp, string typeName)
{
path = path.EndsWith("\\") ? path : path + "\\";
path += typeName + ".cs";
return path;
}
static bool IsNullableType(Type theType)
{
return (theType.IsGenericType && theType.
GetGenericTypeDefinition().Equals
(typeof(Nullable<>)));
}
private static string[] SkipProperties = new[]{"IsLoaded", "IsNew", "IsDirty",
"TableName", "ProviderName",
"NullExceptionMessage","InvalidTypeExceptionMessage",
"LengthExceptionMessage",
"AuditId","Schema","ValidateWhenSaving","DirtyColumns","Errors"};
static IDictionary<string, string> typesMap;
static string comments = @"_NL_///<sumary>
///This class uses {0} from YOUR_DAL_NAMESPACE
///Created by MyCodeGen (YOUR_NAME) on {1}
///</sumary>_NL_";
static string dalObject = "_NL_private YOUR_DAL_NAMESPACE.{0} _dalObject;_NL_";
static string namespaceStart = "namespace {0} _NL_ _BS_ _NL_";
static string property = "public {0} {1} _NL_ _BS_ _NL_ get _BS_ return
_dalObject.{1}; _BE_ _NL_ " +
"set _BS_ _dalObject.{1} = value; _BE_ _NL_ _BE_ _NL_";
static string propertyInternal = "internal {0} {1} _NL_ _BS_ _NL_ get _BS_ return
_dalObject.{1}; _BE_ _NL_ " +
"set _BS_ _dalObject.{1} = value; _BE_ _NL_ _BE_ _NL_";
static string propertyValue = "public {0} {1} _NL_ _BS_ _NL_ get _BS_ return
_dalObject.{2}; _BE_ _NL_ " +
"set _BS_ _dalObject.{2} = value; _BE_ _NL_ _BE_ _NL_";
static string _class = "public partial class {0} _NL_ _BS_ ";
static string regionStart = "#region {0} _NL_";
static string regionEnd = "#endregion{0}_NL__NL_";
static string ctorString = "[DebuggerStepThrough]_NL_public {0}() _NL_
_BS__NL__dalObject = new YOUR_DAL_NAMESPACE.{1}(); _NL_ _BE_ _NL_";
static string ctorStringPK = "[DebuggerStepThrough]_NL_public {0}(int pk) _NL_
_BS__NL__dalObject = new YOUR_DAL_NAMESPACE.{1}(pk);_NL__BE__NL_";
static string ctorStringObject = "[DebuggerStepThrough]_NL_public
{0}(YOUR_DAL_NAMESPACE.{1} dalObject) _NL_ _BS__NL__dalObject = dalObject; _NL_
if(_dalObject==null) _NL__dalObject = new YOUR_DAL_NAMESPACE.{1}();_BE_ _NL_";
static string columnsStructStart = @"_NL_#region Columns Struct
public struct Columns
{
";
static string columnsStructEnd=@"
}
#endregion_NL_";
static string columnDeclaration = "public static string
{0}=_DOUBLEQUOTE_{0}_DOUBLEQUOTE_;_NL_";
static string saveMethod = "_NL_public bool Save() _NL__BS__NL_bool ret=IsValid;
_NL_ if(ret)_NL__dalObject.Save(); _NL_ return ret;_NL__BE__NL_";
static string deleteMethod = @"public int Delete()
{
string pkColumn=_dalObject.GetSchema().PrimaryKey.ColumnName;
object pkValue = _dalObject.GetColumnValue(pkColumn);
return ActiveRecord<Roms_CLASSNAME_>.Delete(pkValue);
}
";
static string isNew_Validate_Properties = @"/// <summary>
/// Enquiries underlying database object to know if it is persisted in
///database or not.
/// True if object has never been saved to database, false otherwise
/// </summary>
public bool IsNew
_BS_
get _BS_ return _dalObject.IsNew; _BE_
_BE_
/// <summary>
/// Validates the underlying dataobject for the lengeth, range of the
///columns defined
/// in database. Should be called before pushing object to database
///(before saving or updating).
/// </summary>
public bool IsValid
_BS_
get _BS_ return _dalObject.Validate(); _BE_
_BE_
/// <summary>
/// This string of validation error messages (<br/> seperated)
/// if the object is not valid.
/// </summary>
/// <returns>string</returns>
public string GetErrors
_BS_
get
_BS_
StringBuilder sb=new StringBuilder();
foreach (var v in _dalObject.GetErrors())
sb.AppendFormat(_DOUBLEQUOTE__BS_0_BE_<br/>_DOUBLEQUOTE_, v);
return sb.ToString();
_BE_
_BE__NL_";
static string imports =
@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using SubSonic;
using YOUR_DAL_NAMESPACE;
";
static string getCollectionsMethods = @"#region Fetch as Collection Methods
///<sumary>
///Returns the collection containing objects of type _CLASSNAME_ corresponding to
_CLASSNAME_Collection
///objects passed
///</sumary>
[DebuggerStepThrough]
public static IList<_CLASSNAME_> _CLASSNAME_s(Roms_CLASSNAME_Collection coll)
{
IList<_CLASSNAME_> list=new List<_CLASSNAME_>();
foreach(var roprof in coll)
list.Add(new _CLASSNAME_(roprof));
return list;
}
/// <summary>
/// Returns the IList<_CLASSNAME_> of _CLASSNAME_ objects
/// </summary>
/// <returns>IList<_CLASSNAME_></returns>
[DebuggerStepThrough]
public static IList<_CLASSNAME_> _CLASSNAME_s()
{
return _CLASSNAME_s(_DOUBLEQUOTE__DOUBLEQUOTE_,null);
}
/// <summary>
/// Returns the IList<_CLASSNAME_> of _CLASSNAME_ objects having
/// value of <see cref=_DOUBLEQUOTE_columnName_DOUBLEQUOTE_/> =
///<see cref=_DOUBLEQUOTE_value_DOUBLEQUOTE_/>
/// </summary>
/// <param name=_DOUBLEQUOTE_columnName_DOUBLEQUOTE_>Name of the column</param>
/// <param name=_DOUBLEQUOTE_value_DOUBLEQUOTE_>Value of the column</param>
/// <returns>IList<_CLASSNAME_></returns>
[DebuggerStepThrough]
public static IList<_CLASSNAME_> _CLASSNAME_s(string columnName, object value)
{
IList<_CLASSNAME_> collection = new List<_CLASSNAME_>();
Roms_CLASSNAME_Collection coll = null;
if (!string.IsNullOrEmpty(columnName))
{
Roms_CLASSNAME_ obj = new Roms_CLASSNAME_();
columnName = obj.GetType().GetNestedType(
_DOUBLEQUOTE_Columns_DOUBLEQUOTE_).
GetField(columnName).GetValue(obj).ToString();
}
if (!string.IsNullOrEmpty(columnName) && value != null)
{
coll = (new Roms_CLASSNAME_Collection()).Where(columnName,
value).Load();
}
else
{
coll = (new Roms_CLASSNAME_Collection()).Load();
}
if (coll != null)
foreach (var v in coll)
collection.Add(new _CLASSNAME_(v));
return collection;
}
#endregion
";
PS:- 試してみる場合は、YOUR_DAL_NAMESPACE、YOUR_BLL_NAMESPACE、YOUR_NAME のプレースホルダーに注意してください。
いいえ、いくつかのオプションがあります。生成されたテーブル クラスを部分的なクラス ファイルで拡張してロジックを追加することができます。これは、多くの小規模なアプリケーションにとって十分な場合があります。また、DTO クラスも必要になる場合があり、subsonic 3 のテーブル クラスは通常、DTO オブジェクトとして機能するようです。subsonic 3 で追加の t4 テンプレート ファイルを記述して、テーブルごとに 1 つのクラスのビジネス クラスを作成できます。コードは既存のテンプレート コードとよく似ているため、非常に簡単です。ss3 のテーブル クラスのテンプレート コードを取得し、それらを ss2 で使用してファイルを生成することもできます。それは、単純な BLL クラスのセットをどの程度生成したいかによって異なります。