varlık nesnesine birden çok IEntityChangeTracker örneği tarafından başvuru yapılamaz. Entity Framework 4.1'de varlığa ilgili nesneleri eklerken


165

City ile referansları olan Çalışan bilgilerini kaydetmeye çalışıyorum. Ama her zaman onaylanmış kişimi kaydetmeye çalışıyorum "ADO.Net Entity Framework Bir varlık nesnesine birden fazla IEntityChangeTracker örneği tarafından başvurulamaz"

Çok fazla yazı okumuştum ama ne yapacağımı tam olarak bilmiyordum ... Kaydet düğmesi tıklama kodum aşağıda verilmiştir

protected void Button1_Click(object sender, EventArgs e)
    {
        EmployeeService es = new EmployeeService();
        CityService cs = new CityService();

        DateTime dt = new DateTime(2008, 12, 12);
        Payroll.Entities.Employee e1 = new Payroll.Entities.Employee();

        Payroll.Entities.City city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));

        e1.Name = "Archana";
        e1.Title = "aaaa";
        e1.BirthDate = dt;
        e1.Gender = "F";
        e1.HireDate = dt;
        e1.MaritalStatus = "M";
        e1.City = city1;        

        es.AddEmpoyee(e1,city1);
    }

ve Çalışan Hizmeti Kodu

public string AddEmpoyee(Payroll.Entities.Employee e1, Payroll.Entities.City c1)
        {
            Payroll_DAO1 payrollDAO = new Payroll_DAO1();
            payrollDAO.AddToEmployee(e1);  //Here I am getting Error..
            payrollDAO.SaveChanges();
            return "SUCCESS";
        }

Yanıtlar:


241

Çünkü bu iki çizgi ...

EmployeeService es = new EmployeeService();
CityService cs = new CityService();

... yapıcıda bir parametre almayın, sanırım sınıflar içinde bir bağlam oluşturursunuz. Yüklediğinizde city1...

Payroll.Entities.City city1 = cs.SelectCity(...);

... city1içindeki bağlama ekleyin CityService. Daha sonra yeniye city1başvuru olarak a Employee e1ekler ve e1 bu başvuruyucity1 içeriğe ekler EmployeeService. Sonuç olarak city1, istisnanın şikayet ettiği iki farklı bağlama bağlandınız.

Hizmet sınıflarının dışında bir bağlam oluşturarak ve her iki hizmette de enjekte ederek ve kullanarak bunu düzeltebilirsiniz:

EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance

Hizmet sınıflarınız, yalnızca tek bir varlık türünden sorumlu depolara benziyor. Böyle bir durumda, hizmetler için ayrı bağlamlar kullandığınızda varlıklar arasındaki ilişkiler devreye girer girmez her zaman sorun yaşayacaksınız.

Ayrıca, EmployeeCityService(tek bir bağlamı vardır) gibi yakından ilişkili bir dizi varlıktan sorumlu tek bir hizmet oluşturabilir ve Button1_Clickyönteminizdeki tüm işlemi bu hizmetin bir yöntemine devredebilirsiniz .


4
Yanıt bazı arka plan bilgileri içermese bile çözme şeklinizi seviyorum.
Daniel Kmak

Görünüşe göre bu sorunumu çözecek, sadece yeni bağlam örneğini nasıl yazacağım hakkında hiçbir fikrim yok :(
Ortund

12
Soyutlama ORM, bir balığa sarı ruj sürmek gibidir.
Ronnie Overby

Burada bir şey eksik olabilir, ancak bazı ORM'lerde (özellikle EntityFramework) veri bağlamı her zaman kısaltılmalıdır. Statik veya yeniden kullanılan bir bağlamın tanıtılması, bir dizi başka zorluk ve problem ortaya çıkaracaktır.
Maritim

@Maritim kullanıma bağlıdır. Web Uygulamalarında, genellikle bir gidiş dönüş. Masaüstü Uygulamalarında, her biri için bir tane kullanabilirsiniz Form(ne olursa olsun, sadece bir iş birimini temsil eder) Thread(çünkü iş DbContextparçacığı güvenli olduğu garanti edilmez).
LuckyLikey

30

Yeniden oluşturma adımları bunun için basitleştirilebilir:

var contextOne = new EntityContext();
var contextTwo = new EntityContext();

var user = contextOne.Users.FirstOrDefault();

var group = new Group();
group.User = user;

contextTwo.Groups.Add(group);
contextTwo.SaveChanges();

Hatasız kod:

var context = new EntityContext();

var user = context.Users.FirstOrDefault();

var group = new Group();
group.User = user; // Be careful when you set entity properties. 
// Be sure that all objects came from the same context

context.Groups.Add(group);
context.SaveChanges();

Sadece birini kullanmak EntityContextbunu çözebilir. Diğer çözümler için diğer yanıtlara başvurun.


2
Diyelim ki contextTwo kullanmak istiyorsunuz? (belki kapsam sorunları vb. nedeniyle) contextOne'dan nasıl ayrılır ve contextTwo'ya nasıl bağlanırsınız?
NullVoxPopuli

Böyle bir şey yapmanız gerekiyorsa, büyük olasılıkla bunu yanlış bir şekilde yapıyorsunuz ... Bir bağlam kullanmanızı öneririm.
Pavel Shkleinik

3
Farklı bir veritabanına işaret ederken olduğu gibi farklı bir örnek kullanmak istediğiniz durumlar vardır.
Jay

1
Bu, sorunun yararlı bir basitleştirilmesidir; ama gerçek bir cevap vermez.
BrainSlugs83

9

Bu eski bir iş parçacığı, ama tercih ettiğim başka bir çözüm, sadece cityId'yi güncellemek ve Çalışanlara delik modeli City'yi Çalışan'a atamamaktır ...

public class Employee{
    ...
    public int? CityId; //The ? is for allow City nullable
    public virtual City City;
}

Sonra atamak yeterli:

e1.CityId=city1.ID;

5

Enjeksiyona ve daha da kötüsü Singleton'a alternatif olarak, Ekle'den önce Detach yöntemini çağırabilirsiniz .

EntityFramework 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);

EntityFramework 4: cs.Detach(city1);

İlk DBContext nesnesine ihtiyacınız olmaması durumunda başka bir yol daha vardır. Sadece ile sarın kullanarak anahtar kelime:

Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
  city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}

1
Ben aşağıdakileri kullandım: dbContext1.Entry(backgroundReport).State = System.Data.Entity.EntityState.Detached'ayırmak ve daha sonra dbContext2.Entry(backgroundReport).State = System.Data.Entity.EntityState.Modified;güncellemek için kullanabilirsiniz . Rüya gibi çalıştı
Peter Smith

Evet, Peter. Durumu Değiştirildi olarak işaretlemem gerekir.
Roman O

Uygulama başlangıç ​​(global.asax) mantığımda widget listesi yüklüyordum .. belleğe saklamak referans nesnelerin basit bir listesi. EF bağlamımı içeride yaptığımdan İfadeleri kullanarak, daha sonra denetleyicim bu nesneleri bir iş grafiğine atamak için etrafta dolaştığında hiçbir sorun olmayacağını düşündüm (hey, bu eski bağlam gitti, değil mi?) - Bu cevap beni kurtardı .
bkwdesign

4

Aynı sorunu yaşadım ama @ Slauma'nın çözümü ile ilgili sorunum (bazı durumlarda harika olmasına rağmen), bağlamın denetleyiciden kullanılabildiğini ima eden hizmete bağlamamı önerir. Ayrıca denetleyicim ve servis katmanlarım arasında sıkı bağlantıyı zorlar.

Hizmet / depo katmanlarını denetleyiciye enjekte etmek için Bağımlılık Enjeksiyonu kullanıyorum ve bu nedenle denetleyiciden bağlama erişemiyorum.

Benim çözümüm, hizmet / depo katmanlarının bağlamın aynı örneğini kullanmasını sağlamaktı - Singleton.

Bağlam Singleton Sınıfı:

Referans: http://msdn.microsoft.com/tr-tr/library/ff650316.aspx
ve http://csharpindepth.com/Articles/General/Singleton.aspx

public sealed class MyModelDbContextSingleton
{
  private static readonly MyModelDbContext instance = new MyModelDbContext();

  static MyModelDbContextSingleton() { }

  private MyModelDbContextSingleton() { }

  public static MyModelDbContext Instance
  {
    get
    {
      return instance;
    }
  }
}  

Havuz Sınıfı:

public class ProjectRepository : IProjectRepository
{
  MyModelDbContext context = MyModelDbContextSingleton.Instance;
  [...]

Bağlamın bir kez başlatılması ve hizmet / depo katmanlarınızın oluşturucularına veya İş Birimi modelini uygulayan okuduğum başka bir çözüm gibi başka çözümler de vardır. Eminim daha fazlası var ...


9
... çoklu iş parçacığı kullanmayı denediğiniz anda bu bozulmuyor mu?
CaffGeek

8
Bir bağlam gerekenden daha uzun süre açık kalmamalıdır, sonsuza kadar açık tutmak için bir Singleton kullanmak yapmak istediğiniz son şeydir.
enzi

3
İstek başına bunun iyi uygulamalarını gördüm. Statik anahtar sözcüğünü kullanmak yanlıştır, ancak bu kalıbı isteğin başlangıcında somutlaştırmak ve isteğin sonunda imha etmek için yaparsanız, bu yasal bir çözüm olacaktır.
Aidin

1
Bu gerçekten kötü bir tavsiye. DI kullanıyorsanız (burada kanıtları göremiyorum?) O zaman DI konteynerinizin bağlam ömrünü yönetmesine izin vermelisiniz ve muhtemelen isteğe bağlı olmalıdır.
Casey

3
Bu kötü. KÖTÜ. KÖTÜ. KÖTÜ. KÖTÜ. Özellikle bu bir web uygulamasıysa, statik nesneler tüm iş parçacıkları ve kullanıcılar arasında paylaşıldığı için. Bu, web sitenizin eşzamanlı olarak birden çok kullanıcısının veri içeriğinizde durduracağı, potansiyel olarak bozulacağı, niyet etmediğiniz değişiklikleri kaydettiği veya hatta rastgele çökmeler oluşturacağı anlamına gelir. DbContexts ASLA iş parçacıkları arasında paylaşılmamalıdır. Sonra statiklerin asla yok
edilmemesi

3

Benim durumumda, ASP.NET Identity Framework kullanıyordum. UserManager.FindByNameAsyncBir ApplicationUservarlık almak için yerleşik yöntemi kullanmıştım . Sonra farklı bir yeni oluşturulan bir varlık üzerinde bu varlık referans çalıştı DbContext. Bu, başlangıçta gördüğünüz istisna ile sonuçlandı.

Yeni oluşturarak bu çözüldü ApplicationUseryalnızca varlık Idgelen UserManageryöntem ve bu yeni bir varlık referans.


1

Aynı sorunu yaşadım ve Güncellemeye çalıştığım nesnenin yeni bir örneğini oluşturmayı çözebilirim. Sonra o nesneyi depoma geçtim.


Örnek kod ile yardımcı olabilir misiniz. ? ne demeye çalıştığınız belli olacak
BJ Patel

1

Bu durumda, hatanın çok açık olduğu ortaya çıkar: Entity Framework, birden çok örneğini IEntityChangeTrackerveya genellikle birden çok örneğini kullanan bir varlığı izleyemez DbContext. Çözeltilerdir: kullanımı bir örneği DbContext; gerekli tüm varlıklara tek bir depo üzerinden erişme (bir örneğine bağlı olarak DbContext); veya bu özel durumu istisna eden bir depodan erişilen tüm varlıklar için izlemeyi kapatmak.

.Net Core Web API'sinde bir kontrol deseninin tersini takip ederken, sık sık aşağıdaki gibi bağımlılıklara sahip denetleyicilere sahip olduğumu görüyorum:

private readonly IMyEntityRepository myEntityRepo; // depends on MyDbContext
private readonly IFooRepository fooRepo; // depends on MyDbContext
private readonly IBarRepository barRepo; // depends on MyDbContext
public MyController(
    IMyEntityRepository myEntityRepo, 
    IFooRepository fooRepo, 
    IBarRepository barRepo)
{
    this.fooRepo = fooRepo;
    this.barRepo = barRepo;
    this.myEntityRepo = myEntityRepo;
}

ve kullanım gibi

...
myEntity.Foo = await this.fooRepository.GetFoos().SingleOrDefaultAsync(f => f.Id == model.FooId);
if (model.BarId.HasValue)
{
    myEntity.Foo.Bar = await this.barRepository.GetBars().SingleOrDefaultAsync(b => b.Id == model.BarId.Value);
}

...
await this.myEntityRepo.UpdateAsync(myEntity); // this throws an error!

Her üç havuz da DbContextistek başına farklı örneklere bağlı olduğundan, sorunu önlemek ve ayrı depoları korumak için iki seçeneğim vardır: çağrı başına yalnızca bir kez yeni bir örnek oluşturmak için DbContext enjeksiyonunu değiştirin:

// services.AddTransient<DbContext, MyDbContext>(); <- one instance per ctor. bad
services.AddScoped<DbContext, MyDbContext>(); // <- one instance per call. good!

veya alt varlık salt okunur bir şekilde kullanılıyorsa, söz konusu örnekte izlemeyi kapatma:

myEntity.Foo.Bar = await this.barRepo.GetBars().AsNoTracking().SingleOrDefault(b => b.Id == model.BarId);


0

Bir projeye IoC uyguladıktan sonra da aynı sorunu yaşadım (ASP.Net MVC EF6.2).

Genellikle bir denetleyicinin yapıcısında bir veri bağlamı başlatır ve tüm depolarımı başlatmak için aynı bağlamı kullanırdım.

Ancak IoC'yi depoları somutlaştırmak için kullanmak hepsinin ayrı bağlamlara sahip olmasına neden oldu ve bu hatayı almaya başladım.

Şimdilik, daha iyi bir yol düşünürken depoları ortak bir bağlamla yeniden yenilemeye geri döndüm.


0

Bu sorunla böyle karşılaştım. Öncelikle Orderbenim ApplicationUsertabloya bir referans gerektiren benim kaydetmek gerekir :

  ApplicationUser user = new ApplicationUser();
  user = UserManager.FindById(User.Identity.GetUserId());

  Order entOrder = new Order();
  entOrder.ApplicationUser = user; //I need this user before saving to my database using EF

Sorun şu ki, yeni Ordervarlığımı kaydetmek için yeni bir ApplicationDbContext başlatıyorum:

 ApplicationDbContext db = new ApplicationDbContext();
 db.Entry(entOrder).State = EntityState.Added;
 db.SaveChanges();

Bu yüzden sorunu çözmek için, ASP.NET MVC'nin yerleşik UserManager'ı kullanmak yerine aynı ApplicationDbContext'i kullandım.

Bunun yerine:

user = UserManager.FindById(User.Identity.GetUserId());

Mevcut ApplicationDbContext örneğimi kullandım:

//db instance here is the same instance as my db on my code above.
user = db.Users.Find(User.Identity.GetUserId()); 

-2

Hata kaynağı:

ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.Name);
ApplicationDbContext db = new ApplicationDbContent();
db.Users.Uploads.Add(new MyUpload{FileName="newfile.png"});
await db.SavechangesAsync();/ZZZZZZZ

Umarım birisi değerli zaman kazandırır


Bunun soruyu cevapladığından emin değilim. Belki bir bağlam yardımcı olabilir.
Stuart Siegler
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.