0

I'm wondering if I can use virtual properties defines in a base class (abstract) to create a link with an other concrete type.

For example :

public abstract class AbstractService
{
   public int Id {get;set;}

   public int? SiteId {get;set;}
   public virtual Site Site {get;set;}
}

public class StudyTeamService : AbstractService
{
   public int? RoleId {get;set;}
   public virtual Role Role {get;set;}
}


public abstract class AbstractSite
{
   public int Id {get;set;}

   public string Name {get;set;}
}

public class Site : AbstractSite
{
   public virtual ICollection<StudyTeamService> StudyTeamServices {get;set;}
}

I presume I have to add an annotation on the ICollection so that it know how to map correctly, but I can't find the correct one.

Do you guys have any idea ?

Seems that if I set [InverseProperty("Site")] on the ICollection, it crash with an error telling that the relation is not defined in the assembly...

Whoami
  • 334
  • 4
  • 16

1 Answers1

1

I think the problem with your code is in

public class Site : AbstractSite
{
   public virtual ICollection<StudyTeamService> StudyTeamServices {get;set;}
}

since the relationship is between Site andAbstractService, not withStudyTeamService`.

The code below works for me:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;

namespace StackOverflow
{

    public abstract class AbstractService
    {
        public int Id { get; set; }
        public virtual Site Site { get; set; }

        public int SiteId { get; set; }
    }

    [Table("StudyTeamServices")]
    public class StudyTeamService : AbstractService
    {
        public virtual Role Role { get; set; }
    }

    public class Role
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }


    public abstract class AbstractSite
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class Site : AbstractSite
    {
        public virtual ICollection<AbstractService> StudyTeamServices { get; set; }
    }

    public class Context : DbContext
    {
        static Context()
        {
            Database.SetInitializer<Context>(null);
        }

        public DbSet<AbstractService> AbstractServices { get; set; }
        public DbSet<StudyTeamService> StudyTeamServices { get; set; }
        public DbSet<Site> Sites { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //Not EdmMetadata Table on DB
            //modelBuilder.Conventions.Remove<IncludeMetadataConvention>();

            modelBuilder.Configurations.Add(new AbstractServiceMap());
        }

    }

    public class AbstractServiceMap : EntityTypeConfiguration<AbstractService>
    {
        public AbstractServiceMap()
        {
            HasRequired(a => a.Site).WithMany(s => s.StudyTeamServices).HasForeignKey(a => a.SiteId);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var context = new Context();
            context.Database.Delete();
            context.Database.Create();

            var studyTeamService = new StudyTeamService();
            studyTeamService.Role = new Role { Name = "role1" };
            studyTeamService.Site = new Site { Name = "Site1" };

            context.StudyTeamServices.Add(studyTeamService);
            context.SaveChanges();
            Console.WriteLine("Done");
            Console.ReadLine();
        }
    }
}
Arialdo Martini
  • 4,427
  • 3
  • 31
  • 42
  • In fact, I don't want the Site to have a list of AbstractService. I want the list to be "concrete typed". Since we have 5 types of services and we do't want to cast them every time we access them. That's why we want them to be of the type of the concrete class and not the abstract one. – Whoami Apr 13 '12 at 12:43
  • Then you should not have a `public virtual Site Site {get;set;}` in `AbstractService`. Put it in the concrete classes. `Site` will have several collections to those concrete classes. Once you write `public virtual Site Site {get;set;}` in `AbstractService` you are defining a relashionship between `AbstractService` and `Site` and (implicitely) a reverse relashionship between `Site` and it's collection of `AbstractService`s. – Arialdo Martini Apr 13 '12 at 12:58
  • The problem is that in the real project, we have : AbstractService, AbstractPersonService that extends AbstractService and AbstractSiteService that also extends AbstractService. And thoses classes are extended by the concrete classes. And public virtual Site Site would be in every concrete type. This is strange since this is a lot of repetition and the inheritance is made for that too... – Whoami Apr 13 '12 at 13:01
  • AFAIK there's no way to avoid this duplication (which I hate as well). Anyway, `Site` is a property of `AbstractService` it's inherited by all concrete classes (that is: each of them can link to a `Site`); furthermore, since `Site` has a collection of `AbstractService`, you can put in it any concrete classes inheriting from `AbstractService`. If so, you can use the solution I posted above. Yet, cast seems to be unavoidable. – Arialdo Martini Apr 13 '12 at 13:11
  • As we say in French : "Fichtre ! Diantre !". I was hoping this could be possible. We will have to do it this way then. Thanks for your help. – Whoami Apr 13 '12 at 13:17
  • Could you help me understanding your problem a bit better? Could you post the POCO classes you would like to have, so that I could think about the corresponding needed mapping? – Arialdo Martini Apr 13 '12 at 13:37
  • We had to change the structure so this is not relevant anymore. Thanks for your help. – Whoami Apr 25 '12 at 06:07