How to pass non-optional NULL parameters to a Stored Proc using OrmLite - ormlite-servicestack

I'm using OrmLite against an existing SQL Server database that has published stored procedures for access. One of these SPs takes 3 int parameters, but expects that one or another will be null. However, none of the parameters are declared optional.
Here's the code I've tried:
using (IDbConnection scon = myFactory.OpenDbConnection())
{
rowCount = scon.SqlScalar<int>("EXEC myProc #FileID, #FileTypeID, #POID",
new
{
FileID = req.FileId,
FileTypeID = (int?)null,
POID = req.PoId,
});
}
But this produces a SqlException: Must declare the scalar variable "#FileTypeID". Examining the SQLParameterCollection under the covers shows that only two parameters are being generated by OrmLite.
Is it possible to call this SP with a null parameter?

It's not supported with SqlScalar. When you look at the code then you can see that SqlScalar methods from class ServiceStack.OrmLite.OrmLiteReadExtensions execute SetParameters method responsible for adding parameters to query with second parameter(excludeNulls) equal true I don't know why- mythz should answer for this ;).
If you want to fix it then you have change all SqlScalar methods to invoke SetParameters with true and SetParameters method should look like following(must support DBNull.Value not null)
private static void SetParameters(this IDbCommand dbCmd, object anonType, bool excludeNulls)
{
dbCmd.Parameters.Clear();
lastQueryType = null;
if (anonType == null) return;
var pis = anonType.GetType().GetSerializableProperties();
foreach (var pi in pis)
{
var mi = pi.GetGetMethod();
if (mi == null) continue;
var value = mi.Invoke(anonType, new object[0]);
if (excludeNulls && value == null) continue;
var p = dbCmd.CreateParameter();
p.ParameterName = pi.Name;
p.DbType = OrmLiteConfig.DialectProvider.GetColumnDbType(pi.PropertyType);
p.Direction = ParameterDirection.Input;
p.Value = value ?? DBNull.Value; // I HAVE CHANGED THAT LINE ONLY
dbCmd.Parameters.Add(p);
}
}
When you change code then you can set null for parameters in the following way:
var result = db.SqlScalar<int>("EXEC DummyScalar #Times", new { Times = (int?)null });
In my opinion you can describe it as a defect on github and I can make pull request.

Related

Project to a Known Type using Simple.OData.Client Dynamic Syntax

Simple.OData.Client has a typed and dynamic (and basic) syntax.
I like the typed, but I don't want to build out all my types. In the end I really only need two or so types in the results I get.
But my queries need more types to properly filter the results.
So I want to use the dynamic syntax. But I want to cast the results to classes I have.
I can easily do this manually, but I thought I would see if Simple.OData.Client supports this before I go writing up all that conversion code for each query.
Here is some dynamic syntax code that runs without errors:
client.For(x.Client).Top(10).Select(x.ClientId, x.Name).FindEntriesAsync();
Here is an example of what I had hoped would work (selecting into a new Client object)
client.For(x.Client).Top(10).Select(new Client(x.ClientId, x.Name)).FindEntriesAsync();
But that kind of projection is not supported (I get an "has some invalid arguments" error).
Is there a way to support projection into an existing class when using the dynamic syntax of Simple.OData.Client?
EDIT: The code below works. But it's performance is terrible. I decided to abandon it and write hand written mappers for each type I needed.
This is what I came up with:
dynamic results = oDataClient.For(x.Client).Select(x.ClientId, x.Name).FindEntriesAsync().Result;
var listOfClients = SimpleODataToList<Client>(results);
public List<T> SimpleODataToList<T>(dynamic sourceObjects) where T : new()
{
List<T> targetList = new List<T>();
foreach (var sourceObject in sourceObjects)
{
// This is a dictionary with keys (properties) and values. But no
// matter what sourceObject is passed in, the values will always be
// the values of the first entry in the sourceObjects list.
var sourceProperties = ((System.Collections.Generic.IDictionary<string, object>)sourceObject);
var targetProperties = typeof(Client).GetProperties().Where(prop => prop.CanWrite);
var targetObject = new T();
foreach (var targetProperty in targetProperties)
{
if (sourceProperties.ContainsKey(targetProperty.Name))
{
var sourceValue = GetProperty(sourceObject, targetProperty.Name);
targetProperty.SetValue(targetObject, sourceValue, null);
}
}
targetList.Add(targetObject);
}
return targetList;
}
public static object GetProperty(object o, string member)
{
if (o == null) throw new ArgumentNullException("o");
if (member == null) throw new ArgumentNullException("member");
Type scope = o.GetType();
IDynamicMetaObjectProvider provider = o as IDynamicMetaObjectProvider;
if (provider != null)
{
ParameterExpression param = Expression.Parameter(typeof(object));
DynamicMetaObject mobj = provider.GetMetaObject(param);
GetMemberBinder binder = (GetMemberBinder)Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, member, scope, new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(0, null) });
DynamicMetaObject ret = mobj.BindGetMember(binder);
BlockExpression final = Expression.Block(
Expression.Label(CallSiteBinder.UpdateLabel),
ret.Expression
);
LambdaExpression lambda = Expression.Lambda(final, param);
Delegate del = lambda.Compile();
return del.DynamicInvoke(o);
}
else
{
return o.GetType().GetProperty(member, BindingFlags.Public | BindingFlags.Instance).GetValue(o, null);
}
}
It was made much harder because normal casts and such for the dynamic objects returned would only give the first object in the list over and over. The GetProperty method works around this limitation.

parsing dynamo db queryresponse to object

I'm using DynamoDB to query a table with the following commands
QueryRequest request = new QueryRequest
{
TableName = "Events",
ExclusiveStartKey = startKey,
KeyConditions = keyConditions,
IndexName = "Title-index" // Specify the index to query against
};
// Issue request
QueryResponse result = client.Query(request);
The ExclusiveStartKey and Keyconditions are predefined
The issue is that the QueryResult result variable is not parsed to my native object, when I use the DynamoDB.Context you cast the method with the expected type, but in this case I need to parse the QueryResult...
Is there any other way to do this?
Or should I parse the object?
I ended up using something like:
using System.Linq;
...
// Issue request
QueryResponse result = AmazonDynamoDBClient.Query(request);
var items = result.Items.FirstOrDefault();
var doc = Document.FromAttributeMap(items);
var myModel = DynamoDBContext.FromDocument<MyModelType>(doc);
What you want is some sort of ORM - a nice read is Using Amazon DynamoDB Object Persistence Framework. Also check out the API reference that also shows samples
Take a look at the examples from Querying Tables Using the AWS SDK for .NET Low-Level API documentation
In your handling method
var response = client.Query(request);
var result = response.QueryResult;
foreach (Dictionary<string, AttributeValue> item in response.QueryResult.Items)
{
// Process the result.
PrintItem(item);
}
PrintItem implementation
private static void PrintItem(Dictionary<string, AttributeValue> attributeList)
{
foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
{
string attributeName = kvp.Key;
AttributeValue value = kvp.Value;
Console.WriteLine(
attributeName + " " +
(value.S == null ? "" : "S=[" + value.S + "]") +
(value.N == null ? "" : "N=[" + value.N + "]") +
(value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") +
(value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]")
);
}
Console.WriteLine("************************************************");
}
I was stuck on the same issue, and found that by using Table.Query with a QueryOperationConfig I could specify the index that I wanted to use. Because the Document object returned from this query has a "ToJson()" method on it, I was able to convert the results of the query to Json, and then use Json.Net to Deserialize it back into my object which worked flawlessly for my totally flat object.
var queryFilter = new QueryFilter(indexedColumnName, QueryOperator.Equal, targetValue);
Table table = Table.LoadTable(client, Configuration.Instance.DynamoTable);
var search = table.Query(new QueryOperationConfig { IndexName = indexName, Filter = queryFilter });
List<MyObject> objects = new List<MyObject>();
List<Document> documentSet = new List<Document>();
do
{
documentSet = search.GetNextSetAsync().Result;
foreach (var document in documentSet)
{
var record = JsonConvert.DeserializeObject<MyObject>(document.ToJson());
objects .Add(record);
}
} while (!search.IsDone);
Hope it helps you out!

How to pass two parameters in query string?

I want to pass two parametrs cartid and productis via query string.
Cartid is to be generated either from session(if available) else from database and Product is to be fetch from previous query sting
My code is(in case cart id is to be fetch from database)
CartInfo cartinfo = new CartInfo();
cartinfo.UserName = Session["UserName"].ToString();
cartinfo.IsOrder = "0";
cartinfo.CartDate = DateTime.Now;
int id = new InsertAction().InsertData(cartinfo);
if (id!=0)
{
lblmsg.Text = "Inserted Sucessfully";
Session["CartID"] = id;
if (Request.QueryString["ProductID"] != null)
{
int productid = int.Parse(Request.QueryString["ProductID"]);
}
Response.Redirect("ViewCartItems.aspx?CartID=id & ProductID=productid");
}
and in case cartid is to be fetch from the session created
if (Session["CartID"] != null)
{
string cartid;
int productid;
if (Request.QueryString["ProductID"] != null)
{
cartid = Session["CartID"].ToString();
productid = int.Parse(Request.QueryString["ProductID"]);
DataSet ds = new AddCartItem().GetCartItem(cartid, productid);
if (ds.Tables[0].Rows.Count > 0)
{
DataSet ds1 = new AddCartItem().UpdateCartItem(cartid, productid);
}
but both the queries are wrong
the are generating url like this
http://localhost:1030/SShopping%20Website/client/ViewCartItems.aspx?CartID=id%20&%20ProductID=productid
Please help
It's usually much easier to read using String.Format:
Response.Redirect(String.Format("ViewCartItems.aspx?CartID={0}&ProductID={1}", id, productid));
Also, it is prefable to use Response.Redirect(url, false) instead of just Response.Redirect(url), so you don't get a ThreadAbortException.
From MSDN:
When you use this method in a page handler to terminate a request for
one page and start a new request for another page, set endResponse to
false and then call the CompleteRequest method. If you specify true
for the endResponse parameter, this method calls the End method for
the original request, which throws a ThreadAbortException exception
when it completes. This exception has a detrimental effect on Web
application performance, which is why passing false for the
endResponse parameter is recommended.
Reading: Response.Redirect
You need to concatenate the values into the string:
Response.Redirect("ViewCartItems.aspx?CartID=" + id.ToString() + "&ProductID=" + productid.ToString());
You are putting space between '&', 'variable name' , '='.
Don't put space. Write like this: &name=, not like & name =.
Response.Redirect("ViewCartItems.aspx?CartID="+id+"&ProductID="+productid);
This will work.

List in asp.net linq query not working

const string keyword = "manoj";
rsp.DataSource = company.GetCompanySearch(keyword);
rsp.DataBind();
public List<Company> GetCompanySearch(string keyword)
{
using (var context = huntableEntities.GetEntitiesWithNoLock())
{
List<Company> query = context.Companies.ToList();
if (!string.IsNullOrEmpty(keyword))
{
keyword = keyword.ToLower();
query = (List<Company>) query.Where(u=>u.CompanyName.Contains(keyword)
|| u.EmailAdress.Contains(keyword)
||u.MasterCountry.Description.Contains(keyword)
||u.MasterIndustry.Description.Contains(keyword)
||u.CompanyDescription.Contains(keyword)
||u.CompanyHeading.Contains(keyword));
}
return query.ToList();
}
}
The following code throwing the following exception:
{"Unable to cast object of type 'WhereListIterator1[Data.Company]' to type 'System.Collections.Generic.List1[Data.Company]'."}
"(List) query.Where()" is equal to "(List) (query.Where())", so this will throw that exception.
Should use query.Where().ToList() but not a explicit cast.
Further, better not put "List query = context.Companies.ToList();" before your "if" statement. In this case, no matter keyword is empty or not, it will query all records into memory and it will cause performance problem.
Can change to below
IQueryable<Company> query = context.Companies; //Remove ToList()
if (!string.IsNullOrEmpty(keyword))
{
keyword = keyword.ToLower();
// Remove cast
query = query.Where(u=>u.CompanyName.Contains(keyword)
|| u.EmailAdress.Contains(keyword)
||u.MasterCountry.Description.Contains(keyword)
||u.MasterIndustry.Description.Contains(keyword)
||u.CompanyDescription.Contains(keyword)
||u.CompanyHeading.Contains(keyword));
}
return query.ToList();

How can I avoid the null object reference error

I have function init, which runs on the creationComplete of the application. The init calls get_login_share_object function, in which objects are created, which are null.
Now my problem is that, I get a null object reference error on the Alert in "init()". How can I avoid that. Is it possible that I can have a check to see, if the objects are null the program should just skip reading the objects.
private function init():void
{
var stored_credentials:Object = get_login_share_object();
Alert.show(stored_credentials.check_remember +" "+ stored_credentials.alias +" "+ stored_credentials.password );
}
private function get_login_share_object():Object
{
//create or retrieve the current shared object
var so:SharedObject = SharedObject.getLocal("loginData","/");
var dataToLoad:ByteArray = so.data.ws_creds;
if(!dataToLoad)
return null;
//read in our key
var aes_key:String = ServerConfig.aes_key;
var key:ByteArray = new ByteArray();
key = Base64.decodeToByteArray(aes_key);
//read in our encryptedText
var encryptedBytes:ByteArray = new ByteArray();
dataToLoad.readBytes(encryptedBytes);
//decrypt using 256b AES encryption
var aes:ICipher = Crypto.getCipher("simple-aes128-ctr", key, Crypto.getPad("pkcs5"));
aes.decrypt(encryptedBytes);
encryptedBytes.position = 0;
var obj:Object = new Object();
obj.alias = encryptedBytes.readUTF();
obj.password = encryptedBytes.readUTF();
obj.check_remember = encryptedBytes.readUTF();
return obj;
}
You could check for the null like this:
var stored_credentials:Object = get_login_share_object();
if (stored_credentials)
Alert.show(stored_credentials.check_remember +" "+ stored_credentials.alias +" "+ stored_credentials.password );
else
trace('No Shared Object');
You should find out why those values are null and fix that first. Generally speaking, if you are expecting a value, it should not be null.
If it really is expected that some of those values are null then yes, you can check them first in two ways:
if(value != null) value.doSomething();
or
try{
Alert.show(stored_credentials.check_remember +" "+ stored_credentials.alias +" "+ stored_credentials.password );
}
catch(e:Error){
// do something else here if the statement under the try failed.
// most likely log the error message and see what it is
}
Your problem is here:
var dataToLoad:ByteArray = so.data.ws_creds;
if(!dataToLoad)
return null;
If there isn't any data to load, you're returning a null. So when you try and access the returned object's properties later, you'll get the null object reference error because you're referencing a null object. :)
There are a couple of easy solutions to this. You can check if the return value is null before you try to reference any properties like so:
if (stored_credentials != null) {
Alert.show(stored_credentials.check_remember +" "+ stored_credentials.alias +" "+ stored_credentials.password );
}
Or you can stop returning a null from your get_login_share_object function. What you return instead is totally up to you, just make sure it returns an object with all the properties you're referencing.

Resources