Наследование и отношения «многие ко многим» - PullRequest
0 голосов
/ 06 марта 2011

У меня есть следующая бизнес-роль, которую мне нужно смоделировать:

  • Участник торгов может оценивать продавца, если он взаимодействует с этим человеком

  • Участник аукциона может оценить предмет, только если он выиграл аукцион

  • Окончательный рейтинг продавца, однако, является средним значением, полученным из рейтинга предмета и другихоценки на себя.

  • Сам рейтинг (будь то предмет или пользователь) - это среднее количество баллов по нескольким вопросам.

СоответственноЯ подумал, что должен создать класс Ratings, а затем наследовать его с помощью UserRating и ItemRating.Оба из них должны иметь ICollection of RatingQuestion (который в конечном итоге будет статической таблицей).Вопросы для UserRating отличаются от вопросов ItemRating, но я подумал, что на самом деле не стоит создавать отдельные таблицы / сущности для вопросов (или, может быть, я должен сделать наследование TPH?).

Итак, вот чтоЯ получил так далеко:

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

    public virtual User By { get; set; }
}

public class UserRating : Rating
{
    public virtual User For { get; set; }

    public virtual ICollection<RatingQuestion> Questions { get; set; }  
}


public class ItemRating : Rating
{
    public virtual Item For { get; set; }

    public virtual ICollection<RatingQuestion> Questions { get; set; }
}


public class RatingQuestion
{
    public int Id { get; set; }

    public string Text { get; set; }

    public virtual ICollection<Rating> Rating { get; set; }
}

Причина, по которой я помещаю ICollection в подклассы, а не в базовый класс Rating, заключается в том, что RatingQuestion для обоих отличается, но я не уверен, что этокак я должен это делать, поправьте меня, если я ошибаюсь, пожалуйста.

Одна вещь, с которой мне нужна помощь, - это решить, выбрать ли TPH или наследование TPT.Я хочу, чтобы все было просто, но я бы также хотел, чтобы моя база данных была нормализована.Кроме того, производительность является фактором, который необходимо принимать во внимание.

Теперь последнее, что мне нужно знать, это то, как это сделать: как сопоставить отношения «многие ко многим» между классами рейтинга (базовым классом или подклассами, не зная, каким я должен бытьиспользуя) и класс RatingQuestion с помощью Fluent API И добавьте атрибут (оценка), который является свойством самой взаимосвязи, чтобы я мог записывать оценку для каждого отдельного RatingQuestion.

Надеюсь, это было достаточно ясно.Все предложения приветствуются.Заранее спасибо!

ОБНОВЛЕНИЕ: (после ответа Ладислава Мрнки)

public abstract class Rating
{
    public int Id { get; set; }
    public virtual User By { get; set; }
    public virtual ICollection<RatingQuestion> RatingQuestions { get; set; }
}

public class UserRating : Rating
{
    public virtual User For { get; set; }
    public virtual ICollection<Question> Questions { get; set; }
}

public class ItemRating : Rating
{
    public virtual Item For { get; set; }
    public virtual ICollection<Question> Questions { get; set; }
}

public class User 
{
    public int Id { get; set; }
    //more properties
    public virtual ICollection<UserRating> OwnRatings { get; set; }
    public virtual ICollection<UserRating> RatingsForOthers { get; set; }
    public virtual ICollection<ItemRating> ItemRatings { get; set; }
}

public class Item
{
    public int Id { get; set; }
    //more properties
    public virtual ItemRating Rating { get; set; } //because an Item will have only one rating 
}

public class UserRatingConfiguration : EntityTypeConfiguration<UserRating>
{
    public UserRatingConfiguration()
    {
        HasOptional(p => p.By)
            .WithMany(u => u.RatingsForOthers)
            .IsIndependent()
            .Map(m => m.MapKey(c => c.Id, "RatingSubmitter"));

        HasRequired(p => p.For)
            .WithMany(u => u.OwnRatings)
            .IsIndependent()
            .Map(m=>m.MapKey(c => c.Id, "RatedSeller"));
    }
}

public class ItemRatingConfiguration : EntityTypeConfiguration<ItemRating>
{
    public ItemRatingConfiguration()
    {
        HasRequired(p => p.By)
            .WithMany(u => u.ItemRatings)
            .IsIndependent()
            .Map(m=>m.MapKey(c => c.Id, "ItemRatingSubmitter"));
    }
}

У меня очень запутанная модель в SQLСервер, который, очевидно, вызван моим испорченным отображением.Какие-нибудь предложения или я должен просто забыть о наследовании и принципе СУХОГО вместе в данном случае?

1 Ответ

1 голос
/ 06 марта 2011

Вы не можете использовать прямое отображение M: N, если вам нужно добавить пользовательское свойство в это отношение.В таком случае вам нужно смоделировать соединительную таблицу как другую сущность, которая будет содержать ссылку на Rating и Question, а также включать свойство Score.

Я бы рекомендовал использовать наследование TPH.Это проще в использовании и имеет лучшую производительность.TPT создает действительно безобразных запросов .Также нет причин иметь RatingQuestions в производных классах.Обе эти коллекции ссылаются на один и тот же тип, поэтому вы можете переместить его в родительский.Более того, согласно в этом вопросе существуют некоторые проблемы со свойствами навигации в дочерних классах при использовании TPH.Я не уверен, что эта проблема все еще действует в Code-first.В любом случае вашей текущей модели просто не нужно свойство навигации для дочернего элемента.

Если вы следуете моим советам, вам не нужно добавлять какие-либо сопоставления.Он будет отображаться с соглашениями по умолчанию при использовании этих классов:

public abstract class Rating
{
    public virtual int Id { get; set; }
    public virtual User By { get; set; }

    private ICollection<RatingQuestion> _ratingQuestions = null;
    public ICollection<RatingQuestion> RatingQuestions
    {
        get
        {
            if (_ratingQuestions == null)
            {
                _ratingQuestions = new HashSet<RatingQuestion>();
            }
            return _ratingQuestions;
        }
        protected set { _ratingQuestions = value; }
    }
}

public class ItemRating : Rating
{
    public virtual Item For { get; set; }
}

public class UserRating : Rating
{
    public virtual User For { get; set; }
}

public class RatingQuestion
{
    public virtual int Id { get; set; }
    public virtual int Score { get; set; }
    public virtual Rating Rating { get; set; }
    public virtual Question Question { get; set; }
}

public class Question
{
    public virtual int Id { get; set; }
    public virtual string Text { get; set; }

    private ICollection<RatingQuestion> _ratingQuestions = null;
    public ICollection<RatingQuestion> RatingQuestions
    {
        get
        {
            if (_ratingQuestions == null)
            {
                _ratingQuestions = new HashSet<RatingQuestion>();
            }
            return _ratingQuestions;
        }
        protected set { _ratingQuestions = value; }
    }
}
...