Null as value in HttpRuntime.Cache.Add - c#

I want to store null for some of the keys in HttpRuntime.Cache as I dont want to again go to Database to find that there is no entry for that key.
So first time, it goes to database and fills the cache. The intent is to serve the following calls using cached data instead of doing the database call.
Here is the code that I am using the following:
Info info = null;
if (HttpRuntime.Cache["Info_" + id.ToString() + "_" + quantity.ToString()] != null)
info = HttpRuntime.Cache["Info_" + id.ToString() + "_" + quantity.ToString()] as Info;
if (info == null)
{
info = (from dd in dc.Infos
where dd.id == id && dd.active == true && dd.quantitytooffset == quantity
select dd).SingleOrDefault();
HttpRuntime.Cache.Add("Info_" + id.ToString() + "_" + quantity.ToString(), info, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null);
}
The last line of code i.e. HttpRuntime.Cache.Add throws an System.ArgumentNullException: Value cannot be null.
Any idea if this is possible or I need to use other datastructure to store null values and look up later?

You could use your own "null" value to place into cache.
For example,
private static Info NULL_INFO = new Info();
Then you could use that instead of null in HttpRuntime.Cache.Add and later after retrieval from cache check that you didn't get your NULL_INFO
if ( info == NULL_INFO) // if you don't have equality operator overloaded, otherwise you'd better use ReferenceEquals()
// return empty data
else if (info == null)
// proceed with try to load from database

I wrote a blog post recently about how the null keyword is often abused leading to this kind of confusion. In your particular case I would look at using an option type to indicate the lack or presence of data rather than null.
I have a simple implementation of an Option type you can use here
The usage would then be something like:
if (HttpRuntime.Cache["xyz"] == null)
// Impossible to make a distinction between whether or not the cache value is missing
// or if it is present but explicitly a null value...
HttpRuntime.Cache["xyz"] = Option<String>.None();
// We have now explicitly stated that the cache contains xyz but the value is null...
HttpRuntime.Cache["xyz"] = Option<String>.Some("hello world");
if (HttpRuntime.Cache["xyz"].IsSome)
{
// cache contains a non-null value for xyz...
}

You just need to check whether the value obtained from your data source is not null before trying to add it back to the cache, see below:
Info info = null;
if (HttpRuntime.Cache["Info_" + id.ToString() + "_" + quantity.ToString()] != null)
info = HttpRuntime.Cache["Info_" + id.ToString() + "_" + quantity.ToString()] as Info;
if (info == null)
{
info = (from dd in dc.Infos
where dd.id == id && dd.active == true && dd.quantitytooffset == quantity
select dd).SingleOrDefault();
if (info != null)
{
HttpRuntime.Cache.Add("Info_" + id.ToString() + "_" + quantity.ToString(), info, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null);
}
}
As you are using the as Info conversion at the beginning of your code, if the key isn't present in the cache it'll return a null value anyway, so you don't need to store the null value in cache. Storing null values in the cache doesn't really serve any use, so there's a reason the framework is not allowing you to do that.
Also as a slight side-note, it would be good to create your cache key once, then re-use it rather than reconstructing it every time it's used. For instance:
var key = string.Format("Info_{0}_{1}", id, quantity);
Then just use:
HttpRuntime.Cache[key]
When accessing it, it'll make your code less prone to typo errors.

This is a generic static class/method to solve this quite common problem (null which means "i checked and it doesn't exist, I don't need to check again").
This solution wraps the value like in the other answers.
Usage example
var user = await Cached.Get(
_cache, userName,
async () => _dbContext.LoadAsync<DbUser>(userName)));
Implementation
public class CachedEntry<T>
{
public CachedEntry(T value)
{
Value = value;
}
public T Value { get; }
}
public static async Task<T> Get<T>(IMemoryCache cache,
string key, Func<Task<T>> getDelegate)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
var cachedEntry = cache.Get<CachedEntry<T>>(key);
if (cachedEntry == null)
{
var result = await getDelegate();
cachedEntry = new CachedEntry<T>(result);
cache.Set(key, cachedEntry);
}
return cachedEntry.Value;
}

Related

'The given key was not present in the dictionary' - but the key exists

I am currently developing a MS Dynamics CRM 2013 - Plugin.
When I try to assign a string-value to a key of a field of an entity it gives me the 'keynotfound'-exception.
This leaves me clueless, because I can verify the key is existing. The key I give is also written correctly, and the data types are compatible, too.
Here's some extra info:
I tried resolving the issue with a server reboot. Nothing.
Remote Debugging is not an option.
I swapped "retrieved.EntityCollection.Entities[i][forField]" with retrieved.EntityCollection.Entities[i]["new_name"] and everything was working fine (kind of pointing out the obvious here, but "new_name" is not the key I try to access).
The execution stops # "if (retrieved.EntityCollection.Entities[i][forField].ToString() != "" && !overwriteExisting)"
Have you got an idea to help me out?
public void GenerateNumberForEntityCollection(string target)
{
try
{
// variables for number generation
bool overwriteExisting = (bool)preImageEntity["new_overwriteexisting"];
int suffixstart = (int)preImageEntity["new_suffixstart"];
string forField= preImageEntity["new_forfield"].ToString();
string prefix = preImageEntity["new_prefix"].ToString();
string postfix = preImageEntity["new_postfix"].ToString();
string separator = preImageEntity["new_separator"].ToString();
// Build query to get all the entries
RetrieveMultipleResponse retrieved;
int PageNumber = 1;
string PagingCookie = string.Empty;
int PageSize = 5000;
string[] Columns = { forField };
QueryExpression query = new QueryExpression()
{
EntityName = target,
ColumnSet = new ColumnSet(Columns),
PageInfo = new PagingInfo()
{
PageNumber = 1,
Count = PageSize
}
};
do
{
if (PageNumber != 1)
{
query.PageInfo.PageNumber = PageNumber;
query.PageInfo.PagingCookie = PagingCookie;
}
RetrieveMultipleRequest retrieve = new RetrieveMultipleRequest();
retrieve.Query = query;
retrieved = (RetrieveMultipleResponse)service.Execute(retrieve);
// Now that all entities are retrieved, iterate through them to gen. the numbers
int i = 0;
foreach (Entity entity in retrieved.EntityCollection.Entities)
{
if (retrieved.EntityCollection.Entities[i][forField].ToString() != "" && !overwriteExisting)
{
//continue;
}
else
{
retrieved.EntityCollection.Entities[i][forField] = prefix + separator + suffixstart.ToString() + separator + postfix;
}
suffixstart++;
service.Update(retrieved.EntityCollection.Entities[i]);
i++;
}
if (retrieved.EntityCollection.MoreRecords)
{
PageNumber++;
PagingCookie = retrieved.EntityCollection.PagingCookie;
}
} while (retrieved.EntityCollection.MoreRecords);
}
catch (Exception e)
{
tracing.Trace("GenerateNumberForEntityCollection: Failed: {0}", e.ToString());
}
}
How did you verify that the key exists?
If the data in a field is null, the Entity instance will not contain that key, even if you specify it in the query's ColumnSet.
This will return you a boolean, indicating if the key exists in the Entity. You can do this control before attempting to read the attribute.
var attributeExists = retrieved.EntityCollection.Entities[i].Contains(forField)
The control below you've done will result in the exception you're getting if the field is null. Just make sure that the attribute exists before.
retrieved.EntityCollection.Entities[i][forField].ToString() != ""
Additionally, you'll get a null reference exception if no records were returned from the query. Make you do a null check on retrieved.EntityCollection.Entities.
When you are querying data in Dynamics CRM it is important to know that record fields having null values in the database are not included in the Attributes collection of the Entity instances being returned.
Getting a value from an Entity's Attribute with this construct:
var value = retrieved.EntityCollection.Entities[i][forField].ToString();
succeeds when attribute forField already has a value in the database, but fails when its current value is null.
Therefore the preferred method to get the attribute values from an entity is GetAttributeValue<T>, like this:
var value = retrieved.EntityCollection.Entities[i].getAttributeValue<string>(forField);
This method returns the value when the attribute exists in the attribute collection, otherwise it returns null.
If any of the fields among
(new_forfield,new_prefix,new_postfix,new_separator) has null value,
that column does not present in the retrieved object and you are trying to get the value of null column preImageEntity["new_forfield"] which will throw keynotfound'-exception ,
so change the code
string forField= preImageEntity["new_forfield"].ToString();
string prefix = preImageEntity["new_prefix"].ToString();
string postfix = preImageEntity["new_postfix"].ToString();
string separator = preImageEntity["new_separator"].ToString();
to
string forField = preImageEntity.Attributes.Contains("new_forfield")? preImageEntity["new_forfield"].ToString():"";
string prefix = preImageEntity.Attributes.Contains("new_forfield") ? preImageEntity["new_prefix"].ToString() : "";
string postfix = preImageEntity.Attributes.Contains("new_forfield") ? preImageEntity["new_postfix"].ToString() : "";
string separator = preImageEntity.Attributes.Contains("new_forfield") ? preImageEntity["new_separator"].ToString() : "";
this will check for field, if it exists than will parse the value to
string else will assign empty string.

How to check if value is multiple or not in key value c#

Here is the method:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var parameters = filterContext.ActionParameters.ToArray();
if (parameters.Count() > 0)
{
foreach (KeyValuePair<string, object> data in parameters)
{
singleparameters = singleparameters + data.Key + ":" + " " + data.Value + ",";
}
singleparameters = singleparameters.Remove(singleparameters.LastIndexOf(','));
I want to check if data value contain single or multiple value.
If the data is single data then key:req and value: pagerecords:200.
If the data contains multiple values there then check if values contain array or single value. Fot example: key:req and values: pagerecords:200, pageinfo:"adsa", count:0, filter: records:200,cotnain:"asa"
I want to check condition an access that..
I am newbie in this area so please give me any suggestion
for more clarification
foreach (KeyValuePair<string, object> data in parameters)
{
if (data.Key == "request" && data.value is jqGridRequest)
{
var fields = g.GetType().GetProperties();
bool hasSingleValue = fields.Count(x => x.GetValue(g, null) != null) == 1;
}
}
as From screenshot its clear that the data.value type is jqGridRequest, so we can directly make use of the object to check or get the desired value, Actually you dont need to do looping also , Directly cast the object and make use of property using if else.
jqGridRequest request = data.value as jqGridRequest;
if (request != null)
{
int pageindex = request.PageIndex;
//// so on other properties, get it and use it directly without looping.
}
This can help you check the value is single or multiple:
var valueFields = data.Value.GetType().GetFields();
var fieldNumber = valueFields.Length;
//If fieldNumber = 1 it is single, otherwise it is multiple
Then you can check if it contains any array
foreach(var fld in valueFields) {
bool isArray = fld.FieldType.IsArray; //true if the field is an array
}

Error for Null value when opening a registry key in C#

I'm having an issue for C# (I'm new to it), when trying to fix a Null value.
Therefore I have a variable "verif" (String verif = String.Empty;), which I used it to read some key from Windows Registry. My code works if the key exists, but when it doesn't I got the error"NullReferanceException was unhandled".
I tried several ways, to catch the exception, to put an "If" statement but I failed miserable.
My code is something like this:
RegistryKey key_user;
RegistryKey key_pwd;
String code = String.Empty;
String tara = String.Empty;
String tot = String.Empty;
String pwd_mdw = String.Empty;
String user_mdw = String.Empty;
String user_uca = String.Empty;
String pwd_uca = String.Empty;
String verif = String.Empty;
private void button1_Click(object sender, EventArgs e)
{tot = listBox1.SelectedValue.ToString();
//MessageBox.Show(tot);
tara = tot.Substring(tot.Length - 2, 2);
//MessageBox.Show(tara);
code = listBox1.SelectedValue.ToString().Substring(0, 2);
user_mdw = textBox1.Text;
//MessageBox.Show(user_mdw);
pwd_mdw = textBox2.Text;
//MessageBox.Show(pwd_mdw);
if (code == "CC")
{
verif = Registry.CurrentUser.OpenSubKey(#"Software\TDCredentials").GetValue("user_mdw_" + tara + "_CC").ToString();
MessageBox.Show("Verif",verif);
MessageBox.Show(user_mdw, "user_mdw");
if (verif==null)
{
key_user = Registry.CurrentUser.CreateSubKey("Software\\TDCredentials");
key_user.SetValue("user_mdw_" + tara + "_CC", user_mdw);
key_user.Close();
key_pwd = Registry.CurrentUser.CreateSubKey("Software\\TDCredentials");
key_pwd.SetValue("pass_mdw_" + tara + "_CC", pwd_mdw);
key_pwd.Close();
MessageBox.Show("User and Password inserted successfully!");
textBox1.Clear();
textBox2.Clear();
}
else
{...
Any hints?
Many thanks in advance, Bogdan.
Looking at what you're trying to do, this line is most likely (one of) your problems;
verif = Registry.CurrentUser.OpenSubKey(#"Software\TDCredentials")
.GetValue("user_mdw_" + tara + "_CC").ToString();
If the key does not exist, OpenSubKey will return null, and you call GetValue() on it without checking.
You can change the line to add a check, something like;
var key = Registry.CurrentUser.OpenSubKey(#"Software\TDCredentials");
var value = key != null ? key.GetValue("user_mdw_" + tara + "_CC") : null;
verif = value != null ? value.ToString() : null;
if(verif == null) {
...
First of all you need to check
Registry.CurrentUser.OpenSubKey(#"Software\TDCredentials")
that this is not null. Then call the getvalue method. Beause if the above key is null then the following getvalue will throw exception.
Try the following check to test if Registry.CurrentUser.OpenSubKey(#"Software\TDCredentials") is not null else it will bomb:
if (code == "CC")
{
if (Registry.CurrentUser.OpenSubKey(#"Software\TDCredentials") != null)
{
verif =
Registry.CurrentUser.OpenSubKey(#"Software\TDCredentials").GetValue("user_mdw_" + "Test" + "_CC").
ToString();
}
Try checking for NULL on OpenSubKey() & GetValue() methods prior to using ToString() method.
You're trying to do too much in one line, without checking the results as you go.
First of all, as others have already said, you need to check that OpenSubKey doesn't return null. You also need to make sure that the key is closed when you're finished, with a using statement:
using (var key = Registry.CurrentUser.OpenSubKey(#"Software\TDCredentials"))
{
if (key == null)
{
// Couldn't open the key, now what?
// You need to make a decision here.
// If you read the documentation for CreateSubKey,
// you'll see that it can *also* return null, so don't rely on it.
}
else
{
// OK, opened the key, and the using statement will close it.
// Now we can try reading values. See the next part of the answer.
}
}
If you successfully open the key, you can try to read the value. Even if you successfully open the key, the value might not exist, or it might not be a string (it could be a DWORD, for instance, or a binary value).
If the value doesn't exist, GetValue returns null, so calling ToString without checking will throw NullReferenceException.
Even if the value exists, calling ToString on it is the wrong thing to do, because it might not be a string value in the registry. If it's a binary value, for example, calling ToString on it will give you the string System.Byte[]. You need to check that it is actually a string.
else
{
// OK, opened the key, and the using statement will close it.
// Now we can try reading values.
string verif = key.GetValue("user_mdw_" + tara + "_CC") as string;
if (verif == null)
{
// The value does not exist, or is not the type you expected it to be (string).
// Now what? You need to make a decision here.
}
else
{
// OK, do something with verif.
}
}
Make sure to read the documentation for these methods, and handle the special cases they mention, especially the circumstances under which they return null:
CreateSubKey
OpenSubKey
GetValue

How to get a max value of a field from mongo cursor in C#

The complete method should be generic like
public string strGetMaxValue(string strDBName, string strCollectionName, string strKey)
{
// in this method if pass some prms it should give max value
}
The one i tried is
string strMaxValue = "";
MongoServer objServer = this.ConnectToServer();
if ((strDBName != null || strDBName != "") && (strCollectionName != null || strCollectionName != ""))
{
string[] strArrays = new string[1];
strArrays[0] = strKey;
//MongoCursor<BsonDocument> objCursor = objServer.GetDatabase(strDBName).GetCollection(strCollectionName).Find(query).SetSortOrder(SortBy.Descending(strArrays)).SetLimit(1);
var objCursor = objServer.GetDatabase(strDBName).GetCollection(strCollectionName).FindAll().SetSortOrder(SortBy.Descending(strArrays)).SetLimit(1).ToArray();
}
In that objCursor i m getting that document which i need.
i want to extract that field value and needs to send it as return parameter.
The method should be generic as such the key value may a field in nested document also.
how to achieve this.?
The method you are looking for is SetFields(params string[] fields) - it can be called on a cursor. It will limit your result set to just the fields you pass in (array) as well as the id. You can then index the field using the []
var result = server
.GetDatabase(strDBName)
.GetCollection(strCollectionName)
.FindAll()
.SetSortOrder(SortBy.Descending(new [] {strKey}))
.SetFields(new [] {strKey}) // The way to wrap something in an array for reference
.SetLimit(1)
.FirstOrDefault(); // Will return null if there are no rows
// There is a chance that we have no results
if (result != null)
// You might want to make sure this is a string / add the datatype
// as a Generic T to your function
return result[strKey].AsString;
else
return null;

Object Reference error in LINQ

I had applied the following code :
var country = from cnty in this.GetAll<CompanyDefinition>().ToList()
where cnty.IsImported = true
select new {
CompanyDefinitionID = cnty.CompanyDefinitionID
, CompanyDefinitionName = cnty.Company.CompanyName + "(" + cnty.Country.CountryCode + "," + cnty.NaicsCode.NaicsCode1 + ")"
};
And I am getting Object Reference error. Its pointing to "select new". Whats the correct way?
The problem is Company, Country or NaicsCode is null, you would need to check this before attempting to access their properties. For example, you could re-write your query as:
var country = from cnty in this.GetAll<CompanyDefinition>()
where cnty.IsImported && cnty.Company != null && cnty.Country != null && cnty.NaicsCode != null
select new {
...
}
If you are employing lazy loading, then using ToList() method is not proper. After calling ToList(), IQueryable<> object is materialized to IEnumerable<>; thus database is not queried for Company or Country references. Try removing ToList() function.

Categories