Новая награда 2017/10/31
Авто-принятый ответ, к сожалению, не работает с моей текущей моделью сущности из-за ограничений TPC. Мне крайне необходимо найти способ облегчить двустороннюю навигацию через интерфейс или абстрактный класс, поэтому я начинаю еще одну награду.
Обратите внимание, что я должен использовать существующий дизайн модели, поэтому рефакторинг не является вариантом.
Оригинальный вопрос ниже
У меня есть родительский объект, который имеет отношение один к одному с несколькими возможными таблицами (FK находится на дочерних таблицах). Поскольку свойство навигации для дочернего элемента определяется интерфейсом, у меня нет навигации к другому концу отношения.
Я понимаю, что это естественное ограничение, но по-прежнему ищут средство для достижения двунаправленной навигации при использовании абстрактных типов или дженериков. Я столкнулся с рядом вопросов, похожих на то, что я хочу сделать, но они либо довольно старые, либо я не думаю, что они точно соответствуют тому, чего я пытаюсь достичь. Я ищу более актуальный ответ, характерный для моей дилеммы.
Это мой код и его можно легко скопировать/вставить в тестовое приложение:
Изменить (в ответ на ответ Ивана Стоева): Когда я попытался реализовать ваше решение, я получил эту ошибку при попытке создать миграцию:
The association 'SoftwareApplicationData_CreatedBy' between entity types 'SoftwareApplicationData' and 'AppUser' is invalid. In a TPC hierarchy independent associations are only allowed on the most derived types.
Итак, мне кажется, что мне нужно отредактировать исходный код, чтобы отразить более сложную модель, которую я первоначально пропустил для краткости. Мои извинения, поскольку я не думал, что дополнительный код будет актуальным до сих пор.
Обратите внимание, что я сделал все сущности, которые теперь наследуются от MyEntity
.
Редактирование конца
public abstract class MyEntity
{
public int Id { get; set; }
public AppUser CreatedBy { get; set; }
}
public class AppUser : MyEntity { }
public interface ISoftwareApplicationData
{
SoftwareApplicationBase Application { get; set; }
}
//Parent entity representing a system installation and the software installed on it.
//The collection property is *not* the generic entity I mentioned earlier.
public class SystemConfiguration : MyEntity
{
public ICollection<SoftwareApplicationBase> Applications { get; set; }
}
//Represents the software itself. Has other generic attributes that I've ommitted for brevity.
//The Data property represents additional, application-specific attributes. I need to be able
//to navigate from SoftwareApplicationBase to whatever may be on the other end
public class SoftwareApplicationBase : MyEntity
{
public SystemConfiguration Configuration { get; set; }
public string ApplicationName { get; set; }
public ISoftwareApplicationData Data { get; set; }
}
//This is a generic, catch-all application class that follows a basic Application/Version
//convention. Most software will use this class
public class SoftwareApplication : MyEntity, ISoftwareApplicationData
{
public SoftwareApplicationBase Application { get; set; }
public string Version { get; set; }
}
//Operating systems have special attributes, so they get their own class.
public class OperatingSystem : MyEntity, ISoftwareApplicationData
{
public SoftwareApplicationBase Application { get; set; }
public string Version { get; set; }
public string ServicePack { get; set; }
}
//Yet another type of software with its own distinct attributes
public class VideoGame : MyEntity, ISoftwareApplicationData
{
public SoftwareApplicationBase Application { get; set; }
public string Publisher { get; set; }
public string Genre { get; set; }
}
Одним из решений, которое я имею в виду, является создание метода, который передаст делегат GetById в коллекцию репозиториев объектов, которые реализуют ISoftwareApplicationData
. Мне не нравится идея делать GetById внутри итераций, но, вероятно, будет только когда-либо пять типов, для которых мне нужно это сделать, поэтому это надежное решение не справляется со всем остальным.