How to access a property from another aggregate? - aggregate

I have two aggregates Order and Customer. Order has a identity reference to customer.
public class Customer
{
public long CustomerId { get; private set; }
public string Name { get; private set; }
public Address Address { get; private set; }
...
}
public class Order
{
public long OrderId { get; private set; }
public long CustomerId { get; private set; }
public Date OrderDate { get; private set; }
...
public string CustomerName { get; priate set; } //from Customer Aggregate
...
public void AssignCustomer(Customer customer)
{
this.CustomrName = customer.Name
}
}
As you see in Order aggregate I need CustomerName plus identity reference, and for this reason I added a method AssignCustomer to set it from outside of Order aggregate.
Is this approach OK from DDD perspective?
If that's accepted, I have another problem. Suppose in OrderRepository there is a method that returns list of Orders like this :
public interface IOrderRepository
{
IList<Order> GetAllOrderWithDate(Date date);
...
}
Now if I want to set CustomerName for all these orders, the N+1 problem would occur, cause I need to read related Customer for each Order in list.
Is there any proper solution in DDD?

Related

I am not able to have one-to-many relationship in Entity Framework

I am following examples from the internet but it's not working. The database is getting created successfully, there is no error.
What I want to have is: one user can have multiple transactions, and a transaction can have references to two users. One of those is the user who did the transaction, the second is the user to whom transaction is done.
But what is happening is I am getting three foreign keys in the Users table, but none in the Transactions table.
See image below:
My classes
public class User
{
public int userId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string CardNumber { get; set; }
public string Password { get; set; }
public int Balance { get; set; }
public string UserType { get; set; }
public string ProfileUrl { get; set; }
public IList<Transaction> Transactions { get; set; }
}
public class Transaction
{
public Transaction()
{
this.TranscationDateTime = DateTime.UtcNow;
}
public int TransactionId { get; set; }
public int Amount { get; set; }
public User FromUser { get; set; }
public User ToUser { get; set; }
public DateTime TranscationDateTime { get; set; }
}
public class DB: DbContext
{
public DB() : base("name=DBConnection")
{ }
public DbSet<User> Users { get; set; }
public DbSet<Transaction> Transactions { get; set; }
}
You need to make some modification to your code.
First of all, each navigation property needs to be marked as virtual, in order to allow Entity Framework to lazy loading, unless you want always eager load all your navigations (could be a choice, is up to you).
After that, each of your user has outgoing and incoming transactions, so for the User class:
public class User
{
public int userId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string CardNumber { get; set; }
public string Password { get; set; }
public int Balance { get; set; }
public string UserType { get; set; }
public string ProfileUrl { get; set; }
public virtual IList<Transaction> IncomingTransactions { get; set; }
public virtual IList<Transaction> OutgoingTransactions { get; set; }
}
Let's make virtual navigation properties of Transaction class
public class Transaction
{
public Transaction()
{
this.TranscationDateTime = DateTime.UtcNow;
}
public int TransactionId { get; set; }
public int Amount { get; set; }
public virtual User FromUser { get; set; }
public virtual User ToUser { get; set; }
public DateTime TranscationDateTime { get; set; }
}
Last, but not least, let's inform your DbContext of how things are supposed to go:
public class MyContext : DbContext
{
public MyContext(string connectionString) : base(connectionString) { }
public DbSet<User> Users { get; set; }
public DbSet<Transaction> Transactions { get; set; }
protected override void OnModelCreating(DbModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Transaction>()
.HasRequired<User>(t => t.FromUser)
.WithMany(u => u.OutgoingTransactions).WillCascadeOnDelete(false);
builder.Entity<Transaction>()
.HasRequired<User>(t => t.ToUser)
.WithMany(u => u.IncomingTransactions).WillCascadeOnDelete(false);
}
}
This should be enough for EF autodiscovery to make the right assumptions and create right database structure, that would be two FKs in Transaction table each of them to the primary key of Users table.
And voilà:
This happens because EF doesn't know that one of the FromUser and ToUser fields is supposed to match the collection Transactions - since you are not following the naming conventions. You have several options on how to resolve this situation:
If you only want to match Transactions collection with either FromUser or ToUser but not both, you can use [ForeignKey] and/or [InverseProperty] attributes to setup the database relation explicitly
If you want to use BOTH of them, then you would need to specify two collections in the User class - e.g. TransactionsFromUser and TransactionsToUser. You might still need to setup the relationships explicitly through the attributes though
What i want to have is one user can have multiple transaction but a transaction can have reference to two user.
Your current database model reflects this accuratly. I will explain why in the rest of my answer.
The User table can not hold the foreign keys to the Transactions table because one User can be associated with multiple Transactions. If the FK column was on the User table, it would need to hold more than one TransactionId.
Instead, the references to the Users are stored in the Transaction table. So every Transaction only has to store a single UserId per FK column.
Transaction.User_userId tells us that this Transaction is in the IList<Transaction> Transactions of the User with the stored User_userId.
To get this list of Transactions for a certain user, we query
SELECT *
FROM Transactions t
INNER JOIN Users u on t.User_userId = u.userId
WHERE u.userId = {theUserId}
The additional FKs ToUser_userId and FromUser_userId exists because they might reference different Users.
If the semantics of the IList<Transaction> Transactions is actually "all transactions that originated from this User", you could configure the ModelBuilder to use the FromUser_userId FK for this collection instead of creating the third FK User_userId. See the answer of Sergey.

Foreign key with OR logic

I have CodeFirst design like this:
public class Email
{
public string Address { get; set; }
public Company Company { get; set; }
public int? CompanyId { get; set; }
public User User { get; set; }
public int? UserId { get; set; }
//many other props
}
public class Company
{
public List<Email> Emails { get; set; }
}
public class User
{
public List<Email> Emails { get; set; }
}
In a good way, Email can belong to only one foreign key: CompanyId or UserId. But now it allows CompanyId and UserId. It's wrong. Anyway, that design with nullables is ugly. For example, to get all emails linked to companies I need do this:
var companyEmails = _context.Emails.Where(x => x.CompanyId.HasValue);
I feel there is a better approach to define multiply foreign keys with OR logic. Please, help me find a way.
If you want to have the only one reference to Company or User, that mean it will not be FK and also you should have additional field with description, to which table this field points.
Alternatively, you can try Table per Hierarchy approach. At this case database table will remain almost the same, only new Discriminator column will be implicitly added, to distinguish classes, but you will can to write more "elegant" code:
public abstract class Email
{
public string Address { get; set; }
}
public class CompanyEmail : Email
{
public Company Company { get; set; }
public int? CompanyId { get; set; }
}
public class UserEmail : BaseEmail
{
public User User { get; set; }
public int? UserId { get; set; }
}
Usage:
var companyEmails = _context.Emails.OfType<CompanyEmail>();
//underlying query:
//select * from dbo.Emails where Discriminator = 'CompanyEmail'

SQLite-Net Extensions: How to get rid of relationship records?

Let's say I have a many-to-many relationship like in the official sample:
public class Student
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[ManyToMany(typeof(StudentSubject))]
public List<Student> Students { get; set; }
}
public class Subject
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Description { get; set; }
[ManyToMany(typeof(StudentSubject))]
public List<Subject> Subjects { get; set; }
}
public class StudentSubject
{
[ForeignKey(typeof(Student))]
public int StudentId { get; set; }
[ForeignKey(typeof(Subject))]
public int SubjectId { get; set; }
}
If I am now deleting a Student, the relationship record represented by the intermediate object does not get deleted, too. (And if I am enabling cascading deletion, the Subject gets deleted – what should not happen.)
Currently I am cleaning up manually by deleting the record with a custom query, but is there a better way with sqlite-net?
You can set to null or an empty list the Subjects property and call UpdateWithChildren on the student object.
student.Subjects = null;
conn.UpdateWithChildren(student);
It will update the student object and its relationships, deleting all the records for that student, which is equivalent to:
conn.Execute("DELETE FROM StudentSubject WHERE StudentId = ?", studentId);
The drawback is that you have one more 'update' statement executing in the database if you let SQLite-Net Extensions handle the relationship.

Advanced TPH Mapping to Legacy Database

I have been working on a project in which I am trying to mold entity framework to an existing FoxPro 2.x database in order to use the data while leaving the tables readable to a legacy application (more details on my previous question).
I've had pretty good luck configuring the DBContext to the physical data tables and I have most of my mapping set up. The legacy data structure has a Bills table with a unique primary Id key, but all the LineItems that can be posted to a bill are stored in a single Charges table without a simple primary key.
My question pertains to discriminator mapping in code-first EF. I am recreating the table as TPH in my data objects, so I have
public abstract class Posting
{
public System.DateTime? Post_Date { get; set; }
public string Bill_Num { get; set; }
public string Type { get; set; }
public string Pcode { get; set; }
public string Pdesc { get; set; }
public decimal? Custid { get; set; }
public string Createby { get; set; }
public System.DateTime? Createdt { get; set; }
public string Createtm { get; set; }
public string Modifyby { get; set; }
public System.DateTime? Modifydt { get; set; }
public string Modifytm { get; set; }
public string Linenote { get; set; }
public decimal? Version { get; set; }
public string Id { get; set; }
public string Batch { get; set; }
public virtual Billing Bill { get; set; }
}
public abstract class Charge : Posting
{
}
public class ServiceLine : Charge
{
public string Chargeid { get; set; }
public virtual ICollection<Payment> Payments { get; set; }
}
public class ChargeVoid : Charge
{
}
public abstract class Payment : Posting
{
}
public class PaymentLine : Payment
{
public string Postid { get; set; }
public string Svc_Code { get; set; }
public string Name { get; set; }
public string Checkno { get; set; }
public System.DateTime? Checkdate { get; set; }
}
public class PaymentVoid : Payment
{
}
where my mapping strategy so far is along these lines:
public class PostingMap : EntityTypeConfiguration<Posting>
{
public PostingMap()
{
// Primary Key
this.HasKey(t => new {t.Bill_Num, t.Post_Date, t.Pcode});
this.Map<Charge>(m => m.Requires("Type").HasValue("C"))
.ToTable("Charges");
this.Map<Payment>(m => m.Requires("Type").HasValue("P"))
.ToTable("Charges");
}
}
I have omitted some fields and mapping classes, but this is the core of it.
Every record has the C/P classification, so this makes everything in the table either a Charge or a Payment.
Every Posting is associated with a Bill via Bill_Num foreign key.
The ServiceLine object is only distinct from ChargeVoid objects (which are adjustment entries and no-value information entries associated with a bill) by having values for Pcode and Chargeid (which is just Bill_Num tagged with 01++). I have no idea how to model this.
It is very similar for the Payment hierarchy as well.
So with my current setup, I have Postings which doesn't have a unique key, Charges which has a subset of ServiceLines with values for Chargeid and Pcode and a subset with nulls, and Payments similar to Charges. PaymentLines are also many-to-one with ServiceLines by way of Pcode while PaymentVoids have Pcode = null.
Is there a way I can assign this complex mapping since I can't simply discriminate on !null? On top of that, will EF handle the key assignments once I get the inheritance set up, or am I going to have issues there as well?
Also, if there is a better way to break this object inheritance down, I am all ears.

Mapping Many to many in Entity framework

simply I ask this How to Map , How to ProductCustomer in the sample ??
public class ProductCustomer
{
public virtual Product Product { get; set; }
public virtual Customer Customer { get; set; }
}
and about Product and Customer :
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
}
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public decimal Amount { get; set; }
}
thanks!
You don't need to create the ProductCustomer object.
In EF, you create your Customer and Product, and then you create collections to each. This will automatically create the proper link tables.
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
public virtual List<Product> Products {get;set;}
}
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public decimal Amount { get; set; }
public virtual List<Customer> Customers {get;set;}
}
This is only the case, however, if your link table has no payload (has no additional data). If it does, then you will need to create the link table as an entity similar to what you originally did, but you add 1:many links in your product and customer classes to the link entity. You then have to modify your queries to query through the link table.