Adding associated entities via actions.

Jul 2, 2012 at 11:42 AM

For example, I have a Customer, and an AddressDetail entity.

I have an action on my Customer called AddNewAddress()

I want to be able to return a new transient entity, in edit mode, of type AddressDetail that is associated with the customer when saved. Is this at all possible.

The reason is that I have a lot of validation logic on my AddressDetail entity for saving. If my AddNewAddress method has a parameter for each of the AddressDetail properties, then I have to duplicate all the validation logic in the Customer class.

Coordinator
Jul 2, 2012 at 12:08 PM

(I am assuming in all this that your Customer object is already persisted before you add the address).

If your AddressDetail object has a Customer property (which it typically would do if Customer had a collection of addresses, though it may be hidden from the user), then your method (on Customer) is simply:

public AddressDetail AddNewAddress() {
  var ad = Container.NewTransientInstance<AddressDetail>();
  ad.Customer = this;
  return ad;
}

Saving the transient AddressDetail will automatically add it to the e.g. Addresses collection on Customer.

If your AddressDetail does not have a Customer property (e.g. if Customer has only a single Address property), then I think the easiest mechanism is to add a Customer property and mark it NotPersisted (and either Disabled or Hidden).  Write the AddNewAddress method as above, and associate it back in the lifecycle method (on the AddressDetail):

public void Persisting() {
  Customer.Address = this;
}

I pretty sure (from memory) that Persisting should work for this. If it doesn't then you would need to use Persisted. 

Jul 2, 2012 at 1:22 PM

Yes, my customer is already persisted.

Between Customer and AddressDetail I have a *-* relationship. So there is a collection of each on the other.

So when i have

        public AddressDetail AddAddress ()
        {
            var ad = Container.NewTransientInstance<AddressDetail> ();
            this.AddressDetails.Add (ad);
            return ad;
        }

I get: SmdCoreModel.AddressDetail not been proxied (all properties must be made virtual/Overrideable and all collection properties should be of type ICollection<T>)

SmdCoreModel.AddressDetail not been proxied (all properties must be made virtual/Overrideable and all collection properties should be of type ICollection<T>)

Editor
Jul 2, 2012 at 1:26 PM
apols for butting in, I always worry when I see a m:n relationship, because there's probably a domain concept waiting to be found. In this case it's probably something like "Occupancy" or "Residence".

I suspect converting m:n into two 1:m and n:1 relationships might also make the persistence concerns easier.

Just my 2c.

Dan

On 2 July 2012 13:22, jfbosch <notifications@codeplex.com> wrote:

From: jfbosch

Yes, my customer is already persisted.

Between Customer and AddressDetail I have a *-* relationship. So there is a collection of each on the other.

So when i have

public AddressDetail AddAddress ()


{
var ad = Container.NewTransientInstance<AddressDetail> ();
this.AddressDetails.Add (ad);
return ad;
}

I get: SmdCoreModel.AddressDetail not been proxied (all properties must be made virtual/Overrideable and all collection properties should be of type ICollection<T>)

SmdCoreModel.AddressDetail not been proxied (all properties must be made virtual/Overrideable and all collection properties should be of type ICollection<T>)

Read the full discussion online.

To add a post to this discussion, reply to this email (nakedobjects@discussions.codeplex.com)

To start a new discussion for this project, email nakedobjects@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Coordinator
Jul 2, 2012 at 1:52 PM

"Between Customer and AddressDetail I have a *-* relationship."

It would be helpful if you'd said that in the first place  -  since that is a relatively unusual pattern!

As Dan says, you might be better off with an intervening Role object  -  after all  there must be an association table in the database for an m:n relationship to exist at all.

"So when i have

        public AddressDetail AddAddress ()
        {
            var ad = Container.NewTransientInstance<AddressDetail> ();
            this.AddressDetails.Add (ad);
            return ad;
        }

I get: SmdCoreModel.AddressDetail not been proxied (all properties must be made virtual/Overrideable and all collection properties should be of type ICollection<T>)"

Yes  -  that won't work because you are attempting to add a transient object to a persistent object and not persisting the former in the same transaction.

However, it might be possible to still use the second pattern that I gave. i.e. add a NotPersisted property to AddressDetail of type Customer, but called, for clarity, 'CreatingCustomer'.  Then in Persisting (or Persisted) call CreatingCustomer.Addresses.Add(this).

I'm not 100% sure this will work, but it is worth a try.

Jul 2, 2012 at 2:08 PM

Hi Dan.

Could you expound?

This is a CRM Module.

A customer has multiple addresses; physical and postal, and also we keep old addresses and just mark them as deleted.

We have some government regulations a verification that an address is not fraudulent, and we have an entity for this too that links to an address.

Also, an address has a 1:N relationship to an Invoice (We snapshot the address fields on the invoice, but also require the relationship for some legacy integration  steps we have to cater for)

Editor
Jul 2, 2012 at 2:22 PM


On 2 July 2012 14:08, jfbosch <notifications@codeplex.com> wrote:

From: jfbosch

Hi Dan.

Could you expound?


something like:

public class Customer {

private List<Occupancy> occupancies = ...

public void addAddress(Address addr) {
var occ = container.newTransientInstance<Occupancy>();
occ.Occupant = this;
occ.Occupied = addr;
container.persist(ref occ);
}

}


public class Occupancy {
public Customer Occupant {get;set;}
public Address Occupied {get;set;}
public bool Deleted {get; set;}
public OccupancyType Type {get;set;} // or maybe bool flags for this
}


public enum OccupancyType {
Physical,
Postal,
Both
}

public class Address { ... }




Dan

This is a CRM Module.

A customer has multiple addresses; physical and postal, and also we keep old addresses and just mark them as deleted.

We have some government regulations a verification that an address is not fraudulent, and we have an entity for this too that links to an address.

Also, an address has a 1:N relationship to an Invoice (We snapshot the address fields on the invoice, but also require the relationship for some legacy integration steps we have to cater for)

Read the full discussion online.

To add a post to this discussion, reply to this email (nakedobjects@discussions.codeplex.com)

To start a new discussion for this project, email nakedobjects@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com


Jul 2, 2012 at 6:32 PM

Richard

As Dan says, you might be better off with an intervening Role object  -  after all  there must be an association table in the database for an m:n relationship to exist at all.

Yes, but EF abstracts it away for me.

However, it might be possible to still use the second pattern that I gave. i.e. add a NotPersisted property to AddressDetail of type Customer, but called, for clarity, 'CreatingCustomer'.  Then in Persisting (or Persisted) call CreatingCustomer.Addresses.Add(this).

Awesome. Great that worked, On Persisted. Persisting wasn't so happy.

 

Dan. No, I understand the concept of the extra entity.

I was meaning that I cannot identify one in the domain that I described. Was asking if you can. :)

Editor
Jul 3, 2012 at 10:08 AM


On 2 July 2012 18:32, jfbosch <notifications@codeplex.com> wrote:

Dan. No, I understand the concept of the extra entity.

I was meaning that I cannot identify one in the domain that I described. Was asking if you can. :)


If you avoid passive words in describing the domain then the role/concept may become clearer.

You wrote: "a customer has multiple addresses; physical and postal ... "

Instead of that, what role is the address playing for the customer?

A physical address suggests to me the place that the customer occupies or resides, which is why I suggested Occupancy as this missing domain concept.

I suspect that the postal address is merely the specific physical address to send correspondence to.

The role is either a concrete type representing association, or is an interface that a concrete type implements.

You might also want to apply "Coad colors" [1] to tease these things out; distinguish the pink moment/interval (Occupancy from the blue (Address)

Not saying any of the above analysis (of an Occupancy type) is necessarily correct in your case, but hopefully this technique I'm describing here might help.

HTH
Dan

Coordinator
Jul 3, 2012 at 10:28 AM

Changing tack, though this is relevant to the discussion with Dan, I'm also interested in you have the relationship between Customer and Address is m:n rather than 1:n  i.e. that more than one customer can share an address.

I once designed an application in which addresses were treated strictly as values and were never duplicated.  It offered some business advantages, but it was considerably more complicated to implement -  because when you entered a new address you had to first check to find whether that exact address already existed in the database and, if it did, create a reference to that address rather than create a new address object.  That pattern tends to work better in places like the UK where there is a definitive publicly-accessible database of all postal addresses and where the postcode can be used as a look-up (typically to 20 addresses).  But I found it much more burdensome elsewhere.

But I don't get the impression that you are doing that exactly?  So what is your rationale for the sharing of addresses between Customers rather than having addresses being specific to each customer and allowing duplicates?  And how are you preventing accidental duplication, or don't you care?

Richard