При использовании Entity Framework Code-first мне нужно иметь модель моего домена анемичной? - PullRequest
9 голосов
/ 25 августа 2011

с использованием Entity Framework Code-First Мне нужно, чтобы все свойства были общедоступными для создания базы данных, это означает, что с использованием подхода Entity Framework Code-First я вынужден иметь анемичный домен , смешанный с богатой моделью?Поскольку свойства, которые не обязательно должны быть открытыми, должны быть.

Например:

При использовании расширенной модели это в Entity Framework не будет работать:

public class Car
{
    private int _actualPosition;

    public void Accelerate()
    {
        this._actualPosition += 10;
    }
}

Для работы нам нужно сделать _actualPosition общедоступным:

public class Car
{
    public int ActualPosition { get; set; }

    public void Accelerate()
    {
        this.ActualPosition += 10;
    }
}

Теперь Entity IMO ужасен, потому что у меня есть метод, который добавляет + 10 в свойстве и делает его публичным в то же времявремя, я не хочу, чтобы это свойство было общедоступным.

Другой пример:

Представьте, что я хочу отношения многие-ко-многим, но с только в одну сторону как:

public class User
{
   public long Id { get; set; }

   public string Name { get; set; }

   public IList<Role> Roles { get; set; }
}

public class Role
{
   public long Id { get; set; }

   public string Name { get; set; }
}

Как я могу установить отношения «многие ко многим» с этой моделью?Насколько мне известно, я никак не могу установить двусторонние отношения:

public class User
{
   public long Id { get; set; }

   public string Name { get; set; }

   public IList<Role> Roles { get; set; }
}

public class Role
{
   public long Id { get; set; }

   public string Name { get; set; }

   public IList<User> Users { get; set; }
}

С помощью этой модели я смогу установить отношения многие ко многим:

modelBuilder.Entity<User>()
                .HasMany(x => x.Roles)
                .WithMany(y => y.Users);

Я прав?Если да, Entity Framework меня не радует.

«Другой пример» работает с ответом @ Slauma :

modelBuilder.Entity<User>()
            .HasMany(x => x.Roles)
            .WithMany()
            .Map(a =>
            {
                a.MapLeftKey("UserId");
                a.MapRightKey("RoleId");
                a.ToTable("UserRoles");
            });

Ответы [ 5 ]

3 голосов
/ 26 августа 2011

Ваш "Другой пример" не верен. Вы не обязаны выставлять оба конца отношения как свойства в ваших модельных классах. В вашем примере вы можете удалить public IList<User> Users { get; set; } из вашего Role класса и определить отображение следующим образом:

modelBuilder.Entity<User>()
            .HasMany(x => x.Roles)
            .WithMany()
            .Map(a =>
            {
                a.MapLeftKey("UserId");
                a.MapRightKey("RoleId");
                a.ToTable("UserRoles");
            });

Это касается и отношений один-ко-многим. Всегда существует перегрузка With... без параметров, WithMany(), WithRequired(), WithOptional() и т. Д. Для определения отображений для отношений, в которых не представлены оба конца в модели.

1 голос
/ 23 февраля 2012

Я считаю, что вы ищете следующее:

 public class Car
{
    public int Id { get; set; }
    public int ActualPosition { get; private set; }

    public void Accelerate()
    {
        this.ActualPosition += 10;
    }
}

Ниже приведен тестовый блок:

 [TestMethod]
    public void AccellerateWillAddTenUnitsToTheActualPosition()
    {
        //Arrange
        var car = new Car();
        var actualPosition = car.ActualPosition;
        var expectedPosition = actualPosition + 10;

        //Act
        car.Accelerate();

        //Assert
        Assert.AreEqual(car.ActualPosition, expectedPosition);
    }
1 голос
/ 25 августа 2011

Вы можете настроить свою Car сущность следующим образом:

public class Car
{
    public int ActualPosition { get; private set; }

    public void Accelerate()
    {
        this.ActualPosition += 10;
    }
}

Это заставляет модифицировать свойство ActualPosition для использования специализированного метода Accelerate.Побочным эффектом использования этого стиля является то, что ваш метод Accelerate не тестируется.

Вы также можете настроить его следующим образом:

public class Car
{
    protected int ActualPosition { get; set; }

    public void Accelerate()
    {
        this.ActualPosition += 10;
    }
}

Однако этот подход не поддается прямой проверкебез использования отражения.

1 голос
/ 25 августа 2011

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

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

public class Car
{
    private CarPersistence _carPersistence;

    public void Accelerate()
    {
        this._carPersistence.ActualPosition += 10; 
    }

    public Car (CarPersistence car) {
        _carPersistence = car;
    }
}

public class CarPersistence
{
    public int ActualPosition;
}

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

Система, подобная этой, с вашими бизнес-классами на одном «слое» и вашими персистентными классами на другом «слое», вы довольно легко можете иметь отношения один ко многим,он просто должен быть встроен во все, что строит ваши бизнес-классы из классов персистентности:

public class User
{
   private UserPersistence _user;
   private IList<Persistence> _roles;

   public User (UserPersistence user, IList<Persistence> roles) {
       _user = user;
       _roles = roles;
   }
}

Извините, синтаксис может быть не идеальным, это лучшее, что я могу вспомнить, транслитерируя из VB в C #;)

0 голосов
/ 25 августа 2011

Да, код, который вы опубликовали, - Анемичная модель .Кроме того, я бы даже не назвал это правильным объектно-ориентированным кодом.Инкапсуляции просто нет, а объекты - это просто контейнеры данных.Похоже, что ваш ORM ограничивает выбор реализации вашего домена.Для чего стоит NHibernate позволяет вам отображать поля напрямую (даже если эти поля доступны только для чтения).Это не требует, чтобы объекты имели свойства.Например:

public class Car {

    private int _position;

    public void Accelerate() {
        _position += 10;
    }
}

Может отображаться как:

<class name="Car" table="Cars" >
    ...
    <property name="_position" column="Position" />
    ...
</class>
...