I am trying to dynamically query a Teradata database using Dapper but am having some issues. Here is the code:
// model variable is the parameter passed in with search information
using (IDbConnection con = new TdConnection(connection.GetConnectionString()))
{
var builder = new SqlBuilder();
var selector = builder.AddTemplate($"SELECT * FROM Temp_Table /**where**/");
if (model.Id != 0)
{
builder.Where("Id = ?", new { model.Id });
}
if (!string.IsNullOrEmpty(model.Employee_Id))
{
builder.Where("Employee_Id = ?", new { model.Employee_Id });
}
var data= con.Query<TableModel>(selector.RawSql, model).ToList();
return data;
}
The error I am getting is:
[Teradata Database] [3939] There is a mismatch between the number of
parameters specified and the number of parameters required.
I have used very similar code to query DB2 which worked just fine; what do I need to do differently with Teradata?
Managed to figure it out. Changed the line for getting the data to:
var data= con.Query<TableModel>(selector.RawSql, selector.Parameters).ToList();
Not sure why passing in the model worked just fine in my DB2 version but not this Teradata version.
At first glance it appears to be falling through and not adding any "where" condition. Try to structure it in such a way that if it falls through then add 1=1 or a Teradata equivalent if that doesn't work.
I'm unfamiliar with the SqlBuilder() class; but if you have a way of seeing if there aren't any Where constraints added, then to add a generic one. Or, a dirtier way would be to keep a bool reference and check at the end.
Update
Try passing in the parameters:
var data= con.Query<TableModel>(selector.RawSql, selector.Parameters).ToList();
Related
I am working with SQL Geography types and since EFCore doesn't support type geography, I need to execute a stored proc to update my entity.
This works well, but when I call the row that has just been updated, Its returning the old value.
I can see the correct value in the DB.
Here is some of my code:
var FlId = new SqlParameter("#flId", SqlDbType.Int) { Value = testModel.Flid ?? SqlInt32.Null };
var OID = new SqlParameter("#oID", SqlDbType.Int) { Value = testModel.OID };
var flName = new SqlParameter("#flName", SqlDbType.VarChar) { Value = testModel.FLName };
var result = _dbContext.Database.ExecuteSqlCommand("testSchema.spUpsertOFL #flId, #oID, #flName,
parameters: new[] { FlId , OID , flName });
The above code works and performs the upsert, but when I then run a regular query using EFCore, I get back a stale record.
var TestQuery = _dbContext.Fl.FirstOrDefault(x => x.FLCode == testModel.Flcode && x.OID == testModel.OID);
I tried reloading the context with:
_dbContext.Entry(fl).Reload();
But I got an error saying :
"There was an internal error creating the Fl. The instance of entity type 'Fl' cannot be tracked because another instance with the same key value for {'FlID'} is already being tracked."
If I use postman and hit my get endpoint - the correct result returns.
Try detaching the entry and then requerying
dbContext.Entry(entry).State = EntityState.Detached;
entry = context.Users.FirstOrDefault(e => e.Id == id);
While researching this problem I saw many people say the best course of action is to dispose and create a new dbcontext https://github.com/dotnet/efcore/issues/16861
The next most common solution I saw was to call dbContext.Entry(entry).Reload(); This did not work for me because I was changing the type of an entry via a discriminator. Detaching the old entry means when you request it again it pulls from the database instead of the local cache
In an MVC 5 web app using Entity Framework, I learned how to populate an Index view by using db.Database.SqlQuery<model> to execute a stored procedure and show the results in the Index View.
This is the relevant code in my Index View (and it works).
// supply parameter values required by the stored procedure
object[] parameters = {
new SqlParameter("#campus",SqlDbType.NVarChar,3) {Value=vm.SelectedCampus},
new SqlParameter("#date1",SqlDbType.DateTime) {Value=Convert.ToDateTime(vm.SelectedStartDate)},
new SqlParameter("#date2",SqlDbType.DateTime) {Value=Convert.ToDateTime(vm.SelectedEndDate)}
};
// populate the list by calling the stored procedure and supplying parameters
IEnumerable<PerfOdomoeterDate> query =
db.Database.SqlQuery<PerfOdomoeterDate>("PerfOdomoeterDate #campus, #date1, #date2",
parameters).OrderBy(m => m.StudentName).ToList();
And to put that code into better context, here is the entire Index ActionResult.
private PerformanceContext db = new PerformanceContext();
private static readonly string d1 = DateTime.Now.ToShortDateString();
private static readonly string d2 = DateTime.Now.ToShortDateString();
[HttpGet]
public ActionResult Index(int? page, string SelectedCampus = "CRA", string SelectedStartDate=null, string SelectedEndDate=null)
{
int PageNumber = (page ?? 1);
PerfOdomoeterDateViewModel vm = new PerfOdomoeterDateViewModel();
vm.SelectedCampus = SelectedCampus;
vm.SelectedStartDate = string.IsNullOrEmpty(SelectedStartDate) ? d1 : SelectedStartDate;
vm.SelectedEndDate = string.IsNullOrEmpty(SelectedEndDate) ? d2 :SelectedEndDate;
vm.CampusList = StaticClasses.ListBank.CampusList();
// supply parameter values required by the stored procedure
object[] parameters = {
new SqlParameter("#campus",SqlDbType.NVarChar,3) {Value=vm.SelectedCampus},
new SqlParameter("#date1",SqlDbType.DateTime) {Value=Convert.ToDateTime(vm.SelectedStartDate)},
new SqlParameter("#date2",SqlDbType.DateTime) {Value=Convert.ToDateTime(vm.SelectedEndDate)}
};
// populate the list by calling the stored procedure and supplying parameters
IEnumerable<PerfOdomoeterDate> query =
db.Database.SqlQuery<PerfOdomoeterDate>("PerfOdomoeterDate #campus, #date1, #date2",
parameters).OrderBy(m => m.StudentName).ToList();
vm.CreditTable = query.ToPagedList(PageNumber, 25);
return View(vm);
}
As I stated, this code works perfectly in the Index View. However, in a separate ActionResult, where the user has an option to export the data set to an Excel file, I use the same code, and I get this runtime error:
The SqlParameter is already contained by another SqlParameterCollection.
I was under the impression that each ActionResult is in its own scope, so how is it that I am getting this error when I am calling up a new query from a separate ActionResult?
Intellisense did not give me any clues as to how I could explicitly empty the parameters after executing the stored procedure.
This is the code in the ActionResult that is producing the error.
public ActionResult ExportToExcel(string SelectedCampus, string SelectedStartDate, string SelectedEndDate)
{
object[] parameters2 = {
new SqlParameter("#campus",SqlDbType.NVarChar,3) {Value=SelectedCampus},
new SqlParameter("#date1",SqlDbType.DateTime) {Value=Convert.ToDateTime(SelectedStartDate)},
new SqlParameter("#date2",SqlDbType.DateTime) {Value=Convert.ToDateTime(SelectedEndDate)}
};
IEnumerable<PerfOdomoeterDate> query =
db.Database.SqlQuery<PerfOdomoeterDate>("PerfOdomoeterDate #campus, #date1, #date2",
parameters2).OrderBy(m => m.StudentName).AsEnumerable();
...
The ADO.Net objects (like SqlParameter, SqlCommand etc.) presented to us by the .Net framework are a mere layer on top of the real stuff under the hood that is managed by the .Net connection pool. If we create a new SqlConnection —which is implicitly done by db.Database.SqlQuery— we don't really establish a new connection to the database. That would be far too expensive. In reality, our connection object "plugs" in to an available connection in the connection pool.
Normally, this mechanism is pretty transparent, but it is unveiled in issues like the one you see here. I remember having experienced similar issues (exceptions that persisted longer than met the eye).
The message is: you can't beat it, so join it. The quick solution seems to be renaming the parameters in one of the methods. A better solution, of course, is to factor out the repetitive part of your code into a method that contains the identical parts.
I would say, This is how as per the design.
You need to extract the data right from there .ToArray() or .ToList() etc...
Do not try to re execute the query for further data operations.
How can I call a scalar function in entity framework 6 ?
I have tried the following code
using (MhEntities DContext = new MhEntities())
{
var Account_IdParameter = Account_Id.HasValue ? new ObjectParameter("Account_Id", Account_Id) : new ObjectParameter("Account_Id", typeof(long));
string res = ((IObjectContextAdapter)DContext).ObjectContext.CreateQuery<string>("MoneyforHealthEntities.Fn_LEVEL0_Acount_Id", Account_IdParameter).FirstOrDefault();
return Convert.ToInt64(res);
}
No need to use ObjectContext to do this. Also, I don't think you can simply pass in the name of the function, you need to give it complete, valid SQL.
So I would try something like this instead:
using (MhEntities DContext = new MhEntities())
{
string res = DContext.Database.SqlQuery<string>("SELECT MoneyforHealthEntities.Fn_LEVEL0_Acount_Id(#p0)", Account_Id).FirstOrDefault();
return Convert.ToInt64(res);
}
Since you didn't give any details about which database you are using, or the exact function definition, it's possible that the above may need further tweaking. But it should at least give you the basic idea.
DateTime ServerTime = new ContextDbEntities().Database.SqlQuery<DateTime>("Select getdate();").FirstOrDefault();
MessageBox.Show(ServerTime.ToString());
All the answers right but if someone uses a stored procedure, it needs to be edit on function import by:
1. Right-click on your function then click on edit.
2. On the edit function Import window.
3. Select Scaler on the returns a collection section.
4. Finally, click ok and save your model.
In my example, I call an Insert stored procedure that returns a string value.
using (DbModel db = new DbModel())
{
string result = db.storedprocedureName(value1,value2).FirstOrDefault();
return result;
}
I am creating small application in which i have used LINQ To SQL to perform all operation to database.
Now here i am giving the small part of my database structure please take a look.
So update language detail i am getting the object of login using the datacontext something like this.
XVDataContext Context = new XVDataContext ();
var myQuery = from objLogIn in Context.GetTable<LogIn>() where objLogIn.Emp_Id == nEmpId select objLogIn;
In nEmpId i will always have some value.
So it is not creating any problem in fact i am getting the required record from DB and storing it in objUser object using the following code.
LogIn objUser = myQuery.First<LogIn>();
Now to update LanguageDetail i am executing following code but it throws Exception when i execute SubmitChanges line.
Here is the code that i am executing to update.
LanguageDetail obj = new LanguageDetail();
foreach (string sLanguages in TextBoxLanguagesKnown.Text.Split('\n'))
{
obj.Emp_Id = objUser.Emp_Id;
obj.Language = sLanguages.Trim();
}
objUser.LanguageDetails[0] = obj;
Context.SubmitChanges();
I already read following links.
cannot add an entity with a key that is already in use
LINQ To SQL exception with Attach(): Cannot add an entity with a key that is alredy in use
Cannot add an entity with a key that is already in use (LINQ)
By reading the above links i found that i am doing some mistake in ID fields but still i am unable to resolve.
Please tell me the clear understanding of raising this issue and how can i resolve this.
EDIT:
I simply want to update LanguageDetail table.
When i try to add new object using following code it still throws exception.
objUser.LanguageDetail.Add(obj);
You might want to add / remove languages for specific user by using following code.
var languages = TextBoxLanguagesKnown.Text.Split('\n');
// Removes deleted languages (first find all language details that are missing from the UI).
var deletedLanguages = objUser.LanguageDetails.Where(ld => !languages
.Any(l => ld.Language == l.Trim())).ToArray();
foreach(var deletedLanguage in deletedLanguages)
{
objUser.LanguageDetails.Remove(deletedLanguage);
Context.LanguageDetails.DeleteOnSubmit(deletedLanguage);
}
// Adds new languages (then adds new language details that are not found in the database).
var newLanguages = languages.Where(l => !objUser.LanguageDetails
.Any(ld => ld.Language == l.Trim())).ToArray();
foreach (string newLanguage in newLanguages)
{
var languageDetail = new LanguageDetail
{
Emp_Id = objUser.Emp_Id,
Language = newLanguage.Trim()
};
objUser.LanguageDetails.Add(languageDetail);
}
Context.SubmitChanges();
From my understanding you want to update the LanguageDetail entity in your database. In order to do so you have to do one of the following:
Retrieve the original LanguageDetail object based on its id, and update that object instead of creating a new one and assigning it the id of an existing object.
Attach the newly created object to your context instead of just giving a reference to it to your LanguageDetails collection.
The exception you are seeing happens because the way linq to sql behaves is that it threats the obj as a new object that you want to insert and because of that it tries to insert it into the language details table.
Modifying your code like that should work:
Context.LanguageDetails.Attach(obj);
objUser.Employee_LanguageDetails[0] = obj;
I want to write a query with a dynamic list of parameters (depending on parameter is set or not).
I want to execute the query on an oracle database using dapper.
Sample code:
var sqlParams = new List<object>();
var sqlBuilder = new StringBuilder();
sqlBuilder.Append("SELECT * FROM EXAMPLE WHERE 1 = 1 ");
if (!string.IsNullOrEmpty(aParam))
{
sqlBuilder.Append(" AND A LIKE ?");
}
if (!string.IsNullOrEmpty(bParam))
{
sqlBuilder.Append(" AND B LIKE ? ");
}
var sql = sqlBuilder.ToString();
return this.Connection.Query<Equipment>(
sql,
new { aParam, bParam } // ??
).ToList();
Dapper only really works with named parameters. I seem to recall that in oracle they are colon-prefixed, so:
if (!string.IsNullOrEmpty(aParam))
{
sqlBuilder.Append(" AND A LIKE :aParam");
}
if (!string.IsNullOrEmpty(bParam))
{
sqlBuilder.Append(" AND B LIKE :bParam");
}
Now your existing code here:
return this.Connection.Query<Equipment>(
sql,
new { aParam, bParam }
).ToList();
should work. Dapper uses the names of the members of the anonymous type as parameter names. It additionally includes some very basic code to check whether any given member does not exist in the sql, so if your sql only mentions :bParam, it won't actually add the aParam value as a parameter.
In more complicated scenarios, there is also a DynamicParameters object you can use, that functions more like a dictionary - but you don't need that here.