多层程序架构,反馈和/或建议

时间:2011-08-16 18:13:41

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

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

    // 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);
        }
    }

2 个答案:

答案 0 :(得分:2)

1)首先问题 - 绑定耦合。

  • FormCustomerCustomerModel
  • CustomerModel为StorageLayerCustomer
  • StorageLayer为CustomerSqlMethods
  • 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);
   }
}

答案 1 :(得分: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现在并不意味着你不会将它改成字符串,或者意识到你需要一些其他参数然后加入字符串。

你必须使用参数。