EntityFramework ObjectContext Refresh issue - entity-framework-4

I have DataContext.Refresh Method:
public void RefreshDataSource()
{
_entities.Refresh(RefreshMode.ClientWins,Departments);
}
And observable collection:
public ObservableCollection<Department> Departments
{
get
{
if (_departments == null && _entities != null)
{
_entities.Departments.Include("Drivers").ToArray();
_departments = new EntityObservableCollection<Department>(_entities.Departments);
}
return _departments;
}
}
If i update records outside context i see only changed records but can't see inserted and removed. Why?

Because Refresh doesn't look for new records. It takes records you already have and updates them with current values. It also probably doesn't handle deleted records especially if you use ClientWins strategy which takes your state as more important.

Related

Deleting a document using ContentService API doesn't refresh cache

In Umbraco 7, I used ContentService API to insert, update or delete documents. After inserting or updating a document, the correct content shows immediately, but after deleting, removed data can be viewed because of the cache. To clear cache, I delete DistCache folder's data from App_data.
How can I refresh the cache programmatically?
Here is my code:
public string DeleteStudent(int studentID)
{
IContentService service = ApplicationContext.Current.Services.ContentService;
if (studentID != 0)
{
IContent student = service.GetById(studentID);
if (student != null && student.ContentType.Alias == "dtStudent")
{
service.Delete(student);
return "Done!";
}
}
return "Error!";
}
Normally you shouldn't have to update the cache manually after a delete (you can check the log file for possible errors)
If you want to do a manual refresh, you can do so by calling the following method
umbraco.library.RefreshContent();
Note: In an Umbraco instance with many nodes the method is very slow
As #Harvey said in the comment, using service.MoveToRecycleBin works
fine. That method will unpublish the content, then move it to the
recycle bin.
Final code:
public string DeleteStudent(int studentID)
{
IContentService service = ApplicationContext.Current.Services.ContentService;
if (studentID != 0)
{
IContent student = service.GetById(studentID);
if (student != null && student.ContentType.Alias == "dtStudent")
{
service.MoveToRecycleBin(student);
return "Done!";
}
}
return "Error!";
}
You can also clear the cache in the Umbraco back office if you right click on the ellipses by the Content tab and then click "Republish entire site". This is technically not a programmatic way but it's quick/easy and does the job!
https://i.stack.imgur.com/MIUOh.jpg

breeze BeforeSaveEntity does not save calculated value

i need to calculate some values on the serverside when a specific entity gets updated.
if i update the entity the following code gets executed (C_CompletePrice gets set) and it even gets reflected on clientside (clientside breeze gets all the properties nicely back)
but when i check the db nothing is saved. so when clearing the browser cache and checking the entity again there are the old values...
private bool BeforeSaveTransaction(tblTransactions transaction, EntityInfo info)
{
transaction.C_CompletePrice = 11111111;
return true;
...
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
var entity = entityInfo.Entity;
if (entity is tblTransactions)
{
return BeforeSaveTransaction(entity as tblTransactions, entityInfo);
}
...
i'm using breeze 1.4.6
on the server i'm using Breeze.WebApi and Breeze.WebApi.EF
the model i'm using: http://pastebin.com/Dc03DrNe
Update
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
foreach (Type entityType in saveMap.Keys)
{
if (entityType.Name == "tblTransactions")
{
foreach (EntityInfo ei in saveMap[entityType])
{
CalculateTransaction(ei);
}
}
}
return base.BeforeSaveEntities(saveMap);
}
private void CalculateTransaction(EntityInfo entityInfo)
{
tblTransactions transaction = (tblTransactions) entityInfo.Entity;
transaction.C_CompletePrice = 1234567;
...
Using BeforeSaveEntities results in the same strange behaviour:
Entites on the client gets updatet :)
DB not :(
So before i'll use now #dominictus solution (overriding SaveAll) i'm kindly asking for the purpose of those methods i've used (bool BeforeSaveEntity(...) and BeforeSaveEntities(saveMap)). I've consulted the doc and i've watched bryan noyes brilliant pluralsight course but still my simple mind doesn't get it :)
Did you update the EntityInfo.OriginalValuesMap as described in the ContextProvider topic?
I do it a bit different. Here is an example where I save a timestamp when object was changed. This is a method in my Context class.
public override int SaveChanges()
{
foreach (
var entry in
this.ChangeTracker.Entries()
.Where((e => (e.State == (EntityState) Breeze.WebApi.EntityState.Added || e.State == (EntityState) Breeze.WebApi.EntityState.Modified))))
{
if (entry.Entity.GetType() == typeof(MyClass))
{
var entity = entry.Entity as MyClass;
if (entity != null) entity.UpdatedDateTime = DateTime.Now;
}
}
return base.SaveChanges();
}
In your case, you could just write: entity.C_CompletePrice = 11111111;
As for method BeforeSaveEntity I prefer using BeforeSaveEntities. Check breeze documentation for examples.

Two checks IValidatableObject in one entity

Is the essence of Project, the creation of which is necessary to check whether there is already an entity with the same name. When editing needs such as checking, but keep in mind that the old and the new name of the entity can be matched.
You also need to display an error message. For this I use interface IValidatableObject, but do not know how to tell the Validate method the object is currently being edited or created
DbContext.ValidateEntity takes the IDictionary<Object, Object> items as the second parameter. You can pass any data there and the data you pass will be passed to IValidatableObject.Validate in the ValidationContext.Items
Assuming you refer to check EF cant do for you.
This is actually difficult to check. You are checking an entity after it has been added to the context. It should not check itself and needs to consider other items in context that are not yet saved. As well as the DB. There are several 3 combinations plus an self recognition. Record a an entity record in LOCAL when ID is blank/new ie multiple new inserts needs careful coding. (Consider using temp IDs)
the not yet saved entries should be in context
Context.Set<TPoco>().Local
and get data from DB and keep in a temp list. BUT dont put in context.
Or use a SECOND context.
var matchingSet = Context.Set<TPoco>().AsNoTracking() // not into context...
.Where(t=>t.field == somevalue).ToList();
So what about logical and actual duplicates on the DB. Logical duplicates are duplicates on a field with no unique index that from a business perspective should be unique.
If you want to check those...
You need to read the DB.... BUT if these records are currently being changed, you CAN NOT just put them into the Context. You would overwrite them.
But what if the values the logical key values have changed?
Something caused a logical dup on a record on the DB may no longer be a dup once saved or vice verse. Is that still a dup or not ?
So you need to decide how you match LOCAL versus loaded records.
Ie check LOCAL and matching DB records and decidr what to do if a record is in both, only local or only db.
LOCAL ONLY and DB Only is easy.
But in both... That is your business process decision.
Problem is solved using method ModelState.AddModelError (string, string) in actions Edit and Create.
[HttpPost]
[HandleError(View="AjaxError")]
public ActionResult Edit(ProjectsViewData data)
{
if (ModelState.IsValid)
{
if (!ContainsProject(data.CurrentObject.Name))
{
db.Projects.Attach(data.CurrentObject);
db.ObjectStateManager.ChangeObjectState(data.CurrentObject, EntityState.Modified);
db.SaveChanges();
return Projects(data);
}
else
{
int projectId = (from p in db.Projects
where p.Name == data.CurrentObject.Name
select p.ProjectID).FirstOrDefault();
if (projectId == data.CurrentObject.ProjectID)
{
db.Projects.Attach(data.CurrentObject);
db.ObjectStateManager.ChangeObjectState(data.CurrentObject, EntityState.Modified);
db.SaveChanges();
return Projects(data);
}
else
{
ModelState.AddModelError("Name", Localizer.ProjectAlreadyExists);
}
}
}
data.ObjectToEdit = data.CurrentObject;
return Projects(data);
}
[HttpPost]
[HandleError(View = "AjaxError")]
public ActionResult Create(ProjectsViewData data)
{
if (ModelState.IsValid)
{
if (!ContainsProject(data.CurrentObject.Name))
{
db.Projects.AddObject(data.CurrentObject);
db.SaveChanges();
return Projects(data);
}
else
{
ModelState.AddModelError("Name", Localizer.ProjectAlreadyExists);
}
}
data.ObjectToAdd = data.CurrentObject;
return Projects(data);
}
Helper method:
private bool ContainsProject(string projectName)
{
if (projectName != null)
{
projectName = Regex.Replace(projectName.Trim(), "\\s+", " ");
List<string> projects = new List<string>();
var projectNames = (from p in db.Projects
select p.Name.Trim()).ToList();
foreach (string p in projectNames)
{
projects.Add(Regex.Replace(p, "\\s+", " "));
}
if (projects.Contains(projectName))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}

Adding record duplicates other object using entity framework

I am trying to add a new record in an MVC controller method using Entity framework.
When i just used "InsertOrUpdate" the audittype got duplicated. Based on the answer from Entity Framework adding record with a related object i hoped to fix it pretty qiock. This is the code I have right now:
Controller:
if (ModelState.IsValid)
{
Audit newAudit = Factory.GetNew();
newAudit.Name = model.Name;
newAudit.Deadline = model.Deadline;
newAudit.AuditType = auditTypeRepository.Find(model.SelectedAuditTypeId);
Repository.InsertOrUpdate(newAudit);
Repository.Save();
return RedirectToAction(MVC.Audits.Details(newAudit.Id));
}
Repository:
public override void InsertOrUpdate(Qdsa.WebApplications.AuditMaster.Data.Audit model)
{
if (model.Id == default(int))
{
// New entity
context.Audits.Add(model);
}
else
{
// Existing entity
model.ModifiedOn = DateTime.Now;
context.Entry(model).State = EntityState.Modified;
}
//If I leave out the code below the AuditType will be duplicated
if (model.AuditType != null)
{
context.Entry<AuditType>(model.AuditType).State = EntityState.Unchanged;
}
}
public virtual void Save()
{
context.SaveChanges();
}
So i thought I fixed the problem. However, AuditType has Child objects too. And now these childobjects get duplicated.
What is the right way to add entities with child objects which already exists?
Because the AuditType is required I can't save it without first and then update it. any suggestions?
UPDATE:
Both the AuditRepostory and the AuditTypeRepository inherit from BaseRepository which has the context as:
protected DBContext context = new DBContext ();
public virtual T Find(int id)
{
return All.SingleOrDefault(s => s.Id == id);
}
I can imagine two reasons for the problem:
Either auditTypeRepository.Find performs a no tracking query (with .AsNoTracking())
Or you are using a context instance per repository, so that Repository and auditTypeRepository are working with two different contexts which will indeed result in a duplication of the AuditType because you don't attach it to the the context that corresponds with Repository (except in the line with your comment).
If the latter is the case you should rethink your design and inject a single context instance into all repositories instead of creating it inside of the repositories.
I think the problem is from here:
newAudit.AuditType = auditTypeRepository.Find(model.SelectedAuditTypeId);
Change that like this:
newAudit.AuditTypeId = model.SelectedAuditTypeId;

Null value lingers in cache after Cache.Remove(key)

I added a caching layer between my ASP.NET MVC application and database using the GetOrStore<T> cache helper below.
This includes caching users' system roles.
When I remove a cached roles object for a signed-in user via HttpRuntime.Cache.Remove(userRoleCacheKey), subsequent requests fail with a NullReferenceException because the cache helper is returning a null value for the role cache, even though the cached key should not exist and the helper should regenerate it.
It seems like the cached key lingers around with a null value. The exception won't budge until I request a role-heavy page a few seconds later.
Why is my cache breaking?
public static class CacheExtensions
{
public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator)
{
var result = cache.Get(key);
if (result == null)
{
result = generator();
if (result != null) // can't store null values in cache.
{
cache[key] = result;
}
}
return (T)result;
}
}
Here is the code that fetches the user's roles and caches it:
public override string[] GetRolesForUser(string userId)
{
return HttpRuntime.Cache.GetOrStore<string[]>(
"RolesForUser[" + userId + "]",
() => Con.Query<string>("SELECT Role FROM vw_UserRoles WHERE UserId = #userId", new { userId = Guid.Parse(userId) }).ToArray());
}
where Con retrieves an open IDbConnection.
Minor issue in your code that can hide the problem from you:
if (result != null) // can't store null values in cache.
{
cache[key] = result;
}
What about else clause? The result returned anyway, even if it is null value. Throw InvalidOperationException in that case and debug your queries passed as generator parameter.

Resources