Are ManyToMany relationships bi-directional?

Posts   
 
    
TomDog
User
Posts: 623
Joined: 25-Oct-2005
# Posted on: 14-Jun-2011 14:48:08   

It appears they are not:

[TestMethod, 
Description("After a prefetch of a ManyToMany relationship can I navigate to an entity at the end of that relationship then navigate back to the root entity")]
        public void BiDirectionalManyToMany()
        {
            var metaData = GetNorthwindLinqMetaData();
            var customer = metaData.Customer
                .WithPath(new PathEdge<EmployeeEntity>(CustomerEntity.PrefetchPathEmployeesViaOrders))
                .First();
            Assert.AreNotEqual(0, customer.EmployeesViaOrders.Count);

            Assert.AreEqual(1, customer.EmployeesViaOrders.First().CustomersViaOrders.Count); //Fails
        }

Version 3.1.11.225, Adapter

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 14-Jun-2011 18:04:12   

True. The reason is that when you fetch the entities, it's a readonly view on the data from that perspective. the bidirectional aspect is done by code in the collection and properties. But with m:n relationships, this isn't possible as the m:n relationship is readonly.

Say I do: customer.Orders.Add(myOrder); this modifies 'Order' by setting myOrder.Customer to customer. With m:n relationships, this would mean: customer.Employees.Add(myEmployee); -> myEmployee.Customers.Add(customer). This isn't the case, as myEmployee.Customers is readonly, so the mechanism isn't build in as it's never activated, the collection is readonly.

One could argue that because the fetch logic is performed, it is necessary to add this, but we didn't do this, as in most situations you don't need this (you only need the m:n for readonly purposes from that perspective anyway). If you need a graph navigation, use the 1:n intermediate m:1 related entity navigation, which is bi-directional (and also allows manipulation of the graph)

Do you need this for some situation which is only doable through m:n graph navigation?

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 623
Joined: 25-Oct-2005
# Posted on: 15-Jun-2011 04:30:50   

Otis wrote:

One could argue that because the fetch logic is performed, it is necessary to add this, but we didn't do this, as in most situations you don't need this (you only need the m:n for readonly purposes from that perspective anyway). If you need a graph navigation, use the 1:n intermediate m:1 related entity navigation, which is bi-directional (and also allows manipulation of the graph)

Do you need this for some situation which is only doable through m:n graph navigation?

Strange question as, correct me if I'm wrong, there is nothing you can do with m:n which you can't do with 1:n intermediate m:1, i.e. they are just there for convenience, which in my case is not so convenient.

I haven't fetched the otherwise redundant intermediate entity so I can't navigate through it.

I think I will abandon using m:n relationships and do the long handed fetching of 1:n intermediate m:1 and perhaps define a property in code on both end entities which will give the direct m:n relationship, pitydisappointed

e.g.

        public virtual IEnumerable<EmployeeEntity> EmployeesViaOrdersInCode
        {
            get { return Orders.Select(o => o.Employee); }
        }

        public virtual IEnumerable<CustomerEntity> CustomersViaOrdersInCode
        {
            get { return Orders.Select(o => o.Customer); }
        }

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 15-Jun-2011 09:57:50   

TomDog wrote:

Otis wrote:

One could argue that because the fetch logic is performed, it is necessary to add this, but we didn't do this, as in most situations you don't need this (you only need the m:n for readonly purposes from that perspective anyway). If you need a graph navigation, use the 1:n intermediate m:1 related entity navigation, which is bi-directional (and also allows manipulation of the graph)

Do you need this for some situation which is only doable through m:n graph navigation?

Strange question as, correct me if I'm wrong, there is nothing you can do with m:n which you can't do with 1:n intermediate m:1, i.e. they are just there for convenience, which in my case is not so convenient.

I haven't fetched the otherwise redundant intermediate entity so I can't navigate through it.

Fetching the intermediate entity can be less ideal in some occasions indeed (although fetching the m:n relationships will always fetch the intermediate entity data anyway, just not materialize it into entities). What I was after is why you need to navigate to the m:n related entities and back simple_smile

I think I will abandon using m:n relationships and do the long handed fetching of 1:n intermediate m:1 and perhaps define a property in code on both end entities which will give the direct m:n relationship, pitydisappointed

e.g.

        public virtual IEnumerable<EmployeeEntity> EmployeesViaOrdersInCode
        {
            get { return Orders.Select(o => o.Employee); }
        }

        public virtual IEnumerable<CustomerEntity> CustomersViaOrdersInCode
        {
            get { return Orders.Select(o => o.Customer); }
        }

From the get go till today, m:n relationships were never bidirectional, you were the first to ask about that, so it's not a use case which is considered often. The bidirectionality is built into the add / assignment of the entity and m:n relationships don't have that.

Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 623
Joined: 25-Oct-2005
# Posted on: 15-Jun-2011 13:45:04   

Otis wrote:

What I was after is why you need to navigate to the m:n related entities and back simple_smile

I have to pass the related entities to a method and inside that method I need to get a value from the 'root' entity and I can't alter the method to take the 'root' entity instead. Wierd situation as even though the schema is m:n the data is only ever 1:0..1:n. Perhaps I should add a 1:0..1 in the designer to reflect that.

Otis wrote:

From the get go till today, m:n relationships were never bidirectional, you were the first to ask about that, so it's not a use case which is considered often.

I'm new to them, what are the use cases for m:n relationships?

Jeremy Thomas
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 16-Jun-2011 11:17:11   

TomDog wrote:

Otis wrote:

From the get go till today, m:n relationships were never bidirectional, you were the first to ask about that, so it's not a use case which is considered often.

I'm new to them, what are the use cases for m:n relationships?

You have two kinds of m:n relationships: 1) the direct or 'pure' m:n relationships, where the intermediate entity only contains a PK with fk fields (e.g. Department m:n Employee, with DepartmentEmployee as intermediate entity which has as only fields DepartmentID, EmployeeID both pkfields) 2) objectified relationships, which are m:n relationships which have an intermediate entity which has non-pk fields. Example of this is Order m:n Product, with intermediate entity OrderDetails. Department m:n Employee can become an objectified relationship as well, if you have to add a 'startdate' field to the relationship, which has to be placed in the intermediate entity. This makes the intermediate entity a real entity and the relationship not pure anymore. Some O/R mappers hide 1) m:n relationships but for the reason a 1) can become a 2) we don't hide them, so your code is consistent when that happens.

Category 2 relationships not always have to be there on purpose. E.g. my example of Order - Product is a 'coincidence' (two m:1 relationships from the same intermediate entity) as the relationship itself isn't something you'd design in. The department m:n employee with a 'startdate' field on the intermediate entity is a deliberately designed relationship and therefore the m:n relationship can help viewing the related entities without having to navigate the intermediate entity. So it's a 'convenience' feature, you can always reach the related entities, the m:n relationship helps getting there easier, at the expense that the relationship is readonly.

Frans Bouma | Lead developer LLBLGen Pro