Clear a group of cache in asp.net mvc 3 - asp.net-mvc-3

I am saving a list to cache. so that there is no need to go to the database eache time when the list needs.
here is the code
public IEnumerable<SelectListItem> GetCategoriesByParentId(int parentCategoryId)
{
string cacheKey = "PC" + parentCategoryId.ToString();
DateTime expiration = DateTime.Now.AddDays(1);
IEnumerable<SelectListItem> categoryList ;
categoryList = HttpContext.Current.Cache[cacheKey] as IEnumerable<SelectListItem>;
if (categoryList == null)
{
categoryList = GetCategoryList(parentCategoryId); // getting category from database
HttpContext.Current.Cache.Add(cacheKey, categoryList, null, expiration, TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Normal, null);
}
return categoryList;
}
My problem is when a new category is added to the database ,i am still gettting old category list .the list do not contain new category..How can clear the cache ( only category cache) after adding or changing a category
Any ideas?

When you insert a new category to the database I suppose you have the parent category id into which this category was inserted so you could remove it from the cache:
HttpContext.Current.Cache.Remove("PC" + parentCategoryId);
Now if the adding of a new category is not done by your application and you have no control over it you may take a look at cache expiration with SQL dependency.

Related

nopCommerce inserting entity fail

I'm creating import from XML function for categories.
First of all, using XDocument, I create list of categories to be added. I turned off isIdentity option for ID in Categories table, because I'm planning to use ID from XML.
XML example:
<cat>
<id>17</id>
<name>Category name</name>
<parent_id>0</parent_id>
</cat>
Then, I wrote method, which tries to get category by ID and update, or insert new:
var category = _categoryService.GetCategoryById(Id);
if (category != null)
{
category.Name = model.Name;
category.ParentCategoryId = model.ParentCategoryId;
category.UpdatedOnUtc = DateTime.UtcNow;
category.Published = true;
category.Deleted = false;
_categoryService.UpdateCategory(category);
}
else
{
category = new Core.Domain.Catalog.Category();
category.Id = model.Id;
category.ParentCategoryId = model.ParentCategoryId;
category.Name = model.Name;
category.UpdatedOnUtc = DateTime.UtcNow;
category.CreatedOnUtc = DateTime.UtcNow;
category.Published = true;
category.Deleted = false;
_categoryService.InsertCategory(category);
}
And then comes the most weird - application throws exception: Cannot insert the value NULL into column 'Id', table 'nopCommerce.dbo.Category'; column does not allow nulls. INSERT fails.
The statement has been terminated.
BUT even in debugger, category ID is not null.. Asking for help!
Thanks in advance!
Update: InsertCategory is a standard nopCommerce method:
/// <summary>
/// Inserts category
/// </summary>
/// <param name="category">Category</param>
public virtual void InsertCategory(Category category)
{
if (category == null)
throw new ArgumentNullException("category");
_categoryRepository.Insert(category);
//cache
_cacheManager.RemoveByPattern(CATEGORIES_PATTERN_KEY);
_cacheManager.RemoveByPattern(PRODUCTCATEGORIES_PATTERN_KEY);
//event notification
_eventPublisher.EntityInserted(category);
}
Your code seems fine presented here seems OK. I'd say you have a problem on your InsertCategory method which is most likely not making use of the id. This is just a guess... If this is not the answer, perhaps you can let us know what you have under it.
I just found out, that I need to change mapping class as well as database, removing autonumber option from ID. That was another pain, but solution is:
instead of this.HasKey(c=>c.Id) use:
this.Property(c=>c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None) ;

MVC3 Entity Framework Code First Updating Subset Related List of Items

I have a table of data with a list of key value pairs in it.
Key Value
--------------------
ElementName PrimaryEmail
Email someemail#gmail.ca
Value Content/Images/logo-here.jpg
I am able to generate new items on my client webpage. When, I create a new row on the client and save it to the server by executing the following code the item saves to the database as expected.
public ViewResult Add(CardElement cardElement)
{
db.Entry(obj).State = EntityState.Added;
db.SaveChange();
return Json(obj);
}
Now, when I want to delete my objects by sending another ajax request I get a failure.
public void Delete(CardElement[] cardElements)
{
foreach (var cardElement in cardElements)
{
db.Entry(cardElement).State = EntityState.Deleted;
}
db.SaveChanges();
}
This results in the following error.
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
I have tried other ways of deleting including find by id remove and attach and delete but obviously I am approaching in the right fashion.
I am not sure what is causing your issue, but I tend to structure my deletes as follows:
public void Delete(CardElement[] cardElements)
{
foreach (var cardElement in cardElements)
{
var element = db.Table.Where(x => x.ID == cardElement.ID).FirstOrDefault();
if(element != null)
db.DeleteObject(element);
}
db.SaveChanges();
}
although I tend to do database first development, which may change things slightly.
EDIT: the error you are receiving states that no rows were updated. When you pass an object to a view, then pass it back to the controller, this tends to break the link between the object and the data store. That is why I prefer to look up the object first based on its ID, so that I have an object that is still linked to the data store.

Using JQGrid with custom paging in Asp.Net MVC

I am using JQGrid with the Trirand.Web.Mvc class, and trying to figure out how to do custom paging.
I have seen the paging demos here
The problem with these demos is that they bind directly to a linq context object and lets MVC take care of the paging.
// This method is called when the grid requests data. You can choose any method to call
// by setting the JQGrid.DataUrl property
public JsonResult PerformanceLinq_DataRequested()
{
// Get both the grid Model and the data Model
// The data model in our case is an autogenerated linq2sql database based on Northwind.
var gridModel = new OrdersJqGridModel();
var northWindModel = new NorthwindDataContext();
// return the result of the DataBind method, passing the datasource as a parameter
// jqGrid for ASP.NET MVC automatically takes care of paging, sorting, filtering/searching, etc
return gridModel.OrdersGrid.DataBind(northWindModel.OrdersLarges);
}
The data set I want to bind to is quite complex and I am returning it from a stored procedure, which does the paging for me.
So all I have to give JQGrid is the correct size of rows for a specific page of the entire resultset. I can also return the total row count.
So I have my results in a List myListOfObjects.
I can pass this into the DataBind using myListOfObjects.AsQueryable()
The problem is, JQGrid thinks there is only {page size} rows, so does not display any of the paging options.
Is it possible to pass in the total row count?
Other grids, like Teleriks MVC grid allows you to pass in the Total row count, and it displays the paging correctly
Ok, so I've managed to solve this myself. There may be other ways to do it, if so I'd love to hear them!
The JQGrid.DataBind produces an JsonResult object, whose Data value is set to Trirands own object Trirand.Web.Mvc.JsonResponse
It's an internal class to their Trirand.Web.Mvc, so i had to copy its structure which I could see using Visual Studio debugging.
It has:
page - the current page number
records - the total record count
rows - of type Trirand.Web.Mvc.JsonRow (which I need to replicate too)
total - the total number of pages needed
JsonRow looks like:
cell - a string array of your columns
id - your row ID
So my code looked like this:
var jsonList = new List<JSONRow>();
myData.ForEach(x => jsonList.Add(new JSONRow(x)));
var jsonResult = Json (new
{
page = page,
rows = jsonList.ToArray(),
records = totalRows,
total = Math.Round((double)totalRows / rows, MidpointRounding.AwayFromZero)
}, JsonRequestBehavior.AllowGet);
return jsonResult;
My JsonRow looks like this:
public class JSONRow
{
public string[] cell { get; set; }
public string id { get; set; }
public JSONRow(MyObjectType myObject)
{
id = myObject.id;
cell = new string[3];
cell[0] = myObject.Col1;
cell[1] = myObject.Col2?? "";
cell[2] = myObject.Col3?? "";
}
}

Subsonic 3 Linq Projection Issue

OK I'm banging my head against a wall with this one ;-)
Given tables in my database called Address, Customer and CustomerType, I want to display combined summary information about the customer so I create a query to join these two tables and retrieve a specified result.
var customers = (from c in tblCustomer.All()
join address in tblAddress.All() on c.Address equals address.AddressId
join type in tblCustomerType.All() on c.CustomerType equals type.CustomerTypeId
select new CustomerSummaryView
{
CustomerName = c.CustomerName,
CustomerType = type.Description,
Postcode = address.Postcode
});
return View(customers);
CustomerSummaryView is a simple POCO
public class CustomerSummaryView
{
public string Postcode { get; set; }
public string CustomerType { get; set; }
public string CustomerName { get; set; }
}
Now for some reason, this doesn't work, I get an IEnumerable list of CustomerSummaryView results, each record has a customer name and a postcode but the customer type field is always null.
I've recreated this problem several times with different database tables, and projected classes.
Anyone any ideas?
I can't repro this issue - here's a test I just tried:
[Fact]
public void Joined_Projection_Should_Return_All_Values() {
var qry = (from c in _db.Customers
join order in _db.Orders on c.CustomerID equals order.CustomerID
join details in _db.OrderDetails on order.OrderID equals details.OrderID
join products in _db.Products on details.ProductID equals products.ProductID
select new CustomerSummaryView
{
CustomerID = c.CustomerID,
OrderID = order.OrderID,
ProductName = products.ProductName
});
Assert.True(qry.Count() > 0);
foreach (var view in qry) {
Assert.False(String.IsNullOrEmpty(view.ProductName));
Assert.True(view.OrderID > 0);
Assert.False(String.IsNullOrEmpty(view.CustomerID));
}
}
This passed perfectly. I'm wondering if you're using a reserved word in there?
This post seems to be referring to a similar issue...
http://groups.google.com/group/subsonicproject/browse_thread/thread/2b569539b7f67a34?hl=en&pli=1
Yes, the reason Rob's example works is because his projection's property names match exactly, whereas John's original example has a difference between CustomerType and type.Description.
This shouldn't have been a problem, but it was - the Projection Mapper was looking for properties of the same name and wasn't mapping a value if it didn't find a match. Therefore, your projection objects' properties would be default values for its type if there wasn't an exact name match.
The good news is, I got the latest source today and built a new Subsonic.Core.dll and the behavior is now fixed.
So John's code above should work as expected.
I just downloaded the latest build from 3/21/2010, which is about 2 months after the last poster on this thread, and the problem still exists in the packaged binary. Bummer.
Here what I have to do:
var data =
(from m in Metric.All()
where m.ParentMetricId == parentId
select new
{
m.MetricName,
m.MetricId,
})
.ToList();
var treeData =
from d in data
select new TreeViewItem
{
Text = d.MetricName,
Value = d.MetricId.ToString(),
LoadOnDemand = true,
Enabled = true,
};
return new JsonResult { Data = treeData };
If I try to do the projection directly from the Subsonic query, the Text property ends up with the ID, and the Value property ends up with the Name. Very strange.

How do I delete records from a child collection in LINQ to SQL?

I have two tables in my database connected by foreign keys: Page (PageId, other data) and PageTag (PageId, Tag). I've used LINQ to generate classes for these tables, with the page as the parent and the Tag as the child collection (one to many relationship). Is there any way to mark PageTag records for deletion from the database from within the Page class?
Quick Clearification:
I want the child objects to be deleted when the parent DataContext calls SubmitChanges(), not before. I want TagString to behave exactly like any of the other properties of the Page object.
I would like to enable code like the following:
Page page = mDataContext.Pages.Where(page => page.pageId = 1);
page.TagString = "new set of tags";
//Changes have not been written to the database at this point.
mDataContext.SubmitChanges();
//All changes should now be saved to the database.
Here is my situation in detail:
In order to make working with the collection of tags easier, I've added a property to the Page object that treats the Tag collection as a string:
public string TagString {
get {
StringBuilder output = new StringBuilder();
foreach (PageTag tag in PageTags) {
output.Append(tag.Tag + " ");
}
if (output.Length > 0) {
output.Remove(output.Length - 1, 1);
}
return output.ToString();
}
set {
string[] tags = value.Split(' ');
PageTags.Clear();
foreach (string tag in tags) {
PageTag pageTag = new PageTag();
pageTag.Tag = tag;
PageTags.Add(pageTag);
}
}
}
Basically, the idea is that when a string of tags is sent to this property, the current tags of the object are deleted and a new set is generated in their place.
The problem I'm encountering is that this line:
PageTags.Clear();
Doesn't actually delete the old tags from the database when changes are submitted.
Looking around, the "proper" way to delete things seems to be to call the DeleteOnSubmit method of the data context class. But I don't appear to have access to the DataContext class from within the Page class.
Does anyone know of a way to mark the child elements for deletion from the database from within the Page class?
After some more research, I believe I've managed to find a solution. Marking an object for deletion when it's removed from a collection is controlled by the DeleteOnNull parameter of the Association attribute.
This parameter is set to true when the relationship between two tables is marked with OnDelete Cascade.
Unfortunately, there is no way to set this attribute from within the designer, and no way to set it from within the partial class in the *DataContext.cs file. The only way to set it without enabling cascading deletes is to manually edit the *DataContext.designer.cs file.
In my case, this meant finding the Page association, and adding the DeleteOnNull property:
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true)]
public Page Page
{
...
}
And adding the DeleteOnNull attribute:
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true, DeleteOnNull = true)]
public Page Page
{
...
}
Note that the attribute needed to be added to the Page property of the PageTag class, not the other way around.
See also:
Beth Massi -- LINQ to SQL and One-To-Many Relationships
Dave Brace -- LINQ to SQL: DeleteOnNull
Sorry, my bad. That won't work.
It really looks like you need to be doing this in your repository, rather than in your Page class. There, you have access to your original data context.
There is a way to "attach" the original data context, but by the time you do that, it has become quite the code smell.
Do you have a relationship, in your Linq to SQL entity diagram, linking the Page and PageTags tables? If you don't, that is why you can't see the PageTags class from the Page class.
If the foreign key in the PageTags database table is set to Allow Nulls, Linq to SQL will not create the link when you drag the tables into the designer, even if you created a relationship on the SQL Server.
This is one of those areas where OR mapping can get kind of hairy. Providing this TagString property makes things a bit more convenient, but in the long run it obfuscates what is really happening when someone utilizes the TagString property. By hiding the fact that your performing data modification, someone can very easily come along and set the TagString without using your Page entity within the scope of a DataContext, which could lead to some difficult to find bugs.
A better solution would be to add a Tags property on the Page class with the L2S model designer, and require that the PageTags be edited directly on the Tags property, within the scope of a DataContext. Make the TagString property read only, so it can be genreated (and still provide some convenience), but eliminate the confusion and difficulty around setting that property. This kind of change clarifies intent, and makes it obvious what is happening and what is required by consumers of the Page object to make it happen.
Since Tags is a property of your Page object, as long as it is attached to a DataContext, any changes to that collection will properly trigger deletions or insertions in the database in response to Remove or Add calls.
Aaron,
Apparently you have to loop thru your PageTag records, calling DeleteOnSubmit for each one. Linq to SQL should create an aggregate query to delete all of the records at once when you call SubmitChanges, so overhead should be minimal.
replace
PageTags.Clear();
with
foreach (PageTag tag in PageTags)
myDataContext.DeleteOnSubmit(tag);
Aaron:
Add a DataContext member to your PageTag partial class.
partial class PageTag
{
DataClassesDataContext myDataContext = new DataClassesDataContext();
public string TagString {
..etc.
Larger code sample posted at Robert Harvey's request:
DataContext.cs file:
namespace MyProject.Library.Model
{
using Tome.Library.Parsing;
using System.Text;
partial class Page
{
//Part of Robert Harvey's proposed solution.
MyDataContext mDataContext = new TomeDataContext();
public string TagString {
get {
StringBuilder output = new StringBuilder();
foreach (PageTag tag in PageTags) {
output.Append(tag.Tag + " ");
}
if (output.Length > 0) {
output.Remove(output.Length - 1, 1);
}
return output.ToString();
}
set {
string[] tags = value.Split(' ');
//Original code, fails to mark for deletion.
//PageTags.Clear();
//Robert Harvey's suggestion, thorws exception "Cannot remove an entity that has not been attached."
foreach (PageTag tag in PageTags) {
mDataContext.PageTags.DeleteOnSubmit(tag);
}
foreach (string tag in tags) {
PageTag PageTag = new PageTag();
PageTag.Tag = tag;
PageTags.Add(PageTag);
}
}
}
private bool mIsNew;
public bool IsNew {
get {
return mIsNew;
}
}
partial void OnCreated() {
mIsNew = true;
}
partial void OnLoaded() {
mIsNew = false;
}
}
}
Repository Methods:
public void Save() {
mDataContext.SubmitChanges();
}
public Page GetPage(string pageName) {
Page page =
(from p in mDataContext.Pages
where p.FileName == pageName
select p).SingleOrDefault();
return page;
}
Usage:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string pageName, FormCollection formValues) {
Page updatedPage = mRepository.GetPage(pageName);
//TagString is a Form value, and is set via UpdateModel.
UpdateModel(updatedPage, formValues.ToValueProvider());
updatedPage.FileName = pageName;
//At this point NO changes should have been written to the database.
mRepository.Save();
//All changes should NOW be saved to the database.
return RedirectToAction("Index", "Pages", new { PageName = pageName });
}

Resources