Archive

Archive for the ‘c#’ Category

How to return clean JSON from EF object with data attributes

April 4th, 2011

Returning JSON is very simple in ASP.NET MVC:

 return Json(Person,  JsonRequestBehavior.AllowGet);

(Person is an Entity Framework object)

The problem with this is if Person have related objects, like e.g. Friends. Then the serializer will throw an error because it will get a circular reference when tying to serialize Person.

So one common solution is to just create a new anonymous object on the fly like so:

 return Json(new {Name = Person.Name, Age = Person.Age},  JsonRequestBehavior.AllowGet);

A second solution is to create a Poco class that you fill with data from the entity object.

A third solution is to “tag” your model members with Attributes. But you cannot do this with an EF class, since the codefile is autogenerated, and if you change the code then your changes will be overwritten the next time you use the designer.

The following code demonstrates how to make this work anyway.

public class PocoAttribute : System.Attribute
{
}

[MetadataType(typeof(PersonMeta))]
public partial class Person
{
}

public class PersonMeta
{
    [Poco]
    public string Name { get; set; }
    [Poco]
    public int Age { get; set; }
}

This is the same approach used to add data annotations to members in your EF objects. (See http://www.asp.net/mvc/tutorials/validation-with-the-data-annotation-validators-cs)

Now we need a way to automatically return a JsonResult with only the properties we have given our attribute to. For this we can create an extension method:

public static Dictionary<string, object> GetPocoDictionary(this EntityObject obj)
{
    PropertyInfo[] properties = obj.GetType().GetProperties();
    var pocoMembers = properties.Where(d=> d.GetCustomAttributes(typeof(PocoAttribute), true).Count() >= 1).ToArray();

    var metadataAttribute = (MetadataTypeAttribute)obj.GetType()
    .GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();

    var metaClassMembers = metadataAttribute.MetadataClassType.GetProperties()
    .Where(d=> d.GetCustomAttributes(typeof(PocoAttribute), true).Count() >= 1).ToArray();

    var combinedPoco = pocoMembers.Union(metaClassMembers);
    var allMembers = from propall in properties
                     join combined in combinedPoco on propall.Name equals combined.Name
                     select propall;

    var dict = new Dictionary<string, object>();
    foreach (var item in allMembers)
    {
        dict.Add(item.Name, item.GetValue(obj, null));
    }
    return dict;
}

This will take any EF object and return a dictionary with only the members that we have given the attribute to.
Now to use it:

 return Json(Person.GetPocoDictionary(),  JsonRequestBehavior.AllowGet);

That’s it!

Martin asp.net, c# , , ,

Community server REST API cache bug

June 2nd, 2009

I’ve been working with the community serverAPI lately and came across this bug:

It seems when doing a request for users with search parameters it does not cache correctly on all input parameters.  Specifically the id parameter.

I try doing the following request:

GET /api/membership.ashx/users/?id=2100,2200,2201

This gives me the user with id 2100, 2200 and 2201

But if you try something similar again before 30 sec (the time it cache the results), with different userids, then you get the same result as the first query returned:

GET /api/membership.ashx/users/?id=2101,3000,3001 returns the same

So what is up with that?

The bug is not in the client library (since I did this in fiddler), and neither the server side API component it seems. Using reflector I found that the  problem is in the generation of the cachekey in the UserQuery object in CS itself:


public string Key
{
get
{
return string.Format("PI{0}PS{1}SB{2}SO{3}ST{4}SUN{5}S{6}H{7}SID{8}RID{9}SEN{10}JD{11}JC{12}PD{13}PC{14}SCO{15}UN{16}EP{17}", new object[] {
this.PageIndex.ToString(), this.PageSize.ToString(), this.SortBy.ToString(), this.Order.ToString(), this.SearchText, this.SearchUsername.ToString(), this.Status.ToString(), this.IncludeHiddenUsers.ToString(), CSContext.Current.SettingsID.ToString(), this.RoleID.ToString(), this.SearchEmail.ToString(), this.JoinedDate.ToString(), this.JoinedDateComparer.ToString(), this.LastPostDate.ToString(), this.LastPostDateComparer.ToString(), this.SortClauseOverride,
(this.Usernames == null) ? "" : string.Join(",", this.Usernames), this.ExtendedParameters.ToString()
});
}
}

The ID’s aren’t included. I have posted at the community server forums, and they have confirmed the problem, so let’s hope it is fixed in the next version.

For now if you want several users, you have to either request by name/email i.e.
If you have to use the ID, you just have to hit the endpoint GET /api/membership.ashx/users/[userid] for each user.

Or if you don’t need the pagesize component (if you want all anyway) you could supply that uniquely for the 30 sec period of the cache, thus giving you fresh data. This is not completely safe though. See my post over at the community server forums for my suggestion.

Martin asp.net, c# , ,

Responsive webservice caching using threads

April 23rd, 2009

I worked on a project where I needed to have a user control that got data from a web service, this would slow down the site without proper caching. Usually when I get data from the database I do the caching every  5 minutes, and the person doing the request at that 5 min mark will get a small hit, but this is not a big hit so it’s acceptable.

When dealing with web services though, you will never know how long the request will take, and what about if the service is down? Here is how I did it:

In this first code block I check if the cache is in memory and if it is, then just get the object and everything is fine. If not I start a thread and let it  fetch the data for me.


//Get date from cache or insert in cache
if (Page.Cache.Get(fullCacheKey) == null)
{

//Make sure we just execute 1 thread, so we insert a threadlock value in cache, with a timeout of 1 minute
if (Page.Cache.Get(threadActiveCacheKey) == null)
{
//Set the threadlock
Page.Cache.Insert(threadActiveCacheKey, true, null, DateTime.Now.AddMinutes(1), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);

//Start a thread to fetch the webservice data, and insert it into cache.
Thread thr = new Thread(delegate() { DoSomeLongstuff(); });
thr.Start();
}

//In the meantime show the old cache
if (Page.Cache[backupCacheKeyPrefix + fullCacheKey] != null)
{
someObject = Page.Cache[backupCacheKeyPrefix + fullCacheKey];
}

}
else
{
someObject = Page.Cache[fullCacheKey];

//Remove the old cache here
Page.Cache.Remove(backupCacheKeyPrefix + fullCacheKey);
}

//Now databind or do whatever you want with the object

This means the very first user will not get the user control displayed, in my case that just means me, as this just happens whenever I shut down the server or app-pool. (or if the slidingcache later explained expires) This is acceptable in my case, if not you would have to make sure the very first request waits for the thread with Thread.Join.

Next up is the code that takes a while (probably under a sec, but still adds to the load time of the page).

What I do here is the web service call, I have removed that part for this example. But at the end I insert the result into memory with a 5 minute absolute expiration. I also remove the “threadlock” set earlier, I put this in so that I will not have several requests initiating their own thread trying to accomplish the very same thing.


private void DoSomeLongstuff()
{
//Do your Webservice call here, the part that takes time

//Create a CacheItemRemovedCallback to be called when the "real" cache is removed
CacheItemRemovedCallback onRemove = new CacheItemRemovedCallback(InsertIntoSafetyCache);

//Insert the result object into cache and remove the "threadlock"
Page.Cache.Insert(fullCacheKey, someObject, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Low, onRemove);
Page.Cache.Remove(threadActiveCacheKey);
}

Notice also the last parameter of the Cache.Insert method, this is what get’s called whenever the cache is deleted, in other words every 5 minutes.

This simply insert the very same cache that was deleted into memory again, with another key. I have set this to a 30 minute sliding expiration. So if it is accessed at least once every 30 minutes it is kept alive.


private void InsertIntoSafetyCache(String cacheKey, Object obj, System.Web.Caching.CacheItemRemovedReason reason) {
//When the "real" cache is removed, insert the object into cache again, but with a different key and different expiration. Used here is sliding expiration of 30 minutes.
Page.Cache.Insert(backupCacheKeyPrefix + cacheKey, obj, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(30), System.Web.Caching.CacheItemPriority.Low, null);
}

This is what happens:

  • When the 5 minutes are up I still have the object in memory, but with another name.
  • I show the old cache while the new one is fetching it’s new data.
  • When the new one has data, I delete the old one.
  • If the web service is down I will have data in memory for a long time, so I will at least show something. 30 minutes, and long after that because it is refreshed at each access.
  • At each request I will try to access the web service again (well as long as I don’t have a thread running)
  • When the web service is up again, I will get it’s data and delete the old cache

So what do you think?

Martin asp.net, c#, caching