I have a MVC Web Application using the following approach:
public class MyController : Controller
{
public FooRepository fooRepository = new FooRepository();
public BarRepository barRepository = new BarRepository();
public ActionResult UpdateItems(int id, int range1, int range2)
{
Foo foo = fooRepository.GetItem(id);
List<Bar> bars = barRepository.GetItemsByRange(range1, range2);
// Some validation rules here...
DoSomeWork(foo, bars);
// Show confirmation / error message
}
private void DoSomeWork(Foo foo, List<Bar> bars)
{
foreach(int i = 0; i < bars.Count; i++)
{
bars[i].Prop1 = foo.Prop1; // This field is updated
bars[i].Owner = "someuser"; // This one too
bars[i].Status = BarStatus.SomeStatus; // This isn't...
}
foo.Status = FooStatus.SomeStatus; // Ok
// Calls DataContext.SubmitChanges()
fooRepository.SubmitChanges();
barRepository.SubmitChanges();
}
}
However, in some "random" cases (I see no pattern), one of the fields doesn't get updated, as noted in the comments.
It seems like LINQ isn't recognizing the field's update, so it gets excluded from the generated query.
Can anyone tell me if I'm missing something here, what could be causing it and/or how can I solve it?
Note: I don't get any Exception and can't verify this case in a development scenario.
From my experience if the error is random and you can't reproduce in development than the problem is user error.
Programming would be really hard if the .net framework or the CLR just randomly decided to do things differently.
You probably have an implicit/explicit bind exclusion floating around somewhere
[Bind(Exclude="...,Status,...")]
Just guessing of course
If Linq thinks that the Status is already BarStatus.SomeStatus, then it won't update it.
What can happen is that you find a record with the status set to this value, and then some other routine changes it, and then, if you are using your same DataContext, you will get the old value from the cached copy and hence Linq thinks that it does not need to update it.
Related
Using NopCommerce 3.8, Visual Studio 2015 proff.
I have created a plugin that is responsible for making restful calls to my Web API that exposes a different DB to that of Nop.
The process is run via a nop Task, it successfully pulls the data back and i can step through and manipulate as i see fit, no issues so far.
Issue comes when i try to update a record on the product table, i perform the update... but nothing happens no change, no error.
I believe this is due to the Context having no idea about my newly instantiated product object, however I'm drawing a blank on what i need to do in relation to my particular example.
Similar questions usually reference a "model" object that is part of the parameter of the method call, "model" has the method ToEntity which seems to be the answer in similar question in stack.
However my example doesn't have the ToEntity class/method possibly because my parameter is actually a list of products. To Clarify here my code.
Method in RestClient.cs
public async Task<List<T>> GetAsync()
{
try
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(ApiControllerURL);
var taskModels = JsonConvert.DeserializeObject<List<T>>(json);
return taskModels;
}
catch (Exception e)
{
return null;
}
}
Method in my Service Class
public async Task<List<MWProduct>> GetProductsAsync()
{
RestClient<MWProduct> restClient = new RestClient<MWProduct>(ApiConst.Products);
var productsList = await restClient.GetAsync();
InsertSyncProd(productsList.Select(x => x).ToList());
return productsList;
}
private void InsertSyncProd(List<MWProduct> inserted)
{
var model = inserted.Select(x =>
{
switch (x.AD_Action)
{
case "I":
//_productService.InsertProduct(row);
break;
case "U":
UpdateSyncProd(inserted);
.....
Then the method to bind and update
private void UpdateSyncProd(List<MWProduct> inserted)
{
var me = inserted.Select(x =>
{
var productEnt = _productRepos.Table.FirstOrDefault(ent => ent.Sku == x.Sku.ToString());
if(productEnt != null)
{
productEnt.Sku = x.Sku.ToString();
productEnt.ShortDescription = x.ShortDescription;
productEnt.FullDescription = x.FullDescription;
productEnt.Name = x.Name;
productEnt.Height = x.Pd_height != null ? Convert.ToDecimal(x.Pd_height) : 0;
productEnt.Width = x.Pd_width != null ? Convert.ToDecimal(x.Pd_width) : 0;
productEnt.Length = x.Pd_depth != null ? Convert.ToDecimal(x.Pd_depth) : 0;
productEnt.UpdatedOnUtc = DateTime.UtcNow;
}
//TODO: set to entity so context nows and can update
_productService.UpdateProduct(productEnt);
return productEnt;
});
}
So as you can see, I get the data and pass data through to certain method based on a result. From that list in the method I iterate over, and pull the the entity from the table, then update via the product service using that manipulated entity.
So what am I missing here, I'm sure its 1 step, and i think it may be either be because 1) The context still has no idea about the entity in question, or 2) Its Incorrect calls.
Summary
Update is not updating, possibly due to context having no knowledge OR my methodology is wrong. (probably both).
UPDATE:
I added some logger.inertlog all around my service, it runs through fine, all to the point of the call of update. But again I check the product and nothing has changed in the admin section.
plugin
I have provided the full source as i think maybe this has something to do with the rest of the code setup possibly?
UPDATE:
Added the following for testin on my execute method.
var myprod = _productRepos.GetById(4852);
myprod.ShortDescription = "db test";
productRepos.Update(myprod);
This successfully updates the product description. I moved my methods from my service into the task class but still no luck. The more i look at it the more im thinking that my async is killing off the db context somehow.
Turned of async and bound the getbyid to a new product, also removed the lambda for the switch and changed it to a foreach loop. Seems to finally update the results.
Cannot confirm if async is the culprit, currently the web api seems to be returning the same result even though the data has changed (some wierd caching by deafult in .net core? ) so im creating a new question for that.
UPDATE: It appears that the issue stems from poor debugging of async. Each instance I am trying to iterate over an await call, simply put im trying to iterate over a collection that technically may or may not be completed yet. And probably due to poor debugging, I was not aware.
So answer await your collection Then iterate after.
I have an object which I have defined , the class which I define my object from that has a variable. The type of this variable is the same as this class, see below:
public class _car
{
public _car()
{
}
_car BMW = null;
}
.
.
.
Pay attention the last line is global definition of an object machine.
My question is if in a method which is not located in _car class does something like this:
public another_Class
{
public another_class()
{
}
public _car machine = new _car();
public int this_Methode()
{
if (Machine.BMW == null){
Machine.BMW = new _car();
return 1;
}
return 0;
}
public void main_Methode()
{
int i=this_Methode();
i+=this_Methode();
//We run main_method in somewhere in our program now you say i is 0 or 1 or2 ?
}
}
think in this way //We run main_method now you tell me i's value? is 0 or 1 or 2?
To respond after your edits:
It's not clear where Machine.BMW is coming from. But if it is available at runtime, then it will be populated by the following method. So the first time it runs, it will return 1 to I.
public int this_Methode()
{
if (Machine.BMW == null){
Machine.BMW = new _car();
return 1;
}
return 0;
}
int i=this_Methode(); //i = 1 as new car was created.
i+=this_Methode(); Unless there is some other code running, this_Methode() will return zero as the car was already created.
you tell me i's value? Is 0 or 1 or 2? It will be 1 based on what you have shown in the code. But if there was other cod that affected Machine.BMW and set it to null, then it would be 2.
I like to create a test project in Visual Studio to try these kinds of things out. There is a free version called Visual Studio Express that you can use. Just create a Console app and try it out. This will help answer these questions quickly as you can try it and see if it works as expected. I do this all the time when something isn't working the way I think it should.
Greg
It looks like you are trying to learn more about C# and classes. Let me give you a few things that may help you out. This is not a direct answer to your question, as more info is needed to properly answer it. But a few pointers in general may help you out and let you clarify the issue:
In your class, the property _car is not initialized with an instance of a BMW, so it will be null when new instances are created.
You then have the line public _car machine = new _car()
This line is most likely inside of a class, as you can't have it just in a C# file on it's own. If this came from a Console.App, it's probably inside the Main Program so it run when you start it, and then it would be available to the rest of the app at runtime.
In another_class, you have a method which check to see if if BMW is null, and if not, it creates a new car. BMW will always be null here, as it has not been created before.
So even though you have the "global" variable, the "another_class" has no direct reference to it, so it's not going to see it. So I think the answer to your question is that it is going to always be null, not "live."
I'm having difficulty making a change to an entity object through a new context. I've had this work plenty of times before, but in this instance I'm getting the old "The ObjectContext instance has been disposed" exception.
Here's my quick edit/save code:
private void SaveChanges()
{
using (var context = new Manticore.ManticoreContext(Global.ManticoreClient))
{
**context.AttachTo("Users", Global.LoggedInUser);**
Global.LoggedInUser.FirstName = this.FirstNameText.Text;
Global.LoggedInUser.LastName = this.LastNameText.Text;
Global.LoggedInUser.Email = this.EmailText.Text;
context.SaveChanges();
}
}
The Global.LoggedInUser property (which is instantiated):
public static Manticore.User LoggedInUser
{
get
{
return HttpContext.Current.Session["LoggedInUser"] as Manticore.User;
}
set
{
HttpContext.Current.Session["LoggedInUser"] = value;
}
}
And the kicker is here's a quick unit test which works (no assert right now, but no exception being thrown):
private User _testUser;
private TestInstanceBucket _testBucket;
[TestInitialize]
public void TestInitialize()
{
using (var context = new Manticore.ManticoreContext())
{
this._testBucket = new TestInstanceBucket(context);
this._testUser = this._testBucket.TestUser;
context.AddObject("Users", this._testUser);
context.SaveChanges();
}
}
[TestMethod]
public void User_ShouldBeAbleToChangeDetails()
{
using (var context = new ManticoreContext())
{
context.AttachTo("Users", this._testUser);
this._testUser.FirstName = "New";
context.SaveChanges();
}
}
Like I say, I've done code like this before and it's been fine. Have I been lucking out, or could storing the entity in the session be causing problems?
Update
I've moved the code from global to a pagebase class which the page using SaveChanges() inherits. Same problem so it rules our static classes/methods and storing the entity in the session somehow causing problems.
Update
So, after several hours of banging my head against a wall, I have a fix that's fairly simple if annoying. After the initial fetch of the user I now call
context.Detach(user);
I can only assume it's something to do with how fast the context is being disposed with garbage collection in ASP.NET compared to in the test environment.
In my EF experience this issue always seems to occur when the entity is still linked to the old context. I believe it is safest to always just re-load the entity using the new context (query on PK). I know it is not the most efficient but it avoids this problem.
I took a look at this page before answering, if you haven't tried some of its suggestions you might give them a go: EF Add/Attach.
My code seems straightforward:
bool rv = false;
var results = from user in Users
where user.userName.Equals(newUser.userName)
select user;
if (results.Count() == 0)
{
this.context.Users.Add(newUser);
this.context.SaveChanges();
rv = true;
}
return rv;
But this causes a DbEntityValidationException with an inner exception value that says:
OriginalValues cannot be used for entities in the Added state.
...What does that even mean? The only thing I can think of is that newUser shows a value of 0 for userID even though this has a private setter and userID, as a primary key, is supposed to be database-generated. But if that were the problem, it would not be possible to simply use Add() to insert an object into any EF database ever. So I'm confused.
Thanks in advance to anyone who can help shed some light on this.
ETA: newUser is created by the following code in my Controller:
[HttpPost]
public ActionResult CreateRegistration(FormVMRegistration newRegistration)
{
//draw info from form and add a new user to repository
User newUser = new User();
newUser.userName = newRegistration.userName;
newUser.screenName = newRegistration.screenName;
newUser.password = newRegistration.usersPW;
bool addSuccess = userRep.Add(newUser);
...
}
FormVMRegistration is just a ViewModel with nothing but string properties, to convey data from the Regiostration form to the Controller.
userRep is my User Repository.
I think it comes from the fact that you're using the newUser object before it is saved. Try to change this line
bool addSuccess = userRep.Add(newUser);
to
bool addSuccess = userRep.Add(newUser, newRegistration.userName);
and then (given passedUserName is a passed name from above) change your linq query to:
var results = from user in Users
where user.userName.Equals(passedUserName)
select user;
Good luck!
As near as can be determined, the problem seems to have stemmed from "deep" references to objects, or references to references. IOW, class Foo made a reference to class Bar which made a reference to class Baz which made a reference to class Foo... and apparently EF doesn't like this so much, because it's not easy to validate.
Maybe this is dreaming, but is it possible to create an attribute that caches the output of a function (say, in HttpRuntime.Cache) and returns the value from the cache instead of actually executing the function when the parameters to the function are the same?
When I say function, I'm talking about any function, whether it fetches data from a DB, whether it adds two integers, or whether it spits out the content of a file. Any function.
Your best bet is Postsharp. I have no idea if they have what you need, but that's certainly worth checking. By the way, make sure to publish the answer here if you find one.
EDIT: also, googling "postsharp caching" gives some links, like this one: Caching with C#, AOP and PostSharp
UPDATE: I recently stumbled upon this article: Introducing Attribute Based Caching. It describes a postsharp-based library on http://cache.codeplex.com/ if you are still looking for a solution.
I have just the same problem - I have multiply expensive methods in my app and it is necessary for me to cache those results. Some time ago I just copy-pasted similar code but then I decided to factor this logic out of my domain.
This is how I did it before:
static List<News> _topNews = null;
static DateTime _topNewsLastUpdateTime = DateTime.MinValue;
const int CacheTime = 5; // In minutes
public IList<News> GetTopNews()
{
if (_topNewsLastUpdateTime.AddMinutes(CacheTime) < DateTime.Now)
{
_topNews = GetList(TopNewsCount);
}
return _topNews;
}
And that is how I can write it now:
public IList<News> GetTopNews()
{
return Cacher.GetFromCache(() => GetList(TopNewsCount));
}
Cacher - is a simple helper class, here it is:
public static class Cacher
{
const int CacheTime = 5; // In minutes
static Dictionary<long, CacheItem> _cachedResults = new Dictionary<long, CacheItem>();
public static T GetFromCache<T>(Func<T> action)
{
long code = action.GetHashCode();
if (!_cachedResults.ContainsKey(code))
{
lock (_cachedResults)
{
if (!_cachedResults.ContainsKey(code))
{
_cachedResults.Add(code, new CacheItem { LastUpdateTime = DateTime.MinValue });
}
}
}
CacheItem item = _cachedResults[code];
if (item.LastUpdateTime.AddMinutes(CacheTime) >= DateTime.Now)
{
return (T)item.Result;
}
T result = action();
_cachedResults[code] = new CacheItem
{
LastUpdateTime = DateTime.Now,
Result = result
};
return result;
}
}
class CacheItem
{
public DateTime LastUpdateTime { get; set; }
public object Result { get; set; }
}
A few words about Cacher. You might notice that I don't use Monitor.Enter() ( lock(...) ) while computing results. It's because copying CacheItem pointer ( return (T)_cachedResults[code].Result; line) is thread safe operation - it is performed by only one stroke. Also it is ok if more than one thread will change this pointer at the same time - they all will be valid.
You could add a dictionary to your class using a comma separated string including the function name as the key, and the result as the value. Then when your functions can check the dictionary for the existence of that value. Save the dictionary in the cache so that it exists for all users.
PostSharp is your one stop shop for this if you want to create a [Cache] attribute (or similar) that you can stick on any method anywhere. Previously when I used PostSharp I could never get past how slow it made my builds (this was back in 2007ish, so this might not be relevant anymore).
An alternate solution is to look into using Render.Partial with ASP.NET MVC in combination with OutputCaching. This is a great solution for serving html for widgets / page regions.
Another solution that would be with MVC would be to implement your [Cache] attribute as an ActionFilterAttribute. This would allow you to take a controller method and tag it to be cached. It would only work for controller methods since the AOP magic only can occur with the ActionFilterAttributes during the MVC pipeline.
Implementing AOP through ActionFilterAttribute has evolved to be the goto solution for my shop.
AFAIK, frankly, no.
But this would be quite an undertaking to implement within the framework in order for it to work generically for everybody in all circumstances, anyway - you could, however, tailor something quite sufficient to needs by simply (where simplicity is relative to needs, obviously) using abstraction, inheritance and the existing ASP.NET Cache.
If you don't need attribute configuration but accept code configuration, maybe MbCache is what you're looking for?