Polymorphism: Is ORM entity a Domain Entity or Data Entity?
I have a BankAccount table. LINQ to SQL generates a class named “BankAccount” as shown below.
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.BankAccount")]
public partial class BankAccount : INotifyPropertyChanging, INotifyPropertyChanged
Now, being a newbie, I am newly creating the domain objects myself. Please see IBankAccount interface and FixedBankAccount class. The key point is there is polymorphic behavior – the IBankAccount can be FixedBankAccount or SavingsBankAccount.
For a different question with this example, I have following two comments.
- @mouters : “Its weird that you have repository objects and domain objects - should your repository not just return the domain objects?”
- @SonOfPirate : “The factory should be used by the repository to create the instance based on the data retrieved from the data store.”
QUESTIONS
1) I am manually creating domain entities. Is it wrong approach? If it is wrong, how the LINQ to SQL classes handle the polymorphism? How to add the methods to those classes?
2) How the factory should be used by the repository to create the instance based on the data retrieved from the data store? Any code example or reference ?
3) Will it satisfy Single Responsibility Principle?
CODE
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;
}
}
READING:
Setting Foreign keys in Linq to SQL
Polymorphic associations in LINQ to SQL
Confusion between DTOs (linq2sql) and Class objects!
LINQ-to-XYZ polymorphism?
Solution 1:
- I'm not entirely sure what you are asking regarding LINQ to SQL, but it's certainly not the wrong approach to manually create you domain objects. You won't get properly encapsulated domain objects if you rely on them being code generated.
-
You have got yourself in a muddle regarding the factory pattern. As mouters has pointed out, you have two objects representing the same thing:
RepositoryLayer.BankAccount
DomainObjectsForBank.IBankAccount
Factories are only required when a 'strategy' is required for object creation. A classic case for their use is polymorphism & inheritance. Your account class has sub classes, so there is a case for an AccountFactory. But where you have over complicated the situation is by having the repository return some kind of account data object, then passing this to the factory to convert it to the correct sub classed domain object. Instead, the repository should get the data from the database, pass it to the factory, and then return the created domain object from the factory. For example:
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);
}
}
}
UPDATE:
My Advice regarding LINQ to SQL is:
- Move to Entity Framework if you can as it's more advanced and is better supported now.
- Don't use it's generated objects as your domain objects. If you look at the code above, I query the data from my ctx.Accounts and use it to instantiate my domain objects. In my experience trying to use and ORM to build domain objects is problematic: see Rich domain model with behaviours and ORM