【问题标题】:Polymorphism: Is ORM entity a Domain Entity or Data Entity?多态性:ORM 实体是域实体还是数据实体?
【发布时间】:2012-06-30 17:32:56
【问题描述】:

我有一个 BankAccount 表。 LINQ to SQL 生成一个名为“BankAccount”的类,如下所示。

[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.BankAccount")]
public partial class BankAccount : INotifyPropertyChanging, INotifyPropertyChanged

现在,作为一个新手,我自己新创建了域对象。请参阅 IBankAccount 接口和 FixedBankAccount 类。关键是存在多态行为——IBankAccount 可以是 FixedBankAccount 或 SavingsBankAccount。

对于这个示例的另一个问题,我有以下两个 cmets。

  1. @mouters:“你有存储库对象和域对象这很奇怪——你的存储库不应该只返回域对象吗?”
  2. @SonOfPirate:“存储库应使用工厂来根据从数据存储中检索到的数据创建实例。”

问题

1) 我正在手动创建域实体。这是错误的方法吗?如果错了,LINQ to SQL 类如何处理多态性?如何将方法添加到这些类中?

2) 存储库应该如何使用工厂来根据从数据存储中检索到的数据创建实例?任何代码示例或参考?

3) 是否满足单一职责原则?

代码

public interface IBankAccount
{
    int BankAccountID { get; set; }
    double Balance { get; set; }
    string AccountStatus { get; set; }
    void FreezeAccount();
    void AddInterest();
}

public class FixedBankAccount : IBankAccount
{

    public int BankAccountID { get; set; }
    public string AccountStatus { get; set; }
    public double Balance { get; set; }

    public void FreezeAccount()
    {
        AccountStatus = "Frozen";
    }

}   


public class BankAccountService
{
    RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
    ApplicationServiceForBank.IBankAccountFactory bankFactory;

    public BankAccountService(RepositoryLayer.IRepository<RepositoryLayer.BankAccount> repo, IBankAccountFactory bankFact)
    {
        accountRepository = repo;
        bankFactory = bankFact;
    }

    public void FreezeAllAccountsForUser(int userId)
    {
        IEnumerable<RepositoryLayer.BankAccount> accountsForUser = accountRepository.FindAll(p => p.BankUser.UserID == userId);
        foreach (RepositoryLayer.BankAccount oneOfRepositoryAccounts in accountsForUser)
        {
            DomainObjectsForBank.IBankAccount domainBankAccountObj = bankFactory.CreateAccount(oneOfRepositoryAccounts);
            if (domainBankAccountObj != null)
            {
                domainBankAccountObj.BankAccountID = oneOfRepositoryAccounts.BankAccountID;
                domainBankAccountObj.FreezeAccount();

                this.accountRepository.UpdateChangesByAttach(oneOfRepositoryAccounts);
                oneOfRepositoryAccounts.Status = domainBankAccountObj.AccountStatus;
                this.accountRepository.SubmitChanges();
            }

        }
    }



}


public interface IBankAccountFactory
{
    DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositoryAccount);
}


public class MySimpleBankAccountFactory : IBankAccountFactory
{
    //Is it correct to accept repositry inside factory?
    public DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositoryAccount)
    {
        DomainObjectsForBank.IBankAccount acc = null;

        if (String.Equals(repositoryAccount.AccountType, "Fixed"))
        {
            acc = new DomainObjectsForBank.FixedBankAccount();
        }

        if (String.Equals(repositoryAccount.AccountType, "Savings"))
        {
            //acc = new DomainObjectsForBank.SavingsBankAccount();
        }

        return acc;
    }
}

正在阅读

  1. Setting Foreign keys in Linq to SQL

  2. Polymorphic associations in LINQ to SQL

  3. Confusion between DTOs (linq2sql) and Class objects!

  4. LINQ-to-XYZ polymorphism?

【问题讨论】:

    标签: c# .net nhibernate linq-to-sql domain-driven-design


    【解决方案1】:
    1. 我不完全确定您对 LINQ to SQL 的要求是什么,但手动创建域对象肯定不是错误的方法。如果您依赖生成代码的域对象,您将无法获得正确封装的域对象。
    2. 您对工厂模式感到困惑。正如mouters 所指出的,您有两个代表同一事物的对象:

      RepositoryLayer.BankAccount

      DomainObjectsForBank.IBankAccount

    只有在对象创建需要“策略”时才需要工厂。使用它们的一个经典案例是多态性和继承。您的帐户类有子类,因此有一个 AccountFactory 的案例。但是,情况过于复杂的地方是让存储库返回某种帐户数据对象,然后将其传递给工厂以将其转换为正确的子类域对象。相反,存储库应该从数据库中获取数据,将其传递给工厂,然后从工厂返回创建的域对象。例如:

    public class AccountRepository : IAccountRepository
    {
        public Account GetById(Guid id)
        {
            using (AccountContext ctx = new AccountContext())
            {
                var data = (from a in ctx.Accounts
                                   where a.AccountId == id
                                   select new { AccountId = a.AccountId, CustomerId = a.CustomerId, Balance = a.Balance, AccountType = (AccountType)a.AccountTypeId }).First();
    
    
                return _accountFactory.Create(data.AccountId, data.CustomerId, data.Balance, data.AccountType);
            }
        }
    
    }
    

    更新

    我对 LINQ to SQL 的建议是:

    1. 如果可以,请移至实体框架,因为它更先进,现在得到更好的支持。
    2. 不要将它生成的对象用作您的域对象。如果您查看上面的代码,我会从我的 ctx.Accounts 中查询数据并使用它来实例化我的域对象。根据我的经验,尝试使用 ORM 来构建域对象是有问题的:请参阅 Rich domain model with behaviours and ORM

    【讨论】:

    • 谢谢大卫。现在我明白了.. 还有一个问题......在接下来的两篇文章中,它提到了在 L2S 生成的类中实现 IAccount(通过添加新的部分类)这不是违反 SRP 吗? stackoverflow.com/questions/1323070/…stackoverflow.com/questions/4467074/linq-to-xyz-polymorphism
    • @Lijo Davids 的回答正是我认为它应该起作用的方式。
    • 我已经用我对 L2S 的想法更新了我的答案。但老实说,我仍然不明白您对 SRP 的要求。
    • @Lijo - 顺便说一下 - 您可能需要考虑接受百分比。您有很多未接受的问题。
    • 好的,我现在明白你的问题了。不,它们不会有两个职责,因为对象它们自己 不负责持久性……L2S 上下文对象提供持久性功能。这使责任分开。
    猜你喜欢
    • 2013-08-09
    • 2021-10-31
    • 1970-01-01
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多