ekim boran
ekim boran

Reputation: 1819

Access child entities' properties from base entity

More than one year later from my first question on SO (Filter base entity from child entities' properties), I have a similar problem.

I have an abstract base type

public abstract class Base{
}

I have some child entities that inherits from this Base Type

public class Child1 : Base{
    public virtual NavigationProperty NavigationProperty {get; set; }
    public int NavigationPropertyId {get; set}
}

public class Child2 : Base{
    public virtual NavigationProperty NavigationProperty {get; set; }
}

The child entities all have NavigationProperty property. And the NavigationProperty class is like

 public class NavigationProperty{
    public virtual ICollection<Child1> Child1s {get; set;}
    public virtual Child2 Child2s {get; set;}
 }

There one-to-one mapping between Child2 and NavigationProperty; one-to-many relationship between Child1 and NavigationProperty. In order to these mapping to work i am using TPT. My first question is, can I move

  public NavigationProperty NavigationProperty {get; set; }

to Base class?

I am trying this for a whole day and did not have any success. If it is not possible can i at least access the NavigationProperty from the base type. After all the childs have this property, I tried something like

 public abstract class Base{
      public abstract NavigationProperty NavigationProperty {get; set; }
 }
 ....
 public abstract class Child2{
      public override NavigationProperty NavigationProperty {get; set; }
 }

But entity framework gives the following error.

 Sequence contains more than one matching element 

I can use something like

   public abstract class Base{
      public abstract NavigationProperty GetNavigationProperty();
   }

  public abstract class Child2{
      public override NavigationProperty NavigationProperty {get; set; }
      public override NavigationProperty GetNavigationProperty(){
           return NavigationProperty;
      }
 }

But I do not want to introduce these extra methods. Are they any way to achieve this more elegantly?

Edit:

I forgot to mention that I have already tried putting [NotMapped] attribute. I guess EF [NotMapped] attribute is inherited too so the child properties are not mapped too.

I do not expect to Linq-to-Entites to work. I do not want to be able to query base entities with navigation properties. I simply want to get rid of GetNavigationProperty and SetNavigationProperty methods. So when i try to access NavigationProperty from base class it should be loaded into memory, that's all. However, after a week of effort, I do not think it is possible.

Upvotes: 4

Views: 1534

Answers (3)

DiskJunky
DiskJunky

Reputation: 4971

EDIT x1 Updated code to avoid literal strings for property name

A little bit of reflection seems to do the job. Class setup;

public class NavigationProperty
{
    public NavigationProperty(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}

public abstract class Base
{
    public NavigationProperty NavigationProperty
    {
        get
        {
            string propertyName = MethodBase.GetCurrentMethod().Name.Replace("get_", string.Empty);
            PropertyInfo property = this.GetType().GetProperty(propertyName);
            if (property != null)
            {
                nav = (NavigationProperty)property.GetValue(this, new object[] { });
            }

            return nav;
        }
        set
        {
            string propertyName = MethodBase.GetCurrentMethod().Name.Replace("set_", string.Empty);
            PropertyInfo property = this.GetType().GetProperty(propertyName);
            if (property != null)
            {
                property.SetValue(this, value, new object[] { });
            }
        }
    }
}

public class Child1 : Base {
    public NavigationProperty NavigationProperty { get; set; }
    public int NavigationPropertyId { get; set; }
}

public class Child2 : Base{
    public NavigationProperty NavigationProperty { get; set; }
}

And in your code;

Child1 c1 = new Child1() { NavigationProperty = new NavigationProperty("child1Value") };
Child2 c2 = new Child2() { NavigationProperty = new NavigationProperty("child2Value") };
Base somebase = c1;
NavigationProperty childNav = somebase.NavigationProperty;
// childNav.Name now contains "child1Value"

Does this meet your requirements? It's a little more clunky than using an abstract method but it at least means you don't have to refactor every child class

Upvotes: 1

user22467
user22467

Reputation:

Short of finding support in Entity Framework for your scenario, you could try this:

public interface IHasNavigationProperty {
    NavigationProperty NavigationProperty { get; }
}

public class Child1 : Base, IHasNavigationProperty {
    public NavigationProperty NavigationProperty { get; set; }
}

public class Base {
    public void AMethodThatDoesStuff() {
        if (this is IHasNavigationProperty) {
            var navigationProperty = ((IHasNavigationProperty)this).NavigationProperty;

            /* do stuff with NavigationProperty */
        }
    }
}

Upvotes: 1

Slauma
Slauma

Reputation: 177153

Can I move

public NavigationProperty NavigationProperty {get; set; }

to Base class?

No, because the inverse properties in entity NavigationProperty refer to Child1 and Child2, not to Base. Navigation properties always must be properties of the declared type and cannot be moved to a base type in the inheritance chain.

For the second problem you could try to exclude the abstract navigation property from the mapping:

public abstract class Base {
    [NotMapped]
    public abstract NavigationProperty NavigationProperty {get; set; }
}

(or modelBuilder.Entity<Base>().Ignore(b => b.NavigationProperty); with Fluent API).

You won't be able though to use Base.NavigationProperty in any queries because you can't use not mapped properties with LINQ-to-Entities.

Upvotes: 1

Related Questions