Entity Framework Code First - Добавление 1: много через объединенный корень - PullRequest
0 голосов
/ 09 октября 2011

У меня есть вопрос относительно добавления дочерних элементов в корневую сущность в Entity Framework 4.1 CF.

Учитывая следующую инфраструктуру базового класса сущности и два POCO:

public abstract class EntityBase<TKeyDataType>
{
    [Key]
    public TKeyDataType Id { get; set; }

    // Equality methods ommitted for brevity...
}

public class Foo : EntityBase<int>, IAggregateRoot
{
    public string Foo1 { get; set; }

    public virtual ICollection<FooSibling> Siblings { get; set; }
}

public class FooSibling : EntityBase<int>
{
    public string SiblingPropFoo { get; set; }

    public int FooId { get; set; }
    public Foo Foo { get; set; }
}

Обратите внимание, что Foo реализует IAggregateRoot (просто пустой интерфейс - воспринимайте его как метаданные в "данных"о данных "контекст).

Пока все хорошо.Если я запустил это, EF создаст базу данных с соответствующим отношением 1: многие.

Единственное свободное отображение, которое я имею на этих двух объектах:

modelBuilder.Entity<Foo>()
            .HasMany(x => x.Siblings)
            .WithRequired(x=>x.Foo)
            .WillCascadeOnDelete(true);

Нет FooSibling безFoo.Сдуйте Foo, вы снесете всех своих братьев и сестер.Этот фрагмент работает.

Проблема заключается в том, что при добавлении FooSiblings POCO в Foo POCO я должен использовать уникальные отрицательные числа, как показано в этом методе обслуживания:

public ResponseBase UpdateBy(RequestBase<Foo> request)
{
    ResponseBase response = new ResponseBase();
    try
    {
        Foo foo = FooRepository.FirstOrDefault(x => x.Id == request.Entity.Id);

        // Dummy adds to test associations.  
        // These come back on the Foo inside the request, but I'm explicitly putting them here  
        // for the purpose of this question. 

        request.Entity.Siblings.Add(new FooSibling() { Id = -2, SiblingPropFoo = "Prop1",  SiblingPropFoo2 = "Prop2" });
        request.Entity.Siblings.Add(new FooSibling() { Id = -1, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });

        // Update Foo's scalars and children (mapping is Foo->Foo)
        foo = AutoMapper.Mapper.Map(request.Entity, foo);

        UnitOfWork.Commit();
        response.Success = true; 
    }
    catch (Exception e)
    {
        response.Success = false;
        response.Message = e.Message; 
    }
    return response;
}

После UnitofWork.Commit()вызывается (он просто вызывает SaveChanges контекста - здесь нет магии), все хорошо ...

Однако, если я не использую уникальные отрицательные числа, такие как и просто пытаюсь установить его родителя,как это:

request.Entity.Siblings.Add(new FooSibling() { Foo = foo, SiblingPropFoo = "Prop1",  SiblingPropFoo2 = "Prop2" });
request.Entity.Siblings.Add(new FooSibling() { Foo = foo, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });

Только один сохраняется в базе данных.

Единственный известный мне способ сделать это без использования отрицательных чисел - это использовать DooSet FooSiblings непосредственно в методе обслуживания:

IRepository<FooSibling> siblingRepo = new CookieCutterEntityFrameworkRepository<FooSibling>(UnitOfWork);
siblingRepo.Insert(new FooSibling() { FooId = foo, .... });

Мой репозиторий CookieCutter абстрагирует все содержимое DbSetи т. д.

Но ... для ясности отбрасывая всю абстракцию и общее вуду, вопрос действительно сводится к тому, есть ли способ обновить мой Foo POCO (корневой объект) и добавить новых братьев и сестер через одинDbSet без использования отрицательных чисел?

Для справки (без абстракции с использованием чистого DbContext):

// This works (using multiple DbSets/Repositories always make life easier...) 
Ctx.Foos.Update(foo);  
Ctx.FooSiblings.Add(new FooSibling() { Foo = foo, ... }); 
Ctx.FooSiblings.Add(new FooSibling() { Foo = foo, ... }); 
Ctx.SaveChanges();  

// This works too (using negative number trick - foo scalar properties get 
// updated and the siblings get persisted to the database properly).  
foo.Siblings.Add(new FooSibling() { Id = -2, ....});
foo.Siblings.Add(new FooSibling() { Id = -1, ....});
Ctx.Foos.Update(foo);  
Ctx.SaveChanges();  

// This doesn't work (but it's what I'm striving for to drive everything off the root).
foo.Siblings.Add(new FooSibling() { Foo = foo });
foo.Siblings.Add(new FooSibling() { Foo = foo });
Ctx.Foos.Update(foo);  
Ctx.SaveChanges();  

В последнем случае (нерабочий случай) я стараюсь настроить его так, чтобы он выбираллюбые изменения в самом Foo POCO.

Я пробовал с выключенными и включенными прокси.Кроме того, в способе настройки контекст остается в области действия в течение всего срока действия HTTP-запроса.

Если это невозможно, какие предложения вы бы дали?

1 Ответ

2 голосов
/ 09 октября 2011

Если я правильно читаю, проблема ожидается через dbcontext или objectcontext.

ПЕРВЫЙ Я хочу взглянуть на ваш последний набор примеров .. нет абстракции. Когда вы прикрепляете график к контексту, все в нем прикрепляется как неизмененное. Период. Контексту не важно, как вы добавили или прикрепили «потомков» к корню (например, братьев и сестер к foo), прежде чем контекст узнал о них. Если foo уже известен контексту, и ТОГДА вы ДОБАВЛЯЕТЕ, тогда контекст знает, КАК вы сделали связывание .. вы сказали «ДОБАВИТЬ», поэтому он помечает их как «Добавленные», и они будут вставлены. Даже если root Не новый. Вам придется изменить его состояние на «Без изменений». Но в вашем случае вы берете несвязанный граф и присоединяете его, поэтому все в графе воспринимается как неизменное. Вы должны явно изменить состояние каждого из братьев и сестер на то, что вы хотите для SaveChanges, чтобы заметить, что ему нужно ВСТАВИТЬ.

Теперь к вашему хранилищу. Я понятия не имею, что все в порядке с отрицательными числами. :) И у меня нет времени экспериментировать с этим. (любопытно, как это обманывает сохранение, но это вонючий взлом :)) Это зависит от того, что происходит за вашим запросом FooRepository. Если этот возвращенный foo отслеживается контекстом, то я бы ожидал , что когда вы добавляете братьев и сестер, контекст узнает, что они добавлены, и создаст вставку. Но если это не отслеживается, то, когда вы добавляете братьев и сестер, контекст не может знать о «ДОБАВИТЬ». В какой бы точке вы ни подключили график (foo + его родные элементы) к контексту, все они будут «неизменными», поэтому вы не получите нужную вставку.

Все это зависит от GUESS от того, что еще происходит в вашем хранилище, и от того, что, если таковые имеются, влияет на автопроизводитель. Я предполагаю, что вы делаете всю работу до того, как контекст узнает о графике, так что в конце я думаю, что вы вызываете ATTACH, что приведет к тому, что INSERTS не будет отправлено в базу данных.

...