Entity Framework nicht aktualisieren von Daten bei der Verwendung von Repository-Muster
Ich bin mit dem repository und unit of work Pattern und dependency injection Zugriff auf die Datenbank mit entity framework 5 in meiner web-Anwendung. Ich habe eine User
Klasse, von dem Entity Framework generiert eine Code-First-Datenbank.
public class User
{
public Guid UserId { get; set; }
public string UserName { get; set; }
.
.
.
public string LanguagePreference { get; set; }
public virtual List<Role> Roles { get; set; }
public virtual List<Branch> Branches { get; set; }
}
Ich habe eine UserService
Klasse, die verwendet wird zum Hinzufügen oder Aktualisieren von Benutzern. Diese Klasse nimmt eine IUserUnitOfWork
als parameter in den Konstruktor und die Einheit injiziert eine UserUnitOfwork
. Die IUserUserOfWork
enthält eine IRepository<User>
eine IRepository<Location>
und ein IRepository<Role>
. Diese sind als Repository<T>
durch die DI-bootstrapper. Die IUserUnitOfWork stellt die verschiedenen Repositories mit dem gleichen entity framework DbContext. Ich Tat dies, wie ich Probleme bei der Aktualisierung der viele-zu-viele-Beziehungen mit Bezug auf den Benutzer (Standorte und Rollen).
UserUnitOfWork:
public IRepository<Branch> BranchRepository {get; set;}
public IRepository<Role> RoleRepository { get; set; }
public IRepository<User> UserRepository { get; set; }
public DbContext Context { get; set; }
public UserUnitOfWork(DbContext context, ITransientErrorDetectionStrategy errorDetectionStrategy,RetryStrategy retryStrategy )
{
Context = context;
BranchRepository = new Repository<Branch>(context, errorDetectionStrategy, retryStrategy);
RoleRepository = new Repository<Role>(context, errorDetectionStrategy, retryStrategy);
UserRepository = new Repository<User>(context, errorDetectionStrategy, retryStrategy);
}
Die Repository-Klasse verwendet dann Entity Framework 5, um den Zugriff auf die Datenbank.
Beispiel der Methode aus dem Repository.FirstOrDefault:
public virtual T FirstOrDefault(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
{
T result = null;
_retryPolicy.ExecuteAction(() =>
{
IQueryable<T> entities = GetHelper(filter, orderBy, includeProperties);
result = entities.FirstOrDefault();
});
return result;
}
Und Aktualisieren von Repository:
public virtual void Update(T entity)
{
if (_dbContext.Entry(entity).State == System.Data.EntityState.Detached)
{
_dbContext.Set<T>().Attach(entity);
_dbContext.Entry(entity).State = System.Data.EntityState.Modified;
}
}
Also mein problem ist jetzt, dass wenn ich ein update der Benutzer es korrekt aktualisiert die Daten in der Datenbank, und als ich auf log out und log in der ersten änderung funktioniert. Jedoch wenn ich das update erneut, und melden Sie sich und in die neue änderung nicht abgeholt, obwohl die Datenbank aktualisiert wird.
Ich fange an zu befürchten, dass der Ansatz, den ich genommen habe, ist falsch, kann mir jemand sagen, wie stellen Sie sicher, dass, wenn ich ein update Entity Framework erhalten immer die neueste version?
EDIT:
So, ich habe eine Anfrage Lifetime-Manager etwa so:
public class PerHttpRequestLifetimeManager : LifetimeManager
{
private readonly object key = new object();
public override object GetValue()
{
if (HttpContext.Current != null &&
HttpContext.Current.Items.Contains(key))
return HttpContext.Current.Items[key];
else
return null;
}
public override void RemoveValue()
{
if (HttpContext.Current != null)
HttpContext.Current.Items.Remove(key);
}
public override void SetValue(object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[key] = newValue;
}
}
In meine DI-bootstrapper ich nun meine setup-domain-Kontext wie folgt:
container.RegisterType<DbContext, DomainContext>(new PerHttpRequestLifetimeManager());
Es immer noch nicht zu sein scheinen zu arbeiten, bin ich etwas fehlt, anderes oder gebe ich es falsch?
EDIT 2:
Nur auf die Architektur:
Wir haben eine MVC-Anwendung, die verwendet, Angular JS, um ajax-Aufrufe zu einer Web-Api-service-layer. Die Web-Api eine ISomethingService injiziert. Es ist diese ISomethingService, der die repositories injiziert. Würde es etwas Verwirrung für die PerHttpRequestLifetimeManager, da es sowohl eine MVC-und Web API-Projekt am laufen?
EDIT 3:
Ein Beispiel, wie ich das speichern der bearbeiteten Benutzer:
Haben wir eine UserModel
Klasse, die verwendet wird für die Kommunikation zwischen dem ServiceLayer -> API -> UI layer und zurück. Die User
Klasse die man erzeugt, die von Entity Framework code first. Die EditUser-Methode in der UserService nimmt in einem UserModel
.
Ich dann user die _unitOfWork.UserRepository, um die entsprechenden Datenbank-Benutzer
var editedUser = _unitOfWork.UserRepository.FirstOrDefault(x => x.UserId == userModel.UserId);
Ich die Felder zuordnen, aus dem userModel die editedUser und ich dann anrufen (in der UserService)
_unitOfWork.UserRepository.Update(editedUser)
und nach
_unitOfWork.Save()
DOCH NOCH EIN EDIT:
Also ich habe bearbeitet eine einfache Methode, die updates zu einem einzigen text-Feld für die user-Tabelle (Sprache). Ich explizit die dispose-Methode aufrufen, nachdem Sie das update, um sicherzustellen, ich bin die Entsorgung der Methode.
public void SetUserLanguagePreference(Guid userId, string language)
{
var user = _unitOfWork.UserRepository.FirstOrDefault(x => x.UserId == userId);
user.LanguagePreference = language;
_unitOfWork.UserRepository.Update(user);
_unitOfWork.Save();
_unitOfWork.Dispose();
}
UnitOfWork.Dispose()
ruft die dispose-Methode des repositories und der Dbcontext
Die Datenbank richtig aktualisiert. Jedoch ist das Verhalten immer noch falsch. Wenn ich den Abmelden und in die erste ruft es den richtigen Wert. Wenn ich es erneut ändern und sich ausloggen und wieder ist es nicht aktualisieren. Das ist das Muster, bevor es das erste update, nachdem ich die log-out und in, aber wenn ich es wieder ändern, und melden Sie sich und in es nicht es abholen.
- Es klingt wie dein Kontext ist nicht erschaffen/zerstört werden pro Anfrage. Wie ist Ihr Kontext instanziiert wird für die Einspritzung?
- mit Einheit wie diesem: container.RegisterType<DbContext, DomainContext - >();
- Fallen alle diese "UnitOfWork" und "Repository" und die Arbeit mit nackten EF. Es erspart Ihnen Tonnen von Kopfschmerzen und Ihr code wird am Ende überraschend einfach.
- es ist ein bisschen spät für, dass ich denke, ich habe es während des gesamten Projekts mit keine Probleme bis jetzt.
- Welche Technologie verwenden Sie für dependency injection? (Ninject, Unity, AutoFac...)
- Ich bin mit Unity. War mit 2.x, aber bis jetzt bin ich versucht, ein upgrade auf 3
- Sie wollen vielleicht betrachten Sie so etwas wie eine HttpRequestLifetimeManager.
- Vielen Dank Erik, werde ich das prüfen
- siehe mein edit oben
- Wo rufst du die SaveChanges?
- siehe mein edit oben
Du musst angemeldet sein, um einen Kommentar abzugeben.
Schließlich nicht Bearbeiten, aber eine Antwort! Wir verwenden die anspruchsbasierte Authentifizierung und haben eine Klasse, überschreibt die
ClaimsPrinciple Authenticate
Methode, die aufgerufen wird, wenn ein Benutzer authentifiziert ist.War es nicht möglich, zu injizieren, die in dieser Methode mit DI-wie Sie es immer ging, um die leeren Konstruktor (nicht sicher, warum, aber so ist es).
Also stattdessen waren wir festlegen des repository in den leeren Konstruktor in etwa so:
Wenn die Authenticate-Methode wird aufgerufen, wir suchen Sie in unserer Datenbank nach einem Benutzer mit der beigefügten Ansprüche zu den "claimsprincipal". Wenn wir ein match, das wir hinzufügen, neue Ansprüche an den token, die dann für jeden Aufruf der Web-Api später. Dieses repository wurde nicht entsorgt werden (auch wenn alle anderen waren) und so, wenn ein Benutzer abgemeldet und in Sie habe die Daten von diesem selben Zusammenhang, die noch nicht entsorgt wurden aus der letzten Zeit, der Benutzer ist angemeldet.
Drei volle Tage versucht zu finden, dass man....
Sehen, ob das hilft: Wie bekomme ich Entity Framework 5 zu aktualisieren, veraltete Daten
Ich lief in das gleiche problem, es spielt keine Regenerierung aus der Datenbank wenn Sie bereits das Objekt in der ObjectContext, natürlich würde dies nur funktionieren, auf einer pro-Objekt-basis, aber das könnte genau das sein, was Sie brauchen.