【问题标题】:Multi tier program architecture, feedback and/or suggestions [closed]多层程序架构,反馈和/或建议[关闭]
【发布时间】:2011-10-28 07:38:03
【问题描述】:

我正在规划一个程序的结构,并希望使用多个层级。 您认为我的方法好还是您有其他建议?

    // The Form is the View + Controller (Windows Forms standard behaviour, don't want to change it)
    class FormCustomer
    {
        CustomerModel _customerModel;
        void LoadCustomer()
        {
            Customer c = _customerModel.ReadCustomer(tbCustomer.Text);
            ShowCustomer(c);
        }
    }

    // The Model-Layer is for business Logic
    class CustomerModel
    {
        StorageLayer _StorageLayer;        
        public Customer ReadCustomer(int id)
        {
            if (id < 0) throw new Exception("Invalid id");
            Customer c = _StorageLayer.ReadCustomer(id);
            if (c == null) throw new Exception("Customer not found");
            return c;
        }
    }

    // The StorageLayer ist a facade to all storage Methods
    // See http://en.wikipedia.org/wiki/Facade_pattern for more details
    class StorageLayer
    {
        SqlMethods _sqlMethods;
        public Customer ReadCustomer(int id)
        {
            return _sqlMethods.ReadCustomer(id)
        }
    }

    // The SqlMethods is one class (or maybe several classes) which contain
    // all the sql operations.
    class SqlMethods
    {
        public Customer ReadCustomer(int id)
        {
            string sql = "Select c.*, a.* From customers c left join addresses a where c.id = " + id; // Not optimized, just an example
            IDataReader dr = ExecuteStatement(sql);
            return FetchCustomer(dr);
        }
    }

【问题讨论】:

标签: c# .net architecture n-tier-architecture


【解决方案1】:

1) 第一个问题 - 绑定耦合。

  • FormCustomerCustomerModel
  • CustomerModel 到 StorageLayer, Customer
  • StorageLayer 到 Customer, SqlMethods
  • TODO:在构建阶段引入接口并注入依赖
 // Now you don't need StorageLayer, basically it would be IDataService
 // rModel class should provide a some kind of business logic otherwise it just 
 // wrapping with zero value a IDataService and will have 
 // a mess introducing model class per entity
 public sealed class CustomerModel 
 {
    private readonly IDataService 

    // now you can inject any an other data service without model changes
    // XmlDataService, WebDataService, etc
    public CustomerModel (IDataService dataService)
    {
        this.dataService = dataService;
    }

    public ICustomer GetCustomer(int id)
    {
        if (id < 0) 
        {
           throw new ArgumentException("id", 
                                  "Id should be equal or greater than zero");
        }

        ICustomer c = this.dataService.SelectEntity<Customer>(id);
        // ...
    }
 }

2) 第二 - 尝试使用泛型,因此每次您需要一个新实体以及客户(如帐户等)时,您至少可以重用基础架构的主要部分。

TODO:考虑将查询与实体分离的设计(也许有时它不会是 SQL 查询?)

public interface IDbEntity 
{
}

public interface IDbContract
{
      string SelectByIdQuery { get; }
}

public sealed class DataBaseContractFactory
{
    public IDbContract CreateContract<TEntity>()
      where TEntity: IDbEntity
    {
       if (typeof(TEntity) == typeof(ICustomer))
       {
           return this.customerDbContract;
       }
    }
}

public sealed class SqlDataService: IDataService
{
   public SqlDataService(DataBaseContractFactory dbContractFactory)
   {
        // assign to private field
   }

   public T SelectEntity<TEntity>(int entityId)
      where TEntity: IDbEntity
   {
       IDbContract contract = this.dbContractFactory.CreateContract<TEntity>();

       // Consider using more safe way of query building:
       // adapter.SelectCommand.Parameters.Add(
       // "@Id", SqlDbType.Int).Value = id;
       string sqlQuery = String.Format(contract.SelectByIdQuery, id);

       IDataReader dataReader = ExecuteStatement(sqlQuery);
       return this.BuildEntytyFromDataReader<TEntity>(dataReader);
   }
}

【讨论】:

    【解决方案2】:
    string sql = "Select c.*, a.* From customers c left
                        join addresses a where c.id = " + id; 
    // Not optimized, just an example IDataReader dr
    = ExecuteStatement(sql);
    

    永远不要出于任何原因这样做。这是完全不能接受的。它不能用于测试,不能用于原型。作为开发人员出于任何原因这样做都是值得惩罚的。

    这是最好的 sql 注入。

    仅仅因为这是一个 int 现在并不意味着您不会将其更改为字符串,或者意识到您需要一些其他参数然后加入字符串。

    您必须使用参数。

    【讨论】:

    • +1 但是,砍手太宽松了。我认为缓慢痛苦的死亡更合适。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-19
    • 2012-03-15
    • 2010-10-07
    • 1970-01-01
    • 2011-06-10
    相关资源
    最近更新 更多