设计模式学习日记三(持续更新)

本篇介绍Domain Model模式,一个银行领域建模,涉及帐号的创建以及帐号之间的现金转账的小程序。

1. POCO 和 PI

前面提到过,Domain Model 与 Active Record 模式不同,它不知道持久化。术语持久化(persistence ignorance,PI)表示普通CLR对象(plain old common runtime object,POCO)业务实体的朴实本质。通常Repository模式(后续中将会介绍该模式)将 Domain Model 的业务对象持久化。

2. 示例程序

创建名为Terminator.Practice.DomainModel的解决方案,并添加下面的类库项目:

Terminator.Practice.DomainModel.Model -- 包含应用程序内所有的业务逻辑。领域对象将存放在此处,并与其他对象建立关系,从而表示应用程序正则构建的银行领域。该项目还将以接口的形式为领域对象持久化和检索定义契约,采用Repository模式来实现所有的持久化管理需求。Model项目不会引用其他任何项目,从而确保:让它与任何基础设施关注点保持隔离,只关注业务领域。

Terminator.Practice.DomainModel.AppService -- 充当应用程序的接口(API)。表示层将通过消息(简单的数据传输对象,后续中将会介绍消息传送模式)与 AppService 通信。AppService 还将定义视图模型,这些是领域模型的展开视图,只用于数据显示(后续中将会更详细的介绍该方式)。

Terminator.Practice.DomainModel.Repository -- 包含Model项目中定义的资源库接口的具体实现。Repository引用了Model项目,从而从数据库提取并持久化领域对象,Repository项目只关注领域对象持久化和检索的责任。

添加一个新的Web应用程序 Terminator.Practice.DomainModel.UI.Web -- Web项目负责应用程序的表示和用户体验需求,这个项目只与

AppService交互,并接收专门为用户体验视图创建的强类型视图模型。

 

对Repository项目添加对Model项目的引用,AppService项目添加对Model和Repository项目的引用,Web项目添加对AppService项目的引用。

创建名为BankAccount.mdf的数据库,创建两张表:

BankAccounts表
列名数据类型是否允许空
BankAccountIduniqueidentifier,Primary keyFalse
BalancemoneyFalse
CustomerRefnvarchar(50)False

 

 

 

 

 

Transactions表
列名数据类型是否允许空
BankAccountIduniqueidentifierFalse
DepositmoneyFalse
WithdrawalmoneyFalse
Referencenvarchar(50)False
DatedatetimeFalse

 

 

 

 

 

现在开始领域建模,在这个场景中,BankAccount为发生的每个动作创建一个Transaction对象,类似于操作日志的功能。

在Model项目中添加一个新类Transaction:

Transaction类代码
 1 using System;
 2 
 3 namespace Terminator.Practice.DomainModel.Model
 4 {
 5     public class Transaction
 6     {
 7         public Transaction(decimal deposit, decimal withdrawal, string reference, DateTime date)
 8         {
 9             Deposit = deposit;
10             Withdrawal = withdrawal;
11             Reference = reference;
12             Date = date;
13         }
14 
15         /// <summary>
16         /// 存款金额
17         /// </summary>
18         public decimal Deposit
19         { get; internal set; }
20         /// <summary>
21         /// 取出金额
22         /// </summary>
23         public decimal Withdrawal
24         { get; internal set; }
25 
26         public string Reference
27         { get; internal set; }
28 
29         public DateTime Date
30         { get; internal set; }
31     }
32 }

注:Transaction对象被称为值对象,这是DDD(领域驱动设计)中的一个术语,后续中将会介绍DDD。

添加第二个类 BankAccount:

BankAccount类代码
 1 using System;
 2 using System.Collections.Generic;
 3 
 4 namespace Terminator.Practice.DomainModel.Model
 5 {
 6     public class BankAccount
 7     {
 8         private readonly IList<Transaction> _transactions;
 9 
10         public BankAccount() : this(Guid.NewGuid(), 0, new List<Transaction>(), "")
11         {
12             _transactions.Add(new Transaction(0m, 0m, "account created", DateTime.Now));
13         }
14 
15         public BankAccount(Guid Id, decimal balance, IList<Transaction> transactions, string customerRef)
16         {
17             AccountNo = Id;
18             Balance = balance;
19             _transactions = transactions;
20             CustomerRef = customerRef;
21         }
22 
23         public Guid AccountNo { get; internal set; }
24         /// <summary>
25         /// 账户金额
26         /// </summary>
27         public decimal Balance { get; internal set; }
28 
29         public string CustomerRef { get; set; }
30 
31         /// <summary>
32         /// 是否可以取出某个金额
33         /// </summary>
34         /// <param name="amount"></param>
35         /// <returns></returns>
36         public bool CanWithdraw(decimal amount)
37         {
38             return (Balance >= amount);
39         }
40 
41         /// <summary>
42         /// 取出某金额
43         /// </summary>
44         /// <param name="amount"></param>
45         /// <param name="reference"></param>
46         public void Withdraw(decimal amount, string reference)
47         {
48             if (!CanWithdraw(amount))
49             {
50                 throw new InsufficientFundsException();
51             }
52             Balance -= amount;
53             _transactions.Add(new Transaction(0m, amount, reference, DateTime.Now));
54         }
55 
56         /// <summary>
57         /// 存款
58         /// </summary>
59         /// <param name="amount"></param>
60         /// <param name="reference"></param>
61         public void Deposit(decimal amount, string reference)
62         {
63             Balance += amount;
64             _transactions.Add(new Transaction(amount, 0m, reference, DateTime.Now));
65         }
66 
67         public IEnumerable<Transaction> GetTransactions()
68         {
69             return _transactions;
70         }
71     }   
72 }

BankAccount类有3个简单的方法:CanWithdraw,Withdraw,Deposit。因为有一个CanWithdraw方法,所以应该期望调用代码在尝试从某个帐号取出现金时使用Test-Doer(先测试再执行)模式:

if(myBankAccount.CanWithdraw(amount))
{
    myBankAccount.Withdraw(amount);
}

如果在不进行检查的情况下对一个没有足够现金金额的帐号使用Withdraw方法,则应该抛出一个异常,为此,定义一个异常类,向Model项目添加InsufficientFundsException类:

using System;

namespace Terminator.Practice.DomainModel.Model
{
    public class InsufficientFundsException : ApplicationException 
    {
    }
}

现在需要某种方法来持久化BankAccount和Transaction,因为不希望污染Domain Model项目,所以添加Repository的接口来定义契约,以满足实体的持久化和检索需求,这就是前面提到的PI和POCO的概念。

创建一个带有如下契约的接口IBankAccountRepository:

using System;
using System.Collections.Generic;

namespace Terminator.Practice.DomainModel.Model
{
    public interface IBankAccountRepository
    {
        void Add(BankAccount bankAccount);
        void Save(BankAccount bankAccount);
        IEnumerable<BankAccount> FindAll();
        BankAccount FindBy(Guid AccountId);
    }
}

向Model项目中添加BankAccountService类:

BankAccountService类代码
 1 using System;
 2 
 3 namespace Terminator.Practice.DomainModel.Model
 4 {
 5     public class BankAccountService
 6     {
 7         private readonly IBankAccountRepository _bankAccountRepository;
 8 
 9         public BankAccountService(IBankAccountRepository bankAccountRepository)
10         {
11             _bankAccountRepository = bankAccountRepository;
12         }
13         
14         public void Transfer(Guid accountNoTo, Guid accountNoFrom, decimal amount)
15         {
16             BankAccount bankAccountTo = _bankAccountRepository.FindBy(accountNoTo);
17             BankAccount bankAccountFrom = _bankAccountRepository.FindBy(accountNoFrom);
18 
19             if (!bankAccountFrom.CanWithdraw(amount))
20             {
21                 throw new InsufficientFundsException();
22             }
23             bankAccountTo.Deposit(amount, "From Acc " + bankAccountFrom.CustomerRef + " ");
24             bankAccountFrom.Withdraw(amount, "Transfer To Acc " + bankAccountTo.CustomerRef + " ");
25 
26             _bankAccountRepository.Save(bankAccountTo);
27             _bankAccountRepository.Save(bankAccountFrom);
28         }      
29     }
30 }

在BankAccountService 的当前实现中,在保存两个银行帐号之间发生的任何错误均会让数据处于非法状态,因此我们需要一个事务保护机制(本篇没有这么做),后续中将会介绍UoW(工作单元模式),它能够确保所有的事务作为一个原子动作进行提交,万一出现异常就回滚。

现在可以编写方法来持久化BankAccount和Transaction对象,在Repository项目添加一个BankAccountRepository类,该类将实现接口IBankAccountRepository:

BankAccountRepository类代码
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Data.SqlClient;
  4 using System.Data;
  5 using System.Configuration;
  6 using Terminator.Practice.DomainModel.Model;
  7 
  8 namespace Terminator.Practice.DomainModel.Repository
  9 {
 10     public class BankAccountRepository : IBankAccountRepository
 11     {
 12         private readonly string _connectionString;
 13 
 14         public BankAccountRepository()
 15         {
 16             _connectionString = ConfigurationManager.ConnectionStrings["BankAccountConnectionString"].ConnectionString;
 17         }
 18 
 19         public void Add(BankAccount bankAccount)
 20         {
 21             string insertSql = @"INSERT INTO BankAccounts(BankAccountID, Balance, CustomerRef)
 22                                             VALUES(@BankAccountID, @Balance, @CustomerRef)";
 23             using (SqlConnection connection = new SqlConnection(_connectionString))
 24             {
 25                 SqlCommand command = connection.CreateCommand();
 26                 command.CommandText = insertSql;
 27 
 28                 SetCommandParametersForInsertUpdateTo(bankAccount, command);
 29 
 30                 connection.Open();
 31 
 32                 command.ExecuteNonQuery();
 33             }
 34 
 35             UpdateTransactionsFor(bankAccount);
 36         }
 37 
 38         public void Save(BankAccount bankAccount)
 39         {
 40             string bankAccoutnUpdateSql = "UPDATE BankAccounts " +
 41                                  "SET Balance = @Balance, CustomerRef= @CustomerRef " +
 42                                  "WHERE BankAccountID = @BankAccountID;";
 43 
 44             using (SqlConnection connection = new SqlConnection(_connectionString))
 45             {
 46                 SqlCommand command = connection.CreateCommand();
 47                 command.CommandText = bankAccoutnUpdateSql;
 48 
 49                 SetCommandParametersForInsertUpdateTo(bankAccount, command);
 50 
 51                 connection.Open();
 52 
 53                 command.ExecuteNonQuery();
 54             }
 55 
 56             UpdateTransactionsFor(bankAccount);
 57         }
 58 
 59         private static void SetCommandParametersForInsertUpdateTo(BankAccount bankAccount, SqlCommand command)
 60         {
 61             command.Parameters.Add(new SqlParameter("@BankAccountID", bankAccount.AccountNo));
 62             command.Parameters.Add(new SqlParameter("@Balance", bankAccount.Balance));
 63             command.Parameters.Add(new SqlParameter("@CustomerRef", bankAccount.CustomerRef));
 64         }
 65 
 66         private void UpdateTransactionsFor(BankAccount bankAccount)
 67         {
 68             string deleteTransactionSQl = "DELETE Transactions WHERE BankAccountId = @BankAccountId;";
 69 
 70             using (SqlConnection connection = new SqlConnection(_connectionString))
 71             {
 72                 SqlCommand command = connection.CreateCommand();
 73                 command.CommandText = deleteTransactionSQl;
 74                 command.Parameters.Add(new SqlParameter("@BankAccountID", bankAccount.AccountNo));
 75                 connection.Open();
 76                 command.ExecuteNonQuery();
 77             }
 78 
 79             string insertTransactionSql = "INSERT INTO Transactions " +
 80                                  "(BankAccountID, Deposit, Withdrawal, Reference, [Date]) VALUES " +
 81                                  "(@BankAccountID, @Deposit,  @Withdrawal,  @Reference, @Date)";
 82 
 83             foreach (Transaction tran in bankAccount.GetTransactions())
 84             {
 85                 using (SqlConnection connection = new SqlConnection(_connectionString))
 86                 {
 87                     SqlCommand command = connection.CreateCommand();
 88                     command.CommandText = insertTransactionSql;
 89 
 90                     command.Parameters.Add(new SqlParameter("@BankAccountID", bankAccount.AccountNo));
 91                     command.Parameters.Add(new SqlParameter("@Deposit", tran.Deposit));
 92                     command.Parameters.Add(new SqlParameter("@Withdrawal", tran.Withdrawal));
 93                     command.Parameters.Add(new SqlParameter("@Reference", tran.Reference));
 94                     command.Parameters.Add(new SqlParameter("@Date", tran.Date));
 95 
 96                     connection.Open();
 97                     command.ExecuteNonQuery();
 98                 }
 99             }
100         }
101 
102         public IEnumerable<BankAccount> FindAll()
103         {
104             IList<BankAccount> accounts;
105 
106             string queryString = "SELECT * FROM dbo.Transactions INNER JOIN " +
107                                  "dbo.BankAccounts ON dbo.Transactions.BankAccountId = dbo.BankAccounts.BankAccountId " +
108                                  "ORDER BY dbo.BankAccounts.BankAccountId;";
109 
110             using (SqlConnection connection = new SqlConnection(_connectionString))
111             {
112                 SqlCommand command = connection.CreateCommand();
113                 command.CommandText = queryString;
114 
115                 connection.Open();
116 
117                 using (SqlDataReader reader = command.ExecuteReader())
118                 {
119                     accounts = CreateListOfAccountsFrom(reader);
120                 }
121             }
122             return accounts;
123         }
124 
125         private IList<BankAccount> CreateListOfAccountsFrom(IDataReader datareader)
126         {
127             IList<BankAccount> accounts = new List<BankAccount>();
128             string id = "";
129             IList<Transaction> transactions = new List<Transaction>();
130 
131             while (datareader.Read())
132             {
133                 if (id != datareader["BankAccountId"].ToString())
134                 {
135                     id = datareader["BankAccountId"].ToString();
136                     transactions = new List<Transaction>();
137                     BankAccount bankAccount = new BankAccount(new Guid(id), Decimal.Parse(datareader["Balance"].ToString()), transactions, datareader["CustomerRef"].ToString());
138                     accounts.Add(bankAccount);
139                 }
140                 transactions.Add(CreateTransactionFrom(datareader));
141             }
142 
143             return accounts;
144         }
145 
146         private Transaction CreateTransactionFrom(IDataRecord rawData)
147         {
148             return new Transaction(Decimal.Parse(rawData["Deposit"].ToString()),
149                                    Decimal.Parse(rawData["Withdrawal"].ToString()),
150                                    rawData["Reference"].ToString(),
151                                    DateTime.Parse(rawData["Date"].ToString()));
152         }
153 
154 
155         public BankAccount FindBy(Guid AccountId)
156         {
157             BankAccount account;
158 
159             string queryString = "SELECT * FROM dbo.Transactions INNER JOIN " +
160                                  "dbo.BankAccounts ON dbo.Transactions.BankAccountId = dbo.BankAccounts.BankAccountId " +
161                                  "WHERE dbo.BankAccounts.BankAccountId = @BankAccountId;";
162 
163             using (SqlConnection connection = new SqlConnection(_connectionString))
164             {
165                 SqlCommand command = connection.CreateCommand();
166                 command.CommandText = queryString;
167 
168                 SqlParameter Idparam = new SqlParameter("@BankAccountId", AccountId);
169                 command.Parameters.Add(Idparam);
170 
171                 connection.Open();
172 
173                 using (SqlDataReader reader = command.ExecuteReader())
174                 {
175                     account = CreateListOfAccountsFrom(reader)[0];
176                 }
177             }
178             return account;
179         }
180     }
181 }

由于Repository项目需要访问web.config,所以需要添加对System.Configuration的引用。

注:在后续中将介绍一些常见的对象关系映射器,能够节省编写ADO.NET基础设施代码的时间

现在可以为客户端添加一个服务层,让其与系统已一种简单的方式进行交互。

在AppService项目中添加一个新文件夹 ViewModel,并向其中添加两个类BankAccountView 和 TransactionView:

using System;
using System.Collections.Generic;

namespace Terminator.Practice.DomainModel.AppService.ViewModels
{
    public class BankAccountView
    {
        public Guid AccountNo { get; set; }
        public string Balance { get; set; }
        public string CustomerRef { get; set; }
        public IList<TransactionView> Transactions { get; set; } 
    }
}
using System;

namespace Terminator.Practice.DomainModel.AppService.ViewModels
{
    public class TransactionView
    {
        public string Deposit { get; set; }
        public string Withdrawal { get; set; }
        public string Reference { get; set; }
        public DateTime Date { get; set; }
    }
}

BankAccountView 和 TransactionView 提供了领域模型用于表示的展开视图,为了将领域实体转换成数据传输视图模型,需要映射器类(后续中将会介绍一种将这个过程自动化的方法)。

在AppService项目根目录创建一个新类ViewMapper:

using System.Collections.Generic;
using Terminator.Practice.DomainModel.AppService.ViewModels;
using Terminator.Practice.DomainModel.Model;

namespace Terminator.Practice.DomainModel.AppService
{
    public static class ViewMapper
    {
        public static TransactionView CreateTransactionViewFrom(Transaction tran)
        {
            return new TransactionView
            {
                Deposit = tran.Deposit.ToString("C"),
                Withdrawal = tran.Withdrawal.ToString("C"),
                Reference = tran.Reference,
                Date = tran.Date
            };
        }

        public static BankAccountView CreateBankAccountViewFrom(BankAccount acc)
        {
            return new BankAccountView
            {
                AccountNo = acc.AccountNo,
                Balance = acc.Balance.ToString("C"),
                CustomerRef = acc.CustomerRef,
                Transactions = new List<TransactionView>()
            };
        }
    }
}

向AppService项目中再添加一个文件夹Messages,该文件夹将包含所有用来与服务层通信的Request-Reponse模式对象,因为所有的Reponse都共享一组相同的属性,所以可以创建一个基类ResponseBase:

namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public abstract class ResponseBase
    {
        public bool Success { get; set; }
        public string Message { get; set; }
    }
}

Success属性表示被调用的方法是否成功运行,而Message属性包含该方法运行结果的详细信息。

在Messages文件夹添加几个类:BankAccountCreateRequest、BankAccountCreateResponse、DepositRequest、FindAllBankAccountResponse、FindBankAccountResponse、TransferRequest、TransferResponse、WithdrawalRequest

namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public class BankAccountCreateRequest 
    {        
        public string CustomerName { get; set; }
    }
}
using System;

namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public class BankAccountCreateResponse : ResponseBase 
    {        
        public Guid BankAccountId { get; set; }
    }
}
using System;

namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public class DepositRequest
    {
        public Guid AccountId { get; set; }
        public decimal Amount { get; set; }
    }
}
using System.Collections.Generic;
using Terminator.Practice.DomainModel.AppService.ViewModels;

namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public class FindAllBankAccountResponse : ResponseBase 
    {
        public IList<BankAccountView> BankAccountView { get; set; }
    }
}
using Terminator.Practice.DomainModel.AppService.ViewModels;

namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public class FindBankAccountResponse : ResponseBase 
    {
        public BankAccountView BankAccount { get; set; }
    }
}
using System;

namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public class TransferRequest
    {
        public Guid AccountIdTo { get; set; }
        public Guid AccountIdFrom { get; set; }
        public decimal Amount { get; set; }
    }
}
namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public class TransferResponse : ResponseBase
    {        
    }
}
using System;

namespace Terminator.Practice.DomainModel.AppService.Messages
{
    public class WithdrawalRequest
    {
        public Guid AccountId { get; set; }
        public decimal Amount { get; set; }
    }
}

消息传送对象就位之后,就可以添加服务类来协调对领域实体(服务和资源库)的方法调用,在AppService项目的根目录添加一个新类ApplicationBankAccountService:

ApplicationBankAccountService类代码
  1 using System;
  2 using System.Collections.Generic;
  3 using Terminator.Practice.DomainModel.AppService.Messages;
  4 using Terminator.Practice.DomainModel.AppService.ViewModels;
  5 using Terminator.Practice.DomainModel.Model;
  6 using Terminator.Practice.DomainModel.Repository;
  7 
  8 namespace Terminator.Practice.DomainModel.AppService
  9 {
 10     public class ApplicationBankAccountService
 11     {
 12         private readonly BankAccountService _bankAccountService;
 13         private readonly IBankAccountRepository _bankRepository;
 14 
 15         public ApplicationBankAccountService() :
 16             this(new BankAccountRepository(), new BankAccountService(new BankAccountRepository()))
 17         { }
 18 
 19         public ApplicationBankAccountService(IBankAccountRepository bankRepository, BankAccountService bankAccountService)
 20         {
 21             _bankRepository = bankRepository;
 22             _bankAccountService = bankAccountService;
 23         }
 24 
 25         public ApplicationBankAccountService(BankAccountService bankAccountService, IBankAccountRepository bankRepository)
 26         {
 27             _bankAccountService = bankAccountService;
 28             _bankRepository = bankRepository;
 29         }
 30 
 31         public BankAccountCreateResponse CreateBankAccount(BankAccountCreateRequest bankAccountCreateRequest)
 32         {
 33             BankAccountCreateResponse bankAccountCreateResponse = new BankAccountCreateResponse();
 34             BankAccount bankAccount = new BankAccount();
 35 
 36             bankAccount.CustomerRef = bankAccountCreateRequest.CustomerName;
 37             _bankRepository.Add(bankAccount);
 38 
 39             bankAccountCreateResponse.BankAccountId = bankAccount.AccountNo;
 40             bankAccountCreateResponse.Success = true;
 41 
 42             return bankAccountCreateResponse;
 43         }
 44 
 45         public void Deposit(DepositRequest depositRequest)
 46         {
 47             BankAccount bankAccount = _bankRepository.FindBy(depositRequest.AccountId);
 48 
 49             bankAccount.Deposit(depositRequest.Amount, "");
 50 
 51             _bankRepository.Save(bankAccount);
 52         }
 53 
 54         public void Withdrawal(WithdrawalRequest withdrawalRequest)
 55         {
 56             BankAccount bankAccount = _bankRepository.FindBy(withdrawalRequest.AccountId);
 57 
 58             bankAccount.Withdraw(withdrawalRequest.Amount, "");
 59 
 60             _bankRepository.Save(bankAccount);
 61         }
 62 
 63         public TransferResponse Transfer(TransferRequest request)
 64         {
 65             TransferResponse response = new TransferResponse();
 66 
 67             try
 68             {
 69                 _bankAccountService.Transfer(request.AccountIdTo, request.AccountIdFrom, request.Amount);
 70                 response.Success = true;
 71             }
 72             catch (InsufficientFundsException)
 73             {
 74                 response.Message = "There is not enough funds in account on: " + request.AccountIdFrom.ToString();
 75                 response.Success = false;
 76             }
 77             return response;
 78         }
 79 
 80         public FindAllBankAccountResponse GetAllBankAccounts()
 81         {
 82             FindAllBankAccountResponse FindAllBankAccountResponse = new FindAllBankAccountResponse();
 83             IList<BankAccountView> bankAccountViews = new List<BankAccountView>();
 84             FindAllBankAccountResponse.BankAccountView = bankAccountViews;
 85 
 86             foreach (BankAccount acc in _bankRepository.FindAll())
 87             {
 88                 bankAccountViews.Add(ViewMapper.CreateBankAccountViewFrom(acc));
 89             }
 90 
 91             return FindAllBankAccountResponse;
 92         }
 93 
 94         public FindBankAccountResponse GetBankAccountBy(Guid Id)
 95         {
 96             FindBankAccountResponse bankAccountResponse = new FindBankAccountResponse();
 97             BankAccount acc = _bankRepository.FindBy(Id);
 98             BankAccountView bankAccountView = ViewMapper.CreateBankAccountViewFrom(acc);
 99 
100             foreach (Transaction tran in acc.GetTransactions())
101             {
102                 bankAccountView.Transactions.Add(ViewMapper.CreateTransactionViewFrom(tran));
103             }
104 
105             bankAccountResponse.BankAccount = bankAccountView;
106 
107             return bankAccountResponse;
108         }
109     }
110 }

ApplicationBankAccountService类协调应用程序活动并将所有的业务任务委托给领域模型,该层并不包含任何业务逻辑,有助于防止任何与业务无关的代码污染领域模型项目,该层还将领域实体转换成数据传输对象,从而保护领域的内部操作,并为一起工作的表示层提供了一个易于使用的API。

为了简单起见,这里选择了简陋的依赖注入方式并对默认的构造器硬编码,以便使用已经编码的资源库领域服务实现,后续中将介绍IoC和IoC容器来提供类的依赖关系。

最后就是创建用户界面,从而能够创建帐号并执行交易。

Default.aspx页面
 1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Terminator.Practice.DomainModel.UI.Web._Default" %>
 2 
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 4 
 5 <html xmlns="http://www.w3.org/1999/xhtml" >
 6 <head runat="server">
 7     <title></title>
 8 </head>
 9 <body>
10     <form id="form1" runat="server">
11     <div>
12      
13      <fieldset>
14         <legend>Create New Account</legend>    
15      <p>        
16         Customer Ref:
17         <asp:TextBox ID="txtCustomerRef" runat="server"></asp:TextBox>
18                 
19         <asp:Button ID="btCreateAccount" runat="server" Text="Create Account" 
20             οnclick="btCreateAccount_Click" />
21     </p>
22     </fieldset>
23     
24     <fieldset>
25         <legend>Account Detail</legend> 
26         <p>
27         <asp:DropDownList AutoPostBack="true" ID="ddlBankAccounts" runat="server" 
28                 onselectedindexchanged="ddlBankAccounts_SelectedIndexChanged"></asp:DropDownList>
29         </p>        
30         <p>
31             Account No:
32             <asp:Label ID="lblAccountNo" runat="server" />
33         </p>
34         <p>
35             Customer Ref:
36             <asp:Label ID="lblCustomerRef" runat="server" />
37         </p>
38         <p>
39             Balance:
40             <asp:Label ID="lblBalance" runat="server" />
41         </p>
42         <p>
43             Amount
44         ¥<asp:TextBox ID="txtAmount" runat="server" Width="60px"></asp:TextBox>
45                 
46         &nbsp;<asp:Button ID="btnWithdrawal" runat="server" Text="Withdrawal" 
47                 οnclick="btnWithdrawal_Click" />
48         &nbsp;<asp:Button ID="btnDeposit" runat="server" Text="Deposit" 
49                 οnclick="btnDeposit_Click" />
50         </p>
51         <p>
52             Transfer
53         ¥<asp:TextBox ID="txtAmountToTransfer" runat="server" Width="60px"></asp:TextBox>
54                 
55         &nbsp;to
56         <asp:DropDownList AutoPostBack="true" ID="ddlBankAccountsToTransferTo" runat="server"></asp:DropDownList>
57         &nbsp;<asp:Button ID="btnTransfer" runat="server" Text="Commit" 
58                 οnclick="btnTransfer_Click" />
59         </p>
60         <p>
61             Transactions</p>
62             <asp:Repeater ID="rptTransactions" runat="server">        
63                 <HeaderTemplate>
64                     <table>
65                     <tr>
66                         <td>deposit</td>
67                         <td>withdrawal</td>
68                         <td>reference</td>
69                     </tr>
70                 </HeaderTemplate> 
71                 <ItemTemplate>
72                     <tr>
73                         <td><%# Eval("Deposit")  %></td>
74                         <td><%# Eval("Withdrawal")  %></td>
75                         <td><%# Eval("Reference")  %></td>                
76                         <td><%# Eval("Date")  %></td>
77                     </tr>
78                 </ItemTemplate> 
79                 <FooterTemplate>
80                     </table>
81                 </FooterTemplate>   
82             </asp:Repeater>
83     </fieldset>    
84     </div> 
85     </form>
86 </body>
87 </html>
Default.aspx.cs代码
  1 using System;
  2 using System.Web.UI.WebControls;
  3 using Terminator.Practice.DomainModel.AppService;
  4 using Terminator.Practice.DomainModel.AppService.Messages;
  5 using Terminator.Practice.DomainModel.AppService.ViewModels;
  6 
  7 namespace Terminator.Practice.DomainModel.UI.Web
  8 {
  9     public partial class _Default : System.Web.UI.Page
 10     {
 11         protected void Page_Load(object sender, EventArgs e)
 12         {
 13             if (!Page.IsPostBack)
 14                 ShowAllAccounts();
 15         }
 16 
 17         private void ShowAllAccounts()
 18         {
 19             ddlBankAccounts.Items.Clear();  
 20 
 21             FindAllBankAccountResponse response = new ApplicationBankAccountService().GetAllBankAccounts();
 22             ddlBankAccounts.Items.Add(new ListItem("Select An Account", ""));
 23 
 24             foreach (BankAccountView accView in response.BankAccountView)
 25             {
 26                 ddlBankAccounts.Items.Add(new ListItem(accView.CustomerRef, accView.AccountNo.ToString()));
 27             }
 28         }
 29 
 30         protected void btCreateAccount_Click(object sender, EventArgs e)
 31         {
 32             BankAccountCreateRequest createAccountRequest = new BankAccountCreateRequest();
 33             createAccountRequest.CustomerName = txtCustomerRef.Text;           
 34             ApplicationBankAccountService service = new ApplicationBankAccountService();
 35 
 36             service.CreateBankAccount(createAccountRequest);   
 37          
 38             ShowAllAccounts();
 39         }
 40 
 41         protected void ddlBankAccounts_SelectedIndexChanged(object sender, EventArgs e)
 42         {
 43             DisplaySelectedAccount();
 44         }
 45 
 46         private void DisplaySelectedAccount()
 47         {
 48             if (ddlBankAccounts.SelectedValue != "")
 49             {
 50                 ApplicationBankAccountService service = new ApplicationBankAccountService();
 51                 FindBankAccountResponse response = service.GetBankAccountBy(new Guid(ddlBankAccounts.SelectedValue));
 52                 BankAccountView accView = response.BankAccount;
 53 
 54                 lblAccountNo.Text = accView.AccountNo.ToString();
 55                 lblBalance.Text = accView.Balance;
 56                 lblCustomerRef.Text = accView.CustomerRef;
 57 
 58                 rptTransactions.DataSource = accView.Transactions;
 59                 rptTransactions.DataBind();
 60 
 61                 FindAllBankAccountResponse allAccountsResponse = service.GetAllBankAccounts();
 62 
 63                 ddlBankAccountsToTransferTo.Items.Clear();
 64 
 65                 foreach (BankAccountView acc in allAccountsResponse.BankAccountView)
 66                 {
 67                     if (acc.AccountNo.ToString() != ddlBankAccounts.SelectedValue)
 68                         ddlBankAccountsToTransferTo.Items.Add(new ListItem(acc.CustomerRef, acc.AccountNo.ToString()));
 69                 }
 70             }
 71         }
 72 
 73         protected void btnWithdrawal_Click(object sender, EventArgs e)
 74         {
 75             ApplicationBankAccountService service = new ApplicationBankAccountService();
 76             WithdrawalRequest request = new WithdrawalRequest();
 77             Guid AccId = new Guid(ddlBankAccounts.SelectedValue);
 78             request.AccountId = AccId;
 79             request.Amount = Decimal.Parse(txtAmount.Text);
 80 
 81             service.Withdrawal(request);
 82             DisplaySelectedAccount();
 83         }
 84 
 85         protected void btnDeposit_Click(object sender, EventArgs e)
 86         {
 87             ApplicationBankAccountService service = new ApplicationBankAccountService();
 88             DepositRequest request = new DepositRequest(); 
 89             Guid AccId = new Guid(ddlBankAccounts.SelectedValue);
 90             request.AccountId = AccId;
 91             request.Amount = Decimal.Parse(txtAmount.Text);
 92 
 93             service.Deposit(request);
 94             DisplaySelectedAccount();
 95         }
 96 
 97         protected void btnTransfer_Click(object sender, EventArgs e)
 98         {
 99             ApplicationBankAccountService service = new ApplicationBankAccountService();
100             TransferRequest request = new TransferRequest();            
101             request.AccountIdFrom = new Guid(ddlBankAccounts.SelectedValue);
102             request.AccountIdTo = new Guid(ddlBankAccountsToTransferTo.SelectedValue);
103             request.Amount = Decimal.Parse(txtAmountToTransfer.Text);
104 
105             service.Transfer(request);
106             DisplaySelectedAccount();
107             
108         }
109     }
110 }
Default.aspx.designer.cs代码
  1 //------------------------------------------------------------------------------
  2 // <自动生成>
  3 //     此代码由工具生成。
  4 //
  5 //     对此文件的更改可能会导致不正确的行为,并且如果
  6 //     重新生成代码,这些更改将会丢失。 
  7 // </自动生成>
  8 //------------------------------------------------------------------------------
  9 
 10 namespace Terminator.Practice.DomainModel.UI.Web {
 11     
 12     
 13     public partial class _Default {
 14         
 15         /// <summary>
 16         /// form1 控件。
 17         /// </summary>
 18         /// <remarks>
 19         /// 自动生成的字段。
 20         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 21         /// </remarks>
 22         protected global::System.Web.UI.HtmlControls.HtmlForm form1;
 23         
 24         /// <summary>
 25         /// txtCustomerRef 控件。
 26         /// </summary>
 27         /// <remarks>
 28         /// 自动生成的字段。
 29         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 30         /// </remarks>
 31         protected global::System.Web.UI.WebControls.TextBox txtCustomerRef;
 32         
 33         /// <summary>
 34         /// btCreateAccount 控件。
 35         /// </summary>
 36         /// <remarks>
 37         /// 自动生成的字段。
 38         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 39         /// </remarks>
 40         protected global::System.Web.UI.WebControls.Button btCreateAccount;
 41         
 42         /// <summary>
 43         /// ddlBankAccounts 控件。
 44         /// </summary>
 45         /// <remarks>
 46         /// 自动生成的字段。
 47         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 48         /// </remarks>
 49         protected global::System.Web.UI.WebControls.DropDownList ddlBankAccounts;
 50         
 51         /// <summary>
 52         /// lblAccountNo 控件。
 53         /// </summary>
 54         /// <remarks>
 55         /// 自动生成的字段。
 56         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 57         /// </remarks>
 58         protected global::System.Web.UI.WebControls.Label lblAccountNo;
 59         
 60         /// <summary>
 61         /// lblCustomerRef 控件。
 62         /// </summary>
 63         /// <remarks>
 64         /// 自动生成的字段。
 65         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 66         /// </remarks>
 67         protected global::System.Web.UI.WebControls.Label lblCustomerRef;
 68         
 69         /// <summary>
 70         /// lblBalance 控件。
 71         /// </summary>
 72         /// <remarks>
 73         /// 自动生成的字段。
 74         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 75         /// </remarks>
 76         protected global::System.Web.UI.WebControls.Label lblBalance;
 77         
 78         /// <summary>
 79         /// txtAmount 控件。
 80         /// </summary>
 81         /// <remarks>
 82         /// 自动生成的字段。
 83         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 84         /// </remarks>
 85         protected global::System.Web.UI.WebControls.TextBox txtAmount;
 86         
 87         /// <summary>
 88         /// btnWithdrawal 控件。
 89         /// </summary>
 90         /// <remarks>
 91         /// 自动生成的字段。
 92         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
 93         /// </remarks>
 94         protected global::System.Web.UI.WebControls.Button btnWithdrawal;
 95         
 96         /// <summary>
 97         /// btnDeposit 控件。
 98         /// </summary>
 99         /// <remarks>
100         /// 自动生成的字段。
101         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
102         /// </remarks>
103         protected global::System.Web.UI.WebControls.Button btnDeposit;
104         
105         /// <summary>
106         /// txtAmountToTransfer 控件。
107         /// </summary>
108         /// <remarks>
109         /// 自动生成的字段。
110         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
111         /// </remarks>
112         protected global::System.Web.UI.WebControls.TextBox txtAmountToTransfer;
113         
114         /// <summary>
115         /// ddlBankAccountsToTransferTo 控件。
116         /// </summary>
117         /// <remarks>
118         /// 自动生成的字段。
119         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
120         /// </remarks>
121         protected global::System.Web.UI.WebControls.DropDownList ddlBankAccountsToTransferTo;
122         
123         /// <summary>
124         /// btnTransfer 控件。
125         /// </summary>
126         /// <remarks>
127         /// 自动生成的字段。
128         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
129         /// </remarks>
130         protected global::System.Web.UI.WebControls.Button btnTransfer;
131         
132         /// <summary>
133         /// rptTransactions 控件。
134         /// </summary>
135         /// <remarks>
136         /// 自动生成的字段。
137         /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
138         /// </remarks>
139         protected global::System.Web.UI.WebControls.Repeater rptTransactions;
140     }
141 }

 web.config数据库连接字符串配置

  <connectionStrings>
    <add name="BankAccountConnectionString"
             connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\BankAccount.mdf;Integrated Security=True;User Instance=True"
             providerName="System.Data.SqlClient"/>
  </connectionStrings>

解决方案结构图

程序运行图:

最后附上demo源码

 

转载于:https://www.cnblogs.com/error500/archive/2013/01/06/2846549.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值