DbContext AutoDetectChangesEnabled е зададен на фалшиво откриване на промени

Малко съм объркан. От това, което прочетох, настройката на DbContext.AutoDetectChangesEnabled до false трябва да деактивира проследяването на промените, което изисква да се извика DbContext.DetectChanges, за да се идентифицират промените, които да бъдат изпратени в базата данни.

От моите регистрационни файлове по-долу обаче става ясно, че промените се регистрират от инструмента за проследяване на промените на dbContexts, дори и с настройка, зададена на false.

Изпускам ли нещо?

Версия на Entity Framework: 5.0.0.0

Клас DbContext

public class ProjectContext : DbContext {
    public DbSet<Project> Projects {get;set;}
}

Клас контролер

private ProjectContext db = new ProjectContext();

public method(){
    Project p = new Project("uniqueName");
    db.Configuration.AutoDetectChangesEnabled = false;
    db.Projects.Add(p);
    DebugChangeTracker();
    db.SaveChanges();

    db.Projects.First().ProjectName = "a differentName!";
    DebugChangeTracker();
    db.SaveChanges();
}

Метод на регистриране

    private void DebugChangeTracker()
    {
        var path = "C:\\mypath\\";
        path = path + Util.GetMsSinceEpoch().ToString() + "changeTracker.log";

        using (StreamWriter sw = new StreamWriter(path))
        {
            var changeTracker = db.ChangeTracker;
            var entries = changeTracker.Entries();
            foreach (var x in entries)
            {

                var name = x.Entity.ToString();
                var state = x.State;

                sw.WriteLine("");
                sw.WriteLine("***Entity Name: " + name +
                             "is in a state of " + state);
                var currentValues = x.CurrentValues;
                sw.WriteLine("***CurrentValues***");
                PrintPropertyValues(currentValues,sw);
                if (state != EntityState.Added)
                {
                    sw.WriteLine("***Original Values***");
                    PrintPropertyValues(x.OriginalValues,sw);
                }
            }
        }
    }

Първи дневник

***Entity Name: Models.Projectis in a state of Added
***CurrentValues***
ProjectId:0
ProjectName:uniqueName

Втори дневник

***Entity Name: Models.Projectis in a state of Modified
***CurrentValues***
ProjectId:1
ProjectName:uniqueName
***Original Values***
ProjectId:1
ProjectName:a differentName!

person Jesse    schedule 31.05.2013    source източник


Отговори (3)


Задаването на AutoDetectChangesEnabled на false не деактивира проследяването на промените. (Това би направил методът за разширение AsNoTracking().) Той просто деактивира автоматичното извикване на DetectChanges, което иначе би се случило в много DbContext API методи.

Но DetectChanges не е единственият метод, който участва в проследяването на промените. Ако обаче не го извикате ръчно на правилните места, където е необходимо, проследените състояния на обекта са непълни или грешни, което води до неправилно записани данни.

Във вашия случай състоянието Added в първата част на вашето method се очаква, дори когато AutoDetectChangesEnabled е зададено на false, защото вие само извиквате db.Projects.Add(p). (Между другото, редът липсва във вашия код, но предполагам, че това е просто грешка при копиране и поставяне.) Извикването на метод от DbContext API проследява промените правилно и състоянията в инструмента за проследяване ще бъдат правилни, ако състоянието е било правилно преди извикването на Add.

Или с други думи: извикването на API метод не превръща правилното състояние в грешно състояние. Но: Ако AutoDetectChangesEnabled е false, това също няма да превърне грешно състояние в правилно състояние, какъвто би бил случаят, ако AutoDetectChangesEnabled е true.

Въпреки това, във втората част на вашия method вие просто променяте стойността на свойството на POCO. След тази точка състоянието на инструмента за проследяване на промените е грешно (Unchanged) и без извикване на DetectChanges (ръчно или - ако AutoDetectChangesEnabled е true - автоматично в ChangeTracker.Entries или SaveChanges) никога няма да бъде коригирано. Ефектът е, че променената стойност на свойството не се записва в базата данни.

В последния раздел, споменаващ състоянието Unchanged, имам предвид моя собствен тест (а също и това, което бих очаквал). Не знам и не мога да възпроизведа защо имате състояние Modified.

Съжалявам, ако всичко това звучи малко объркващо. Артър Викерс може да го обясни по-добре.

Смятам, че автоматичното откриване на промени и поведението при деактивирането му са доста трудни за разбиране и овладяване и обикновено не докосвам настройките по подразбиране (AutoDetectChangesEnabled = true) за всички проследявани промени, които са по-сложни от най-простите неща (като групово добавяне на обекти в примка и т.н.).

person Slauma    schedule 31.05.2013
comment
Трябваше да го прочета няколко пъти, но това доста помогна да отговоря на въпроса си, благодаря! Съжаляваме за грешката при копиране и поставяне; ще актуализирам въпроса за бъдещето. - person Jesse; 01.06.2013
comment
За съжаление груповото добавяне на обекти в цикъл е, когато искате проследяването на промените да е деактивирано. Това е мащабно ускоряване (размер на извадката 1, тестван в моето приложение, но това беше единствената разлика между две серии, добавящи ~3000 реда). - person Ed S.; 12.10.2013
comment
@EdS.: Груповото добавяне е едно от най-простите неща, които имах предвид, където всъщност бих деактивирал автоматичното откриване на промяна. - person Slauma; 12.10.2013
comment

Взети от тези два проекта, след което модифицирани CustomSegue и CustomUnwindSegue като пример. https://github.com/soleares/SOLPresentingFun https://github.com/Phillipus/CustomSegue

CustomSegue.m

#import "CustomSegue.h"
#import "SOLSlideTransitionAnimator.h"

@interface CustomSegue()<UIViewControllerTransitioningDelegate>
@property (nonatomic,strong) SOLSlideTransitionAnimator* animator;
@end

@implementation CustomSegue

- (void)perform {
    UIViewController *sourceViewController = self.sourceViewController;
    UIViewController *destinationViewController = self.destinationViewController;

    destinationViewController.transitioningDelegate = self;
    destinationViewController.modalPresentationStyle = UIModalPresentationCustom;

    [sourceViewController presentViewController:destinationViewController animated:YES completion:nil];
}

/*
 Called when presenting a view controller that has a transitioningDelegate
 */
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
                                                                  presentingController:(UIViewController *)presenting
                                                                      sourceController:(UIViewController *)source
{
    self.animator.appearing = YES;
    return self.animator;
}

-(SOLSlideTransitionAnimator*) animator
{
    if(!_animator)
    {
        _animator = [[SOLSlideTransitionAnimator alloc] init];
        _animator.appearing = NO;
        _animator.duration = 0.35;
        _animator.edge = SOLEdgeRight;
    }
    return _animator;
}

@end

CustomUnwindSegue.m

@interface CustomUnwindSegue()<UIViewControllerTransitioningDelegate>
@property (nonatomic,strong) SOLSlideTransitionAnimator* animator;
@end



#import "CustomUnwindSegue.h"
#import "SOLSlideTransitionAnimator.h"

@implementation CustomUnwindSegue

- (void)perform {
    UIViewController *sourceViewController = self.sourceViewController;

    sourceViewController.transitioningDelegate = self;
    sourceViewController.modalPresentationStyle = UIModalPresentationCustom;

    [sourceViewController dismissViewControllerAnimated:YES completion:nil];
}

-(SOLSlideTransitionAnimator*) animator
{
    if(!_animator)
    {
        _animator = [[SOLSlideTransitionAnimator alloc] init];
        _animator.appearing = NO;
        _animator.duration = 0.35;
        _animator.edge = SOLEdgeRight;
    }
    return _animator;
}

/*
 Called when dismissing a view controller that has a transitioningDelegate
 */
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    self.animator.appearing = NO;
    return self.animator;
}

@end
- person Iman Mahmoudinasab; 06.01.2014
comment
Как да му се подиграем? - person Sana; 03.06.2019

Ако някой търси AutoDetectChangesEnabled в Entity Framework Core, можете да го намерите под ChangeTracker вместо Configuration

Използване като:

context.ChangeTracker.AutoDetectChangesEnabled = false;

//Do something here
context.PriceRecords.Add(newPriceRecord);

context.ChangeTracker.AutoDetectChangesEnabled = true;
person Jiri Houzvicka    schedule 27.07.2017
comment
Благодаря, точно това, което търсих. - person Daniel; 03.06.2019
comment
Търся това в конфигурацията толкова много часове. Благодаря. - person Sagar Khatri; 14.03.2021

според Entity статията на Framework Automatic Detect Changes

те казаха:

може да получите значителни подобрения в производителността, като го изключите в some cases

вижте този пример от тази статия

using (var context = new BloggingContext()) 
{ 
    try 
    { 
        context.Configuration.AutoDetectChangesEnabled = false; 

        // Make many calls in a loop 
        foreach (var blog in aLotOfBlogs) 
        { 
            context.Blogs.Add(blog); 
        } 
    } 
    finally 
    { 
        context.Configuration.AutoDetectChangesEnabled = true; 
    }
}

Този код избягва ненужните извиквания към DetectChanges, които биха възникнали при извикване на методите DbSet.Add и SaveChanges.

person Basheer AL-MOMANI    schedule 06.10.2016
comment
Ако го включите отново във финалния блок, прави ли автоматично това, което би направил, ако беше включен, но по-бързо? - person Ian Warburton; 03.10.2017