Must declare the scalar variable - Dapper - c#

I do multiple mapping on the dapper. Then I try to implement dapper builder
But the it return exception:
Must declare the scalar variable \"#ExecutionId\".\r\nInvalid usage of the option NEXT in the FETCH statement.
Without multiple mapping, never give a problem
Here my snippet code
var Builder = new SqlBuilder();
var SelectedQuery = Builder.AddTemplate(# "SELECT e.[Id], e.[BuyOrderBookId], e.[SellOrderBookId], e.[Volume], e.[Price], e.[CreationDate], e.[StatusId], bo.[UserId], bo.[MarketId], so.[UserId] FROM[dbo].[Execution] AS e JOIN[dbo].[OrderBook] AS bo ON e.BuyOrderBookId = bo.Id JOIN[dbo].[OrderBook] as so ON e.SellOrderBookId = so.Id
/**where**/
ORDER BY e.[CreationDate] DESC OFFSET #skip ROWS FETCH NEXT #take ROWS ONLY;
");
//Execution ID
if (filter.ExecutionId.HasValue)
Builder.Where("e.[Id] = #ExecutionId", new {ExecutionId = filter.ExecutionId.Value
});
var query = await connection.QueryAsync < ExecutionViewModel, OrderBookViewModel, OrderBookViewModel, ExecutionViewModel > (SelectedQuery.RawSql, (execute, buyOrder, sellOrder) => {
execute.BuyUserId = buyOrder.UserId;
execute.SellUserId = sellOrder.UserId;
execute.MarketId = buyOrder.MarketId;
return execute;
},
splitOn: "UserId,UserId",
param: new {
SelectedQuery.Parameters,
skip = (pagingParam.PageNumber - 1) * pagingParam.PageSize,
take = pagingParam.PageSize
});
Anyone know did I do something wrong here?
Update
I just fix like this
if (filter.ExecutionId.HasValue)
Builder.Where(String.Format("e.[Id] = {0}",filter.ExecutionId));
I believe this is not good way to implement. Is risk sql injection.

Try changing the where clause like this:
if (filter.ExecutionId.HasValue)
Builder.Where("e.[Id]", new {Id = filter.ExecutionId.Value});

You can add parameters like this.
if (filter.ExecutionId.HasValue)
{
Builder.Where("e.[Id] = #ExecutionId");
((DynamicParameters)SelectedQuery.Parameters)
.AddDynamicParams(new {
ExecutionId = filter.ExecutionId.Value
});
}

Related

Calling stored procedure from EF returns error The SqlParameter is already contained by another SqlParameterCollection

I'm calling a SQL stored procedure in EF
var paramSortColumn = new SqlParameter("#SortColumn", SqlDbType.NVarChar, 200) { Value = SortColumn };
var paramSortOrder = new SqlParameter("#SortOrder", SqlDbType.NVarChar, 10) { Value = SortOrder };
var paramStartIndex = new SqlParameter("#StartIndex", SqlDbType.Int) { Value = filter.StartIndex - 1 };
var paramItemsPerPage = new SqlParameter("#ItemsPerPage", SqlDbType.Int) { Value = filter.ItemsPerPage };
var paramDealerBranchID = new SqlParameter("#DealerBranchIDs", SqlDbType.NVarChar){Value = selectedIds };
var paramBypassDealerBranchIDs = new SqlParameter("#BypassDealerBranchIDs", SqlDbType.TinyInt) { Value = Convert.ToInt32(filter.ByPassDealerBranchIDs) };
var result =
_dbContext.Database.SqlQuery<DealerMappingDetailsReportModel>(
"usp_DealerMappingDetail #DealerBranchIDs,#BypassDealerBranchIDs,#SortColumn,#SortOrder,#StartIndex,#ItemsPerPage",
paramDealerBranchID, paramBypassDealerBranchIDs, paramSortColumn, paramSortOrder, paramStartIndex, paramItemsPerPage
);
return result.ToList();
but it returns
The SqlParameter is already contained by another
SqlParameterCollection
I had the same issue with a Database.SqlQuery that was calling a Stored Procedure. Many folks recommend "clearing" and/or "cloning" the Parameters collection (among other things).
However...
I noticed my SQL command-text wasn't "complete"...and found...I had forgotten to include the EXEC command in my SQL. It looks like you may have the same issue.
I CHANGED THIS:
In my methods, I usually like to hand-back Queryables, so my methods often look like below.
...notice that 'EXEC' is missing
var query = UnitOfWork.DbContext.Database.SqlQuery<DocumentStatusDataItem>("dbo.usp_ListDocumentStatusForATFDocuments #ContextFullName, #WorkflowNames",
new SqlParameter("ContextFullName", context.FullName),
new SqlParameter("WorkflowNames", namesXML))
.AsQueryable();
TO THIS:
...notice that 'EXEC' is now there
var query = UnitOfWork.DbContext.Database.SqlQuery<DocumentStatusDataItem>("EXEC dbo.usp_ListDocumentStatusForATFDocuments #ContextFullName, #WorkflowNames",
new SqlParameter("ContextFullName", context.FullName),
new SqlParameter("WorkflowNames", namesXML))
.AsQueryable();
Then my calls started working:
For example...calls like this suddenly started working...and I stopped getting the exception.
List<DocumentStatusDataItem> collection = query.ToList();
However, this same solution also worked for ToList() methods I have that were having the same issue.

assigning value from select linq query not updating the list

query is like this:-
labRequestItem.LabTest.Select(p=>p.LabTestICDCodes= labRequests.ICDCodes.Select(t => new ICDCodeList()
{
ICDCodeId = t.ICDCodeId,
ICDCode = t.ICDCodeName,
ICDDescription = t.ICDDescription,
ICDCodeType = t.ICDCodeType
}).ToArray());
Now the issue is that LabTestICDCodes is not updating with the new list value returning from select statement
I have tested in the Linqpad, following shall work, but please note:
finalResult would be List of type of LabTestICDCodes (which is again IEnumerable itself) with new / modified values, as that's the only column projected. You have to project the whole object you want as a result, values will modify as expected
var finalResult =
labRequestItem.LabTest.Select(p=>p.LabTestICDCodes= labRequests.ICDCodes.Select(t => new ICDCodeList()
{
ICDCodeId = t.ICDCodeId,
ICDCode = t.ICDCodeName,
ICDDescription = t.ICDDescription,
ICDCodeType = t.ICDCodeType
}).ToArray()).ToList();
I found only way to solve this problem by doing following step:-
1.Get the value in local variable
var icdList = labRequests.ICDCodes.Select(t => new ICDCodeList()
{
ICDCodeId = t.ICDCodeId,
ICDCode = t.ICDCodeName,
ICDDescription = t.ICDDescription,
ICDCodeType = t.ICDCodeType
}).ToArray();
Now assign the value by usinf foreach loop.
foreach (var test in labRequestItem.LabTest)
{
test.LabTestICDCodes = icdList;
}
In Last step I have to use foreach loop, if any other solution then
please reply otherwise I will use the same as it mentioned above.

NEST - IndexMany doesn't index my objects

I've used NEST for elasticsearch for a while now and up until now I've used the regular ElasticSearchClient.Index(...) function, but now I want to index many items in a bulk operation.
I found the IndexMany(...) function, but I must do something wrong because nothing is added to the elastic search database as it does with the regular Index(...) function?
Does anyone have any idea?
Thanks in advance!
I found the problem. I had to specifiy the index name in the call to IndexMany
var res = ElasticClient.CreateIndex("pages", i => i.Mappings(m => m.Map<ESPageViewModel>(mm => mm.AutoMap())));
var page = new ESPageViewModel
{
Id = dbPage.Id,
PageId = dbPage.PageId,
Name = dbPage.Name,
Options = pageTags,
CustomerCategoryId = saveTagOptions.CustomerCategoryId,
Link = dbPage.Link,
Price = dbPage.Price
};
var pages = new List<ESPageViewModel>() { page };
var res2 = ElasticClient.IndexManyAsync<ESPageViewModel>(pages, "pages");
This works as expected. Guess I could specify a default index name in the configuration to avoid specifying the index for the IndexMany call.
If you are using C# you should create a list of objects that you want to insert then call the IndexMany function.
Example :
List<Business> businessList = new List<Business>();
#region Fill the business list
...............................
#endregion
if (businessList.Count == 1000) // the size of the bulk.
{
EsClient.IndexMany<Business>(businessList, IndexName);
businessList.Clear();
}
And in the end check again
if (businessList.Count > 0)
{
EsClient.IndexMany<Business>(businessList, IndexName);
}

How to work around NotMapped properties in queries?

I have method that looks like this:
private static IEnumerable<OrganizationViewModel> GetOrganizations()
{
var db = new GroveDbContext();
var results = db.Organizations.Select(org => new OrganizationViewModel
{
Id = org.OrgID,
Name = org.OrgName,
SiteCount = org.Sites.Count(),
DbSecureFileCount = 0,
DbFileCount = 0
});
return results;
}
This is returns results pretty promptly.
However, you'll notice the OrganizationViewModel has to properties which are getting set with "0". There are properties in the Organization model which I added via a partial class and decorated with [NotMapped]: UnsecureFileCount and SecureFileCount.
If I change those 0s to something useful...
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount
... I get the "Only initializers, entity members, and entity navigation properties are supported" exception. I find this a little confusing because I don't feel I'm asking the database about them, I'm only setting properties of the view model.
However, since EF isn't listening to my argument I tried a different approach:
private static IEnumerable<OrganizationViewModel> GetOrganizations()
{
var db = new GroveDbContext();
var results = new List<OrganizationViewModel>();
foreach (var org in db.Organizations)
{
results.Add(new OrganizationViewModel
{
Id = org.OrgID,
Name = org.OrgName,
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount,
SiteCount = org.Sites.Count()
});
}
return results;
}
Technically this gives me the correct results without an exception but it takes forever. (By "forever" I mean more than 60 seconds whereas the first version delivers results in under a second.)
Is there a way to optimize the second approach? Or is there a way to get the first approach to work?
Another option would be to load the values back as an anonymous type and the loop through those to load your viewmodel (n+1 is most likely the reason for the slowness).
For example:
var results = db.Organizations.Select(org => new
{
Id = org.OrgID,
Name = org.OrgName,
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount,
SiteCount = org.Sites.Count()
}).ToList();
var viewmodels = results.Select( x=> new OrganizationViewModel
{
Id = x.Id,
Name = x.Name,
DbSecureFileCount = x.DbSecureFileCount,
DbFileCount = x.DbFileCount,
SiteCount = x.SiteCount
});
Sorry about the formatting; I'm typing on a phone.
You are basically lazy loading each object at each iteration of the loop, causing n+1 queries.
What you should do is bring in the entire collection into memory, and use it from there.
Sample code:
var organizationList = db.Organizations.Load();
foreach (var org in organizationList.Local)
{
//Here you are free to do whatever you want
}

Linq To Sql surprisingly fast retreving data. Is it normal that it is 10x faster than ADO?

I'm currently learning Linq to Sql and Im very surprised by the performance of selecting data. I'm retreving joined data from few tables. I select about 40k of rows. Mapping this data to objects using ADO times about 35s, using NHbiernate times about 130s and what is suspicious using Linq To Sql only 3,5s. Additionally I would like to write that I'm using immediately loading which looks like:
THESIS th = new THESIS(connectionString);
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<NumericFormula>(x => x.RPN);
dlo.LoadWith<RPN>(x => x.RPNDetails);
dlo.LoadWith<RPNDetail>(x => x.Parameter);
th.LoadOptions = dlo;
th.Log = Console.Out;
Looking to the logs when I'm iterating I can't see that Linq To Sql generate some additional queries to database.
I'm very surprised by huge differences in performance and I wonder that maybe I don't understand something.
Could someone explain me why it works so fast?
To measure time I'm using Stopwatch class.
ADO.NET Code:
public static List<NumericFormulaDO> SelectAllNumericFormulas()
{
var nFormulas = new List<NumericFormulaDO>();
string queryString = #"
SELECT *
FROM NumericFormula nf
Left Join Unit u on u.Unit_Id = nf.Unit_Id
Left Join UnitType ut on ut.UnitType_Id = u.UnitType_Id
Join RPN r on r.RPN_Id = nf.RPN_Id
Join RPNDetails rd on rd.RPN_Id = r.RPN_Id
Join Parameter par on par.Parameter_Id = rd.Parameter_Id where nf.NumericFormula_Id<=10000";
using (var connection = new SqlConnection(connectionString))
{
var command = new SqlCommand(queryString, connection);
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var det = new RPNDetailsDO();
det.RPNDetails_Id = Int32.Parse(reader["RPNDetails_Id"].ToString());
det.RPN_Id = Int32.Parse(reader["RPN_Id"].ToString());
det.Identifier = reader["Identifier"].ToString();
det.Parameter.Architecture = reader["Architecture"].ToString();
det.Parameter.Code = reader["Code"].ToString();
det.Parameter.Description = reader["Description"].ToString();
det.Parameter.Parameter_Id = Int32.Parse(reader["Parameter_Id"].ToString());
det.Parameter.ParameterType = reader["ParameterType"].ToString();
det.Parameter.QualityDeviationLevel = reader["QualityDeviationLevel"].ToString();
if (nFormulas.Count > 0)
{
if (nFormulas.Any(x => x.RPN.RPN_Id == Int32.Parse(reader["RPN_Id"].ToString())))
{
nFormulas.First(x=>x.RPN.RPN_Id == Int32.Parse(reader["RPN_Id"].ToString())).RPN.RPNDetails.Add(det);
}
else
{
NumericFormulaDO nFormula = CreatingNumericFormulaDO(reader, det);
nFormulas.Add(nFormula);
//System.Diagnostics.Trace.WriteLine(nFormulas.Count.ToString());
}
}
else
{
NumericFormulaDO nFormula = CreatingNumericFormulaDO(reader, det);
nFormulas.Add(nFormula);
//System.Diagnostics.Trace.WriteLine(nFormulas.Count.ToString());
}
}
}
}
return nFormulas;
}
private static NumericFormulaDO CreatingNumericFormulaDO(SqlDataReader reader, RPNDetailsDO det)
{
var nFormula = new NumericFormulaDO();
nFormula.CalculateDuringLoad = Boolean.Parse(reader["CalculateDuringLoad"].ToString());
nFormula.NumericFormula_Id = Int32.Parse(reader["NumericFormula_Id"].ToString());
nFormula.RPN.RPN_Id = Int32.Parse(reader["RPN_Id"].ToString());
nFormula.RPN.Formula = reader["Formula"].ToString();
nFormula.Unit.Name = reader["Name"].ToString();
if (reader["Unit_Id"] != DBNull.Value)
{
nFormula.Unit.Unit_Id = Int32.Parse(reader["Unit_Id"].ToString());
nFormula.Unit.UnitType.Type = reader["Type"].ToString();
nFormula.Unit.UnitType.UnitType_Id = Int32.Parse(reader["UnitType_Id"].ToString());
}
nFormula.RPN.RPNDetails.Add(det);
return nFormula;
}
LINQ to SQL Code:
THESIS th = new THESIS(connectionString);
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<NumericFormula>(x => x.RPN);
dlo.LoadWith<RPN>(x => x.RPNDetails);
dlo.LoadWith<RPNDetail>(x => x.Parameter);
th.LoadOptions = dlo;
th.Log = Console.Out;
var nFormulas =
th.NumericFormulas.ToList<NumericFormula>();
NHibernate Code:
IQueryable<NumericFormulaDO> nFormulas =
session.Query<NumericFormulaDO>()
.Where(x=>x.NumericFormula_Id <=10000);
List<NumericFormulaDO> nForList =
new List<NumericFormulaDO>();
nForList = nFormulas.ToList<NumericFormulaDO>();
Related to your comments you can see that in ADO I'm using SqlReader and in LINQ I try to use immediate execution.
Of course it is possible that my mapping "algorithm" in ADO part it's not very good but NHibernate is much more slow than ADO (4x slower) so I wonder if for sure is everything alright in LINQ to SQL part because I think in NHibernate is everything good and after all is much more slow than little confusing ADO part.
Thank you guys for responses.
LINQ-to-SQL consumes ADO.NET and has additional overheads, so no: it shouldn't be faster unless it isn't doing the same work. There was mention of access via ordinals vs names, but frankly that affects micro-seconds, not seconds. It won't explain an order of magnitude change.
The only way to answer this is to trace what LINQ-to-SQL is doing. Fortunately this is simple - you can just do:
dbContext.Log = Console.Out;
which will write the TSQL is executes to the console. There are two options then:
you discover the TSQL isn't doing the same thing (maybe it isn't eager-loading)
you discover the TSQL is valid (=doing the same), but has a better plan - in which case... "borrow" it :p
Once you have the TSQL to compare, test that side-by-side, so you are testing the same work. If you want the convenience without the overheads, I'd look at "dapper" - takes away the boring grunt-work of mapping readers to objects, but very optimised.
Rewritten ADO.NET code based on above remarks, this should be a lot faster. You could still improve by using the ordinal value instead of the column names and by reading the fields in exactly the same order as in the query, but those are micro optimizations.
I've also removed a couple of duplications. You might also want to check how to improve the performance of typecasting and conversions, as the Parse(ToString) route is very inefficient and can cause very strange issues when running with systems running in different languages. There's also a chance of dataloss when doing these conversions when decimal, float or doubles are involved, as not all of their values can be translated to strings correctly (or can't roundtrip back).
public static List<NumericFormulaDO> SelectAllNumericFormulas()
{
var nFormulas = new Dictionary<int, NumericFormulaDO>();
string queryString = #"
SELECT *
FROM NumericFormula nf
Left Join Unit u on u.Unit_Id = nf.Unit_Id
Left Join UnitType ut on ut.UnitType_Id = u.UnitType_Id
Join RPN r on r.RPN_Id = nf.RPN_Id
Join RPNDetails rd on rd.RPN_Id = r.RPN_Id
Join Parameter par on par.Parameter_Id = rd.Parameter_Id where nf.NumericFormula_Id<=10000";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = new SqlCommand(queryString, connection));
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var det = new RPNDetailsDO();
det.RPNDetails_Id = (int) reader.GetValue("RPNDetails_Id");
det.RPN_Id = (int) reader.GetValue("RPN_Id");
det.Identifier = (string) reader.GetValue("Identifier");
det.Parameter.Architecture = (string)reader.GetValue("Architecture");
det.Parameter.Code = (string)reader.GetValue("Code");
det.Parameter.Description = (string)reader.GetValue("Description");
det.Parameter.Parameter_Id = (int) reader.GetValue("Parameter_Id");
det.Parameter.ParameterType = (string)reader.GetValue("ParameterType");
det.Parameter.QualityDeviationLevel = (string)reader.GetValue("QualityDeviationLevel");
NumericFormulaDO parent = null;
if (!nFormulas.TryGetValue((int)reader.GetValue("RPN_Id"), out parent)
{
parent = CreatingNumericFormulaDO(reader, det);
nFormulas.Add(parent.RPN.RPNID, parent);
}
else
{
parent.RPN.RPNDetails.Add(det);
}
}
}
}
return nFormulas.Values.ToList();
}

Categories

Resources