Table of contents
Any and HasManyToAny
There are certain cases when you need to make an association from an entity to a range of possible objects that doesn't necessarily share a common base class.
This is a fairly advanced scenario. Try to find a simpler solution if you can.
Using Any
A simple example may be a payment method in an Order class, where the choices are either a bank account or a credit card, like this:
using Castle.ActiveRecord; [ActiveRecord("CreditCards")] public class CreditCard : ActiveRecordBase, IPaymentMethod { ... } [ActiveRecord("BankAccounts")] public class BankAccount : ActiveRecordBase, IPaymentMethod { ... }
A possible Order class does not know in advance the payment map, or how to map them. They are not part of any hierarchy (either in the object model or an ActiveRecord one). The solution is to map them to this schema:
CREATE TABLE Orders ( [Id] [int] not null identity(1,1), ... [Billing_Details_Id] [int] not null, [Billing_Details_Type] [nvarchar] not null )
Together BillingDetailsId and BillingDetailsType points to the correct account or credit card that should pay for the order. Here is the attributes declarations. Note that unlike most other attributes, here you need to specify a few properties. They cannot be infered.
[Any(typeof(int), MetaType=typeof(string), TypeColumn="Billing_Details_Type", IdColumn="Billing_Details_Id", Cascade=CascadeEnum.SaveUpdate)] [Any.MetaValue("CREDIT_CARD", typeof(CreditCard))] [Any.MetaValue("BANK_ACCOUNT", typeof(BankAccount))] public IPaymentMethod PaymentMethod { get { ... } set { ... } }
The first parameter is the type of the Id column (in this case BillingDetailsId), the second is the MetaType definition, which in this case mean the type of the the field that defines the type of the id.
Next we have the TypeColumn and IdColumn, which match Billing_Details_Type and Billing_Details_Id.
The interesting part is the Any.MetaValue attribute. Here, we define that when the value in the Billing_Details_Type column is "CREDIT_CARD", the value in the Billing_Details_Id column is the primary key of a CreditCard, and when the Billing_Details_Type is BANK_ACCOUNT, then the value in Billing_Details_Id should be interpreted as the primary key of a BankAccount class.
The type of the property should be of a common type or interface that all the possible objects share (worst case scenario: make it of type System.Object).
Using HasManyToAny
A natural extention of Any, the HasManyToAnyAttribute provides the same functionality for collections. Here is an example of a class that needs a set of payment methods:
[HasManyToAny(typeof(IPayment), "pay_id", "payments_table", typeof(int), "Billing_Details_Type", "Billing_Details_Id", MetaType=typeof(string))] [Any.MetaValue("CREDIT_CARD", typeof(CreditCard))] [Any.MetaValue("BANK_ACCOUNT", typeof(BankAccount))] public ISet PaymentMethod { get { ... } set { ... } }
The parameters for HasManyToAny are (in order of apperances in the constructor):
- typeof(IPayment): the type of the objects in this collection
- pay_id: the key column that maps the values in this collection to this object
- payment_table: the table for this collection
- typeof(int): the type of the id column - identical to the first parameter of Any
- Billing_Details_Type: identical in function to the Billing_Details_Type mentioned above
- Billing_Details_Id: identical to the Billing_Details_Id mentioned above
- MetaType=typeof(string): the type of the type column / identical to the one described above
More information on the attribute can be found at Attributes article.