powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Entity Framework сохранить только один объект.
17 сообщений из 17, страница 1 из 1
Entity Framework сохранить только один объект.
    #38144303
Lexxxxx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Можно ли в entity framework обновить/добавить в БД только один экземпляр сущности, а не сохранять сразу весь контекст, как делает метод SaveChanges?
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144356
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LexxxxxМожно ли в entity framework обновить/добавить в БД только один экземпляр сущности, а не сохранять сразу весь контекст, как делает метод SaveChanges?

1 Можно в контексте изменить только одну Entity и SaveChanges
2 Можно изменить State всех Entity контекста на Unchanged, нужную оставить в состоянии Modified
3 Можно создать Stored Procedure на стороне БД и замапить ее в EDM
4 Можно создать в Storage модели искусственное определение функции и замапить ее как и в случае 3:

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
...............
		<Function Name="MyProc" IsComposable="false">
			<CommandText>
				UPDATE T1 SET ZZZ = @Zzz WHERE ID = @Id;
				SELECT @@ROWCOUNT;
			</CommandText>
			<Parameter Mode="In" Name="Id" Type="int"/>
			<Parameter Mode="In" Name="Zzz" Type="int"/>
		</Function>
	</Schema>
	</edmx:StorageModels>



Дальше импортируете ее в Concept/Mapping схему дизайнером или руками , в в вашем DbContext tt сгенерит обертку для вызова функции. Вызываете, ctx.MyProc(1, 7);

В базу сыпется:

Код: sql
1.
2.
3.
4.
exec sp_executesql N'
				UPDATE T1 SET ZZZ = @Zzz WHERE ID = @Id;
				SELECT @@ROWCOUNT;
			',N'@Id int,@Zzz int',@Id=1,@Zzz=7



Последний подход плохой тем, что при обновлении схемы из БД, StorageModel полностью пересоздается и определение функции будет утеряно.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144359
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
можно еще достучаться до DbConnection ctx.Database.Connection... и по старинке.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144424
Lexxxxx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lord British1 Можно в контексте изменить только одну Entity и SaveChanges
Суть именно в том, чтобы имея больше одной измененной сущности сохранить изменения в БД только у выбранной или добавить новую когда есть обновленные, но еще не сохраненные в БД.

Lord British2 Можно изменить State всех Entity контекста на Unchanged, нужную оставить в состоянии Modified
Вот это кажется может подойти, если не очень ресурсоемко. Можно "пальцем показать" каких нибудь подробностей? Как добраться до State?

Lord Britishможно еще достучаться до DbConnection ctx.Database.Connection... и по старинке.
Строить команду и выполнять ее на этом соединении? Кажется это будет довольно громоздко и неустойчиво.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144469
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lexxxxx
Lord British2 Можно изменить State всех Entity контекста на Unchanged, нужную оставить в состоянии Modified
Вот это кажется может подойти, если не очень ресурсоемко. Можно "пальцем показать" каких нибудь подробностей? Как добраться до State?



Что-то вроде такого гавнакода
Код: sql
1.
2.


...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144472
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LexxxxxLord British2 Можно изменить State всех Entity контекста на Unchanged, нужную оставить в состоянии Modified
Вот это кажется может подойти, если не очень ресурсоемко. Можно "пальцем показать" каких нибудь подробностей? Как добраться до State?



дубль два.

примерно так
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication17
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var ctx = new DB1Entities())
            {
                ctx.T1.First(o => o.ID == 1).ZZZ = 8;
                
                var z = ctx.T1.First(o => o.ID == 2); z.ZZZ = 7;

                ctx.DumpEntries("before saveonlyone");

                ctx.SaveOnlyOne(z);

                ctx.DumpEntries("after saveonlyone");

                ctx.SaveChanges();

                ctx.DumpEntries("after saveall");
            }
        }

    }

    public static class EfHelper
    {
        public static void SaveOnlyOne(this DbContext ctx, object entity)
        {
            var oldStates = ctx.SetAllToUnchangedButThis(entity);

            ctx.SaveChanges();

            ctx.RestoreAllStates(oldStates);
        }

        private static Dictionary<object, EntityState> SetAllToUnchangedButThis(this DbContext ctx, object entity)
        {
            var objCtx = ((IObjectContextAdapter)ctx).ObjectContext;

            var entries = objCtx.ObjectStateManager.GetObjectStateEntries(
                EntityState.Added | EntityState.Deleted | EntityState.Modified);

            var oldStates = new Dictionary<object, EntityState>();
            foreach (var e in entries)
            {
                if (e.Entity == entity)
                    continue;

                oldStates.Add(e.Entity, e.State);

                e.ChangeState(EntityState.Unchanged);
            }

            return oldStates;
        }

        private static void RestoreAllStates(this DbContext ctx, Dictionary<object, EntityState> oldStates)
        {
            var objCtx = ((IObjectContextAdapter)ctx).ObjectContext;

            foreach (var entity in oldStates.Keys)
                objCtx.ObjectStateManager
                    .GetObjectStateEntry(entity)
                    .ChangeState(oldStates[entity]);
        }

        public static void DumpEntries(this DbContext ctx, string message)
        {
            Console.WriteLine(message);

            foreach (var e in ctx.ChangeTracker.Entries())
                Console.WriteLine("{0} - {1}", e.Entity, e.State);
        }

    }
}

...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144538
Lexxxxx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lord British,

Спасибо, вроде то что надо. А почему Вы в RestoreAllStates снова получаете значения из ObjectContext? Может лучше было бы в словарь складывать сразу ObjectStateEntry, чтобы потом снова их не искать при помощи GetObjectStateEntry?
И не совсем ясно, что будет со связанными сущностями сохраняемой сущности? По идее они перейдут в состояние EntityState.Unchanged и сохранены не будут. Или я ошибаюсь?
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144572
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LexxxxxСпасибо, вроде то что надо. А почему Вы в RestoreAllStates снова получаете значения из ObjectContext?


Потому, что писал быстро, абы как :).

LexxxxxМожет лучше было бы в словарь складывать сразу ObjectStateEntry, чтобы потом снова их не искать при помощи GetObjectStateEntry?


Да, можно и так. Причины те же что и выше.


LexxxxxИ не совсем ясно, что будет со связанными сущностями сохраняемой сущности? По идее они перейдут в состояние EntityState.Unchanged и сохранены не будут. Или я ошибаюсь?

SaveOnlyOne сначала сохраняет состояния всех Entry (кроме одной) в сторонку, потом меняет их состояние на Unchanged (кроме одной). Сохраняет context, поскольку там осталась только одна Entity, то сохранена будет только она в зависимости от того что у нее за состояние - Added/Changed/Deleted. После этого, в контексте все имеют состояние Unchanged. Далее восстанавливаются состояния всех Entity (кроме одной), а та Entity остается в состоянии Unchanged (она то уже сохранена), если вы ее опять измените, она поменяет состояние и т. д..

--
PS. Контекст расчитан на то, что вы с ним будете работать по принципам UnitOfWork, а сохранение выборочно сущностей не совсем то. Такое может быть когда вы смешали две или более единицы работы. Но технически выкрутиться можно.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144579
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Еще подкину для размышлений. Если будешь переписывать.

EF пытается генерить при апдейтах запросы так, чтобы включались только измененные поля. Когда ты руками делаешь смену состояния с Unchanged на Modified, то теряется информация о том какие именно поля были изменены. Потому, в апдейтах будут присутствовать все поля.

Кроме того, я использовал ObjectContext API не случайно, есть разница в том как ведут себя:

ObjectContext API: objectStateEntry.ChangeObjectState(EntityState.Unchanged)
Db Context API: entry.State = EntityState.Unchanged

В случае смены Modified ->Unchanged, первое по мимо изменения состояния присваивает original значениям current значения в entry.

Во втором случае по мимо смены состояния current значениям присваиваются original.

Различия только в поведении при смене с Modified на Unchanged.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144582
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PSPS на уровне ObjectStateEntry можно отдельно менять состояние не всей сущности а каждого отдельного проперти.

PSPSPS. Но лучше поменяй подход.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144618
Lexxxxx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lord British,

Не работает однако. Вылетает исключение о невозможности выполнения "метода AcceptChanges, поскольку значения ключей объекта конфликтуют с другим объектом в диспетчере ObjectStateManager. Перед вызовом метода AcceptChanges убедитесь в уникальности значений ключей." при выполнении item.ChangeState(EntityState.Unchanged). Не желает он чего-то чтобы руками состояние объектов меняли.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144636
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lexxxxx,

Тот код что я приводил работает. Как его применили. Другой :). Там об уникальности ключей у вас в ошибке. Показуйте свой код.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144645
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А, понял в чем фишка.

Если сделать в том примере ситуацию как эта:

c# using (var ctx = new DB1Entities())
{
ctx.T1.First(o => o.ID == 1).ZZZ = 8;
ctx.T1.Add(new T1() { ZZZ=1 });
ctx.T1.Add(new T1() { ZZZ=2 });

var z = ctx.T1.First(o => o.ID == 2); z.ZZZ = 7;

ctx.DumpEntries("before saveonlyone");

ctx.SaveOnlyOne(z);

ctx.DumpEntries("after saveonlyone");

ctx.SaveChanges();

ctx.DumpEntries("after saveall");
}


То возникнет это исключение. Это потому, что только что добавленные сущности не участвуют в IdentityMap, а при изменении состояния на Unchanged, EF вставляет их в Identity Map с ключем 0. От того и такое исключение.

Как вариант вместо смены состояния на Unchanged рассмотрите вариант с Detach().

гавнакод fixed
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication17
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var ctx = new DB1Entities())
            {
                ctx.T1.First(o => o.ID == 1).ZZZ = 11;
                ctx.T1.Add(new T1() { ZZZ=1 });
                ctx.T1.Add(new T1() { ZZZ=2 });

                var z = ctx.T1.First(o => o.ID == 2); z.ZZZ = 15;

                ctx.DumpEntries("before saveonlyone");

                ctx.SaveOnlyOne(z);

                ctx.DumpEntries("after saveonlyone");

                ctx.SaveChanges();

                ctx.DumpEntries("after saveall");
            }
        }

    }

    public static class EfHelper
    {
        public static void SaveOnlyOne(this DbContext ctx, object entity)
        {
            var oldStates = ctx.SetAllToUnchangedButThis(entity);

            ctx.SaveChanges();

            ctx.RestoreAllStates(oldStates);
        }

        private static Dictionary<object, EntityState> SetAllToUnchangedButThis(this DbContext ctx, object entity)
        {
            var objCtx = ((IObjectContextAdapter)ctx).ObjectContext;

            var entries = objCtx.ObjectStateManager.GetObjectStateEntries(
                EntityState.Added | EntityState.Deleted | EntityState.Modified);

            var oldStates = new Dictionary<object, EntityState>();
            foreach (var e in entries)
            {
                if (e.Entity == entity)
                    continue;

                oldStates.Add(e.Entity, e.State);

                objCtx.Detach(e.Entity);
            }

            return oldStates;
        }

        private static void RestoreAllStates(this DbContext ctx, Dictionary<object, EntityState> oldStates)
        {
            foreach (var entity in oldStates.Keys)
                ctx.Entry(entity).State = oldStates[entity];
        }

        public static void DumpEntries(this DbContext ctx, string message)
        {
            Console.WriteLine(message);

            foreach (var e in ctx.ChangeTracker.Entries())
                Console.WriteLine("{0} - {1}", e.Entity, e.State);
        }

    }
}


...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144735
Lexxxxx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lord BritishКак вариант вместо смены состояния на Unchanged рассмотрите вариант с Detach().
А обратно аттачить их разве не надо?
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144746
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LexxxxxLord BritishКак вариант вместо смены состояния на Unchanged рассмотрите вариант с Detach().
А обратно аттачить их разве не надо?

надо, не явно происходит тут

ctx.Entry(entity).State = oldStates[entity];

Это просто идею показать и не более.
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144936
Lexxxxx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lord Britishнадо, не явно происходит тут
ctx.Entry(entity).State = oldStates[entity];

Неявно он не аттачится. А чтобы приатачить явно нужно учесть много условий (или я пока многого не понимаю). Чет мне все меньше хочется сохранять только одну сущность. ) А надо бы. (
...
Рейтинг: 0 / 0
Entity Framework сохранить только один объект.
    #38144958
Lord British
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Lexxxxx,

По вашей ссылке ObjectContext API, эта строка

ctx.Entry(entity).State = oldStates[entity];

DbContext API. В сеттере State происходит Attach.
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Entity Framework сохранить только один объект.
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]