3

我使用 Subsonic 生成了一个不错的 DAL。有没有办法可以为 BLL 生成骨架?我不想将 SS 层直接插入我的 GUI。

我已经在两个 SS 论坛中跋涉,似乎每个人都SSS 生成的层称为 DAL,但他们其用作 BLL。

您是否使用过 SS 并将 DAL 和 BLL 层分开,而无需从头开始手动编码 BLL?

4

2 回答 2

1

是的,我使用单独的 DAL 和 BLL,除非项目非常小并且业务逻辑不多。

你是对的,在 BAL 中为 DAL 属性执行所有实现真的很烦人,并且破坏了 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 (&lt;br/&gt; 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&lt;_CLASSNAME_&gt; of _CLASSNAME_ objects 
    /// </summary>
    /// <returns>IList&lt;_CLASSNAME_&gt;</returns>
    [DebuggerStepThrough]
    public static IList<_CLASSNAME_> _CLASSNAME_s()
    {
        return _CLASSNAME_s(_DOUBLEQUOTE__DOUBLEQUOTE_,null);
    }

    /// <summary>
    /// Returns the IList&lt;_CLASSNAME_&gt; 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&lt;_CLASSNAME_&gt;</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 占位符。

于 2009-06-11T06:10:04.387 回答
1

不,但有一些选择。您可以使用部分类文件扩展生成的表类以添加更多逻辑,这对于许多较小的应用程序可能已经足够了。您可能还需要 DTO 类,并且 subsonic 3 的表类通常似乎作为 DTO 对象工作。您可以在 subsonic 3 中编写额外的 t4 模板文件来创建业务类,每个表一个类。代码将很像现有的模板代码,所以应该很容易。您甚至可以在 ss3 中获取表类的模板代码,并在 ss2 中使用它们来生成文件。这取决于您想要生成一组简单的 BLL 类的程度。

于 2009-06-11T06:18:26.853 回答