Entity Framework 4 TPH наследование, как изменить один тип в другой? - PullRequest
5 голосов
/ 03 ноября 2010

Я нашел некоторую информацию по этому поводу, но мне недостаточно, чтобы понять, для чего лучше всего подходит для этого сценария. У меня есть типичная настройка TPH с абстрактным базовым классом «Фирма». У меня есть несколько детей "Маленькая Фирма", "Большая Фирма" и т. Д., Унаследованных от Фирмы. На самом деле у меня фактически есть разные реалистичные классификации для фирм, но я пытаюсь сделать это простым в этом примере. В базе данных согласно TPH у меня есть одна таблица Firm со столбцом FirmTypeId (int), который различает все эти типы. Все прекрасно работает, за исключением того, что у меня есть требование разрешить пользователю менять один тип фирмы на другой. Например, пользователь мог ошибиться при добавлении фирмы и хотел бы изменить ее с Большой фирмы на Малую фирму. Поскольку структура сущностей не позволяет предоставлять доступ к различающему столбцу базы данных как свойству, я не верю, что есть способ изменить один тип на другой через EF. Пожалуйста, поправьте меня, если я ошибаюсь. То, как я это вижу, у меня есть два варианта:

  1. Не используйте TPH. Просто создайте Фирменный объект и вернитесь к использованию .Where (FirmTypeId == что-то) для различения типов.
  2. Выполнение SQL напрямую с использованием context.ExecuteStoreCommand для обновления столбца FirmTypeId базы данных.

Я видел пост, где люди предполагают, что одним из принципов ООП является то, что экземпляры не могут менять свой тип. Хотя это имеет смысл для меня, я просто не могу соединить точки. Если бы мы следовали этому правилу, то единственный случай использования любого вида наследования (TPH / TPT) - это когда один уверен, что один тип никогда не будет преобразован в другой. Таким образом, маленькая фирма никогда не станет большой фирмой. Я вижу предложения, что вместо этого следует использовать композицию. Хотя это не имеет смысла для меня (то есть я не вижу, как у фирмы есть большая фирма, для меня большая фирма - это фирма), я вижу, как можно смоделировать состав в EF, если данные находятся в несколько столов. Однако в ситуации, когда у меня есть одна таблица в базе данных, кажется, что это TPH или то, что я описал в # 1 и # 2 выше.

Ответы [ 4 ]

2 голосов
/ 07 февраля 2015

Я столкнулся с этой проблемой в нашем проекте, где у нас есть ядро ​​DBContext и несколько «подключаемых» модулей со своими собственными DBContexts, в которых «пользователь модуля» наследует «основного (базового) пользователя».Надеюсь, это понятно.

Нам также потребовалась возможность изменить (давайте назовем это) User на Customer (и при необходимости также на другое "унаследованное" Users в то же время, чтобы этот пользовательможно использовать все эти модули.

Из-за этого мы попытались использовать наследование TPT вместо TPH - но TPH тоже бы сработал.

Один из способов - использовать пользовательскую хранимую процедуру как подсказывают многие ...

Другой способ, который мне пришёл в голову, - это отправить пользовательский запрос на вставку / обновление в БД. В TPT это будетbe:

private static bool UserToCustomer(User u, Customer c)
    {
        try
        {
            string sqlcommand = "INSERT INTO [dbo].[Customers] ([Id], [Email]) VALUES (" + u.Id + ", '" + c.Email + "')";
            var sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings["DBContext"].ConnectionString);
            sqlconn.Open();
            var sql = new SqlCommand(sqlcommand, sqlconn);
            var rows = sql.ExecuteNonQuery();
            sqlconn.Close();

            return rows == 1;
        }
        catch (Exception)
        {
            return false;
        }
    }

В этом сценарии Customer наследует User и имеет только string Email.

При использовании TPH запрос будет изменяться только с *От 1030 * до UPDATE ... SET ... WHERE [Id] = .... Не забудьте также изменить столбец Discriminator.

После следующего вызова dbcontext.Users.OfType<Customer> наш первоначальный пользователь "преобразован" в клиента.


Итог: я также попробовал решение другого вопроса, который включал отсоединение оригиналty (пользователь) из ObjectStateManager и создание нового состояния сущности (клиента) , изменение , сохранение dbcontext.SaveChanges().Это не сработало для меня (ни TPH, ни TPT).Либо потому, что для каждого модуля используются отдельные DBContexts, либо EntityFramework 6 (.1) игнорирует это. Его можно найти здесь.

1 голос
/ 03 ноября 2010

Да, с тобой все в порядке.Наследование EF не поддерживает этот сценарий.Лучший способ изменить тип Фирма для существующей Фирма - это использовать хранимую процедуру.

Пожалуйста, посмотрите на этот пост для получения дополнительной информации:
Изменение унаследованных типов в Entity Framework

0 голосов
/ 26 апреля 2017

РЕДАКТИРОВАТЬ: Извинения, это ответ EF 6.x

Я публикую пример кода для полноты. В этом сценарии у меня есть базовый класс Thing. Затем подклассы: ActiveThing и DeletedThing

Мои OData ThingsController содержат основной GetThings, который я намереваюсь предоставить только ActiveThing s, но его GetThing (ThingId) все еще может возвращать любой тип объекта. Действие Delete выполняет преобразование из ActiveThing в DeletedThing во многом так, как запрашивает OP, и во многом так, как описано в других ответах. Я использую встроенный SQL (параметризованный)

public class myDbModel:DbContext
{
    public myDbModel(): base("name=ThingDb"){}

    public DbSet<Thing> Things { get; set; }  //db table

    public DbSet<ActiveThing> ActiveThings { get; set; } // now my ThingsController 'GetThings' pulls from this

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
       //TPH (table-per-hierarchy):
      modelBuilder.Entity<Ross.Biz.ThingStatusLocation.Thing>()
        .Map<Ross.Biz.ThingStatusLocation.ActiveThing>(thg => thg.Requires("Discriminator").HasValue("A"))
        .Map<Ross.Biz.ThingStatusLocation.DeletedThing>(thg => thg.Requires("Discriminator").HasValue("D"));
    }

}

Вот мой обновленный ThingsController.cs

public class ThingsController : ODataController
{
    private myDbModel db = new myDbModel();

    /// <summary>
    /// Only exposes ActiveThings (not DeletedThings)
    /// </summary>
    /// <returns></returns>
    [EnableQuery]
    public IQueryable<Thing> GetThings()
    {
        return db.ActiveThings;
    }

    public async Task<IHttpActionResult> Delete([FromODataUri] long key)
    {
        using (var context = new myDbModel())
        {
            using (var transaction = context.Database.BeginTransaction())
            {
                Thing thing = await db.Things.FindAsync(key);
                if (thing == null || thing is DeletedThing) // love the simple expressiveness here
                {
                    return NotFound();//was already deleted previously, so return NotFound status code
                }

                //soft delete: converts ActiveThing to DeletedThing via direct query to DB
                context.Database.ExecuteSqlCommand(
                    "UPDATE Things SET Discriminator='D', DeletedOn=@NowDate WHERE Id=@ThingId", 
                    new SqlParameter("@ThingId", key), 
                    new SqlParameter("@NowDate", DateTimeOffset.Now)
                    );

                context.ThingTransactionHistory.Add(new Ross.Biz.ThingStatusLocation.ThingTransactionHistory
                {
                    ThingId = thing.Id,
                    TransactionTime = DateTimeOffset.Now,
                    TransactionCode = "DEL",
                    UpdateUser = User.Identity.Name,
                    UpdateValue = "MARKED DELETED"
                });
                context.SaveChanges();
                transaction.Commit();
            }
        }

        return StatusCode(HttpStatusCode.NoContent);
    }
}
0 голосов
/ 04 ноября 2010

Если вы явно не хотите использовать полиморфную функциональность реляционного наследования, то почему бы не взглянуть на стратегию расщепления?

http://msdn.microsoft.com/en-us/data/ff657841.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...