How to write an update statement using parameters? - sqlanywhere

I'm having trouble writing the update statement gathered from a few different classes. So let me explain what I have..
I have a Minutes.cs class which will get and set the attributes of the database
public class Minute{
public Minute()
{
//
// TODO: Add constructor logic here
//
}
public int ID
{
get;
set;
}
public string Location
{
get; set;
}
public string PeriodDate
{
get;
set;
}
public string PeriodTime
{
get;
set;
}
public string Facilitator
{
get;
set;
}
public string Attenders
{
get;
set;
}
public string Agenda
{
get; set;
}
public string Title
{
get; set;
}
public DateTime DateTime
{
get;
set;
}}
This is my MinuteDB.cs class which interacts with the database layer, and i want to pass the parameters (SAParameters) from this class to the aspx.cs page so I can do a parameterized update(the only reason is because I cant update a datetime format, so i have to use parameterised queries :S), and I don't know if I'm doing this right.
public class MinuteDB{
//string strConnString = ConfigurationManager.ConnectionStrings["emaDB2"].ConnectionString;
public static String DB_STR = ConfigurationManager.ConnectionStrings["emaDB2"].ConnectionString;
//public static String DB_STR = "UserID=dba;Password=sql;DatabaseName=emaDB;ServerName=emaDB";
public MinuteDB()
{
}
public static void DoUpdateQuery(String sql)
{
//Create a connection, replace the data source name with the name of the SQL Anywhere Demo Database that you installed
SAConnection myConnection = new SAConnection(DB_STR);
//open the connection
myConnection.Open();
//Create a command object.
SACommand myCommand = myConnection.CreateCommand();
//Specify a query.
myCommand.CommandText = sql;
SAParameter parameters = new SAParameter();
parameters.SADbType = SADbType.NVarChar;
myCommand.Parameters.Add(parameters);
parameters = new SAParameter();
parameters.SADbType = SADbType.NVarChar;
myCommand.Parameters.Add(parameters);
parameters = new SAParameter();
parameters.SADbType = SADbType.NVarChar;
myCommand.Parameters.Add(parameters);
parameters = new SAParameter();
parameters.SADbType = SADbType.NVarChar;
myCommand.Parameters.Add(parameters);
parameters = new SAParameter();
parameters.SADbType = SADbType.NVarChar;
myCommand.Parameters.Add(parameters);
parameters = new SAParameter();
parameters.SADbType = SADbType.NVarChar;
myCommand.Parameters.Add(parameters);
parameters = new SAParameter();
parameters.SADbType = SADbType.Integer;
myCommand.Parameters.Add(parameters);
parameters = new SAParameter();
parameters.SADbType = SADbType.DateTime;
myCommand.Parameters.Add(parameters);
Minute recentMinutes = new Minute();
myCommand.Parameters[0].Value = recentMinutes.Title;
myCommand.Parameters[1].Value = recentMinutes.Location;
myCommand.Parameters[2].Value = recentMinutes.PeriodDate;
myCommand.Parameters[3].Value = recentMinutes.PeriodTime;
myCommand.Parameters[4].Value = recentMinutes.Attenders;
myCommand.Parameters[5].Value = recentMinutes.Agenda;
myCommand.Parameters[6].Value = recentMinutes.ID;
myCommand.Parameters[7].Value = recentMinutes.DateTime;
try
{
myCommand.ExecuteNonQuery();
}
catch (Exception excp)
{
throw excp;
}
finally
{
myConnection.Close();
}
}
public static void UpdateMinutesByMinutesID(int minuteID, string location, string title, string perioddate, string periodtime, string attenders, string agenda,DateTime datetime)
{
string sql = "UPDATE meetingMinutes SET title=?,location=?,perioddate=?,periodtime=?,attenders=?,agenda=?,datetime=? WHERE minuteID = " + minuteID;
DoUpdateQuery(sql);
}
}
This is my aspx.cs class, where on click of a button, it will update to database
protected void btnUpdate_Click(object sender, EventArgs e)
{
int minID = Convert.ToInt32(Session["minID"]);
string dateAndTime = tbDatepicker.Text + " " + tbTimepicker.Text;
CultureInfo provider = CultureInfo.InvariantCulture;
DateTime theDateTime = DateTime.ParseExact(dateAndTime, "d MMMM yyyy hh:mm tt", provider);
Minute min = new Minute();
min.DateTime = theDateTime;
try
{
MinuteDB.UpdateMinutesByMinutesID(minID,tbLocation.Text,tbTitle.Text,tbDatepicker.Text,tbTimepicker.Text,tbAttenders.Text,tbAgenda.Text,theDateTime);
}
finally
{
btnUpdate.Attributes.Add("onclick", "displaySuccessfulUpdate();");
Response.Redirect("HomePage.aspx");
}
}
I don't know why I cannot get this to work. It prompts me an error, saying Column(minID) could not be found. Is it because I'm not passing the actual parameters to the method?? Can someone help me out?
PS: I'm using SQL Anywhere 12

I would suspect the column minID was not found, as reported. (There is a column called ID, but not minID according to the class definition above; a mismatch or just a harmless DAL mapping?)
I would not expect it to have anything to do with the parameters; the error is talking about the query shape and not the bound values (or lack thereof).
Consult the database (directly) to inspect/verify the table schema. Perhaps some code/sproc was not updated ..

Related

Building GroupBy Expression Tree - IEnumerable parameter not defined error

I want to build an expression for IQueryable GroupBy. While at the moment I'm just simplifying the problem to try and get it working, the eventual final implementation will involve the creation of quite complex expression trees so I want to build a complete expression that can then be integrated into other expressions.
I specifically want to build an expression of this overload:
public static System.Linq.IQueryable<TResult> GroupBy<TSource,TKey,TResult> (
this System.Linq.IQueryable<TSource> source,
System.Linq.Expressions.Expression<Func<TSource,TKey>> keySelector,
System.Linq.Expressions.Expression<Func<TKey,System.Collections.Generic.IEnumerable<TSource>,TResult>> resultSelector);
... my problem is in the implementation of the resultSelector and and the IEnumerable<TSource>.
I have a table of Customers (just dummy data for the purposes of working out this problem). This is stored in an SQL DB and I specifically want to use IQueryable to access the data.
public class Customer
{
public int Id { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public int Age { get; set; }
}
I also have a GroupResult class used to hold the results of the GroupBy (I have different constructors which I've been using in my testing to work out where my problem is occurring)
internal class GroupResult
{
public string? Name { get; set; }
public int NumRecords { get; set; }
public decimal AverageAge { get; set; }
public int TotalAge { get; set; }
public GroupResult() { }
public GroupResult(string name)
{
Name = name;
}
public GroupResult(IEnumerable<Customer> customers)
{
Name = Guid.NewGuid().ToString();
NumRecords = customers.Count();
}
public GroupResult(string name, IEnumerable<Customer> customers)
{
Name = name;
NumRecords = customers.Count();
}
}
The main static class that displays prompts to select column to group on, creates the relevant expression tree and executes it
internal static class SimpleGroupByCustomer
{
internal static DataContext db;
internal static void Execute()
{
using (db = new DataContext())
{
//get input
Console.WriteLine();
Console.WriteLine("Simple Customer GroupBy");
Console.WriteLine("=======================");
Console.WriteLine("Simple GroupBy on the Customer Table");
Console.WriteLine();
Console.WriteLine("Select the property that you want to group by.");
Console.WriteLine();
var dbSet = db.Set<Customer>();
var query = dbSet.AsQueryable();
//for this example we're just prompting for a column in the customer table
//GetColumnName is a helper function that lists the available columns and allows
//one to be selected
string colName = Wrapper.GetColumnName("Customer");
MethodInfo? method = typeof(SimpleGroupByCustomer).GetMethod("GetGroupBy",
BindingFlags.Static | BindingFlags.NonPublic);
if (method != null)
{
method = method.MakeGenericMethod(new Type[] { typeof(String), query.ElementType });
method.Invoke(null, new object[] { query, colName });
}
}
}
internal static void GetGroupBy<T, TTable>(IQueryable query, string colName)
{
Type TTmp = typeof(TTable);
var param = Expression.Parameter(TTmp, "c");
var prop = Expression.PropertyOrField(param, colName);
LambdaExpression keySelector = Expression.Lambda<Func<TTable, T>>(prop, param);
var param1 = Expression.Parameter(typeof(T), "Key");
var param2 = Expression.Parameter(typeof(IEnumerable<TTable>), "Customers");
var ci = typeof(GroupResult).GetConstructor(new[] { typeof(T), typeof(IEnumerable<TTable>) });
//var ci = typeof(GroupResult).GetConstructor(new[] { typeof(T) });
//var ci = typeof(GroupResult).GetConstructor(new[] { typeof(IEnumerable<TTable>) });
if (ci == null)
return;
var pExp = new ParameterExpression[] { param1, param2 };
var methodExpression = Expression.Lambda<Func<T, IEnumerable<TTable>, GroupResult>>(
Expression.New(ci, new Expression[] { param1, param2 }), //<--- ERROR HERE
pExp
);
Type[] typeArgs = new Type[] { typeof(TTable), typeof(T), typeof(GroupResult) };
Expression[] methodParams = new Expression[] { query.Expression, keySelector, methodExpression };
var resultExpression = Expression.Call(typeof(Queryable), "GroupBy", typeArgs, methodParams);
IQueryable dbQuery = query.Provider.CreateQuery(resultExpression);
if (dbQuery is IQueryable<GroupResult> results)
{
foreach (var result in results)
{
Console.WriteLine("{0,-15}\t{1}", result.Name, result.NumRecords.ToString());
}
}
}
}
When I run this and try and iterate through the results I get the following exception:
System.InvalidOperationException: 'variable 'Customers' of type 'System.Collections.Generic.IEnumerable`1[ExpressionTrees3.Data.Customer]' referenced from scope '', but it is not defined'
which is being caused by the param2 ParameterExpression marked above.
If I use the GroupResult constructor that just takes the key value
var ci = typeof(GroupResult).GetConstructor(new[] { typeof(T) });
and omit the param2 from the Lambda body definition the code works as expected and I get a collection of GroupResult records containing the distinct key values in the Name field (but obviously no summary value).
I've tried everything I can think of and just can't get past this error - it's as though the GroupBy is not actually producing the IEnumerable grouping of Customers for each key.
I suspect I'm missing something really obvious here, but just can't see it. Any help would really very much appreciated.
Please note that I am after answers to this specific issue, I'm not looking for alternative ways of doing a GroupBy (unless there's a fundamental reason why this shouldn't work) - this will be rolled into a much larger solution for building queries and I want to use the same process throughout.
Thanks Svyatoslav - as I thought, it was me being especially dumb!
Your comments, as well as a discussion with a friend who has a lot SQL knowledge pointed me in the right direction.
I had been thinking that the GroupBy expression was going to return an Enumerable for each key value and was trying to pass that into a function ... it always felt wrong, but I just ignored that and kept going.
It's obvious now that I need to tell the GroupBy what to calculate and return (i.e. your comment about aggregation).
So for this easy example, the solution is very simple:
var pExp = new ParameterExpression[] { param1, param2 };
var countTypes = new Type[] { typeof(TTable) };
var countParams = new Expression[] { param2 };
var countExp = Expression.Call(typeof(Enumerable), "Count", countTypes, countParams);
var methodExpression = Expression.Lambda<Func<T, IEnumerable<TTable>, GroupResult>>(
Expression.New(ci, new Expression[] { param1, countExp }),
pExp
);
Just by adding the 'Count' expression into the GroupBy method call it works!
.. and adding a new ctor for GroupResult:
public GroupResult(string name, int count)
{
Name = name;
NumRecords = count;
}
(yep, I feel a bit stupid!)

Is it possible to write custom attributes or annotation in a vala class, like C# or Java?

as the question implies how can one reduce boilerplate code or add custom functionality like for example in Java
#Setter(AccessLevel.PROTECTED) private String name;
or Jackson-annotation #JsonProperty
how could one implement or emulate this functionality?
thanks
No, you can't create custom attributes right now.
For properties you can use next syntax:
public string name { get; protected set; default = "42"; }
GObject type system also support Json Serialization/Deserialization for properties.
Here small example(valac file --pkg json-glib-1.0):
public enum MyEnum {
FOO, BAR, FOOBAR
}
public class MyObject : Object {
public string str { get; set; }
public MyEnum en { get; set; }
public int num { get; set; }
public MyObject (string str, MyEnum en, int num) {
this.str = str;
this.num = num;
this.en = en;
}
}
public static int main (string[] args) {
MyObject obj = new MyObject ("my string", MyEnum.FOOBAR, 10);
Json.Node root = Json.gobject_serialize (obj);
// To string: (see gobject_to_data)
Json.Generator generator = new Json.Generator ();
generator.set_root (root);
string data = generator.to_data (null);
// Output:
// ``{"str":"my string","en":2,"num":10}``
print (data);
print ("\n");
return 0;
}
You can find more on ValaDoc.

Populate dropdownlist with a class

I want to create a "package program" for address part of my projects. I need it in almost every project so i wanted to make it easier.
So i decided to create a class to load city names to a dropdownlist. Here i coded:
public class Address
{
string connStr = "Data Source...";
public int id { get; set; }
public string name { get; set; }
public Address(int ID, string Name)
{
this.id = ID;
this.name = Name;
}
public List<Address> LoadCities()
{
List<Address> cities = new List<Address>();
SqlConnection con = new SqlConnection(connStr);
SqlCommand cmd = new SqlCommand("select x,y from ...", con);
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Address city = new Address(rdr.GetInt32(0), rdr.GetString(1));
cities.Add(city);
}
con.Close();
return cities;
}
}
This is my package program. I added this as a reference to my project. And tried to populate my dropdownlist as follows:
List<Address> cities = ???
ddlCity.DataTextField = "x";
ddlCity.DataValueField = "y";
ddlCity.DataSource = cities;
ddlCity.DataBind();
At ??? position i just wanted to code like this: List cities = Address.Loadcities();
So, i have a mistake here and i couldnt get it. Because i am new at "class" works.
Thanks in advance.
public static List<Address> LoadCities()
You need the function on that class to be static.
Then you just do:
List<Address> cities = Address.LoadCities();
Here's some documentation for those times when you can't sleep:
http://msdn.microsoft.com/en-us/library/98f28cdx.aspx

WCF Data Service based on EF5 Model; how to add a custom type

I am trying to build a WCF Data Service with a ServiceMethod returning a custom type.
This type is used as a container to transmit multiple data collection at once. I am not able to define this type as entity or complex type.
public class BrfPackageDataContainer {
public ICollection<BrfFlight> Flights {
get;
set;
}
public ICollection<BrfFlight_Info> Flight_Infos {
get;
set;
}
public ICollection<BrfInfo> Infos {
get;
set;
}
public BrfPackageDataContainer() {
this.Flights = new List<BrfFlight>();
this.Flight_Infos = new List<BrfFlight_Info>();
this.Infos = new List<BrfInfo>();
}
}
This is my ServiceMethod declaration:
[WebGet]
[SingleResult]
public FlightInfoEntities.BrfPackageDataContainer GetBrfPackage () {
var brfPackageDataContainer = new FlightInfoEntities.BrfPackageDataContainer();
brfPackageDataContainer.Demodata();
return brfPackageDataContainer;
}
I got this running when using an empty dummy DataService as data source for the service class definition. But when I use my Entity Framework Model as data source the service refuse to start because of the missing metadata for the custom type.
My question is:
How can I use an EF Model as data source AND still use my custom type as a return value for my method.
Problem solved with a workaround:
I added 3 complex types to my modell, matching the data structure of each individual result set.
Furthermore I added a container class outside the data context which uses the complex types to hold the data in one object.
I extended the context class with a custom method to handle the stored procedure call and mapping the results to the appropriate complex types.ObjectContext.Translate helps a lot...
The WCF Data Service class is instantiated with a dummy DataContext. This enables metadata creation for my custom data container class which now can be used as return type of a custom WCF Data Service Method.
The data context is instantiated when the method is called.
Data container class` public class BrfPackageDataContainer {
public Guid TransactionId {
get;
set;
}
public List<BrfFlight> Flights {
get;
set;
}
public List<BrfFlight_Info> Flight_Infos {
get;
set;
}
public List<BrfInfo> Infos {
get;
set;
}
public BrfPackageDataContainer () {
this.Flights = new List<BrfFlight>();
this.Flight_Infos = new List<BrfFlight_Info>();
this.Infos = new List<BrfInfo>();
}
}`
context extension:
public partial class FlightInfoEntities
{
public virtual BrfPackageDataContainer GetBrfPackage(int? crewId, string operatorCode, string departure, int? flightId, DateTime? stdRangeStart,
DateTime? stdRangeEnd, string requestingApplication, string requestingComputerName,
string requestingACReg, ref Guid transactionId, int? specificInfoTypeId, byte? levelOfDetail,
bool? skipLog) {
using (DbCommand command = this.Database.Connection.CreateCommand()) {
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[dbo].[GetBrfPackage]";
...
var dataContainer = new BrfPackageDataContainer();
try {
this.Database.Connection.Open();
using (DbDataReader reader = command.ExecuteReader()) {
dataContainer.Flights = ((IObjectContextAdapter)this).ObjectContext.Translate<BrfFlight>(reader).ToList();
reader.NextResult();
dataContainer.Flight_Infos = ((IObjectContextAdapter)this).ObjectContext.Translate<BrfFlight_Info>(reader).ToList();
reader.NextResult();
dataContainer.Infos = ((IObjectContextAdapter)this).ObjectContext.Translate<BrfInfo>(reader).ToList();
}
return dataContainer;
} catch (Exception ex) {
throw ex;
}
}
WCF Data Service Method:
[WebGet]
[SingleResult]
public BrfPackageDataContainer GetBrfPackage () {
using (var brfCtx = new FlightInfoEntities()) {
Guid transactionId = new Guid();
var brfPackageDataContainer = brfCtx.GetBrfPackage(null,"4T",null,null,null,null,"test",Environment.MachineName,null,ref transactionId,null,3,false);
return brfPackageDataContainer;
}
}

Why is this code unable to persist data?

public class Customer : BaseClass<Customer>
{
public string Name { get; set; }
public DateTime? DateOfBirth { get; set; }
public string TelephoneNumber { get; set; }
public string InsuranceProvider { get; set; }
public int? PolicyNumber { get; set; }
public byte [] Photo { get; set; }
}
This message shows up:
Or, this:
How to modify this code to be able to persist data?
CustomerDAO.cs
class CustomerDAO
{
.....
public int Save(ITransactionManager tm, Customer item)
{
int count = -1;
try
{
ISqlQueryExecutor<Customer> queryExecutor = new SqlQueryExecutor<Customer>(tm);
count =
queryExecutor.
ExecuteNonQuery(#"INSERT INTO Customer(
ID
,Name
,TelephoneNumber
,DateOfBirth,
InsuranceProvider,
PolicyNumber)
VALUES(
#ID
,#Name
,#TelephoneNumber
,#DateOfBirth,
#InsuranceProvider,
#PolicyNumber)",
item.ID,
item.Name,
item.TelephoneNumber,
item.DateOfBirth,
item.InsuranceProvider,
item.PolicyNumber,item.Photo
);
//new DbParameter(item.ID, DbType.Int32),
//new DbParameter(item.Name, DbType.String),
//new DbParameter(item.TelephoneNumber, DbType.String),
//new DbParameter(item.DateOfBirth, DbType.DateTime),
//new DbParameter(item.InsuranceProvider, DbType.String),
//new DbParameter(item.PolicyNumber, DbType.Int32)
//new DbParameter(item.Photo, DbType.Binary)
//);
string str = string.Empty;
}
catch (Exception ex)
{
throw ex;
}
return count;
}
.... ....
}
CustomerBLL.cs
class CustomerBLL
{
... ... ...
public int Save(Customer item)
{
int newId = 0;
ITransactionManager tm = ApplicationContext.Get(DBNameConst.ActiveConnStringName);
try
{
tm.BeginTransaction();
item.ID = newId = PivotTable.GetNextID(tm, "Customer").Value;
customerDao.Save(tm, item);
PivotTable.UpdateNextIdField(tm, "Customer", newId);
tm.CommitTransaction();
}
catch (Exception ex)
{
tm.RollbackTransaction();
throw ex;
}
return newId;
}
... ... ...
}
ASqlQueryExecutor.cs
public abstract class ASqlQueryExecutor<T> : ISqlQueryExecutor<T>
{
public virtual int ExecuteNonQuery(string queryString, params object[] parameters)
{
int count = -1;
try
{
Command = ParameterAttacher.AttachSaveParameters(TransactionManager, queryString, parameters);
Command.CommandText = queryString;
count = Command.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
return count;
}
ParameterAttacher.cs
class ParameterAttacher
{
public static IDbCommand AttachSaveParameters(ITransactionManager tm, string queryString, params object [] argumentsList)
{
IDbCommand command = new DbObjectInstantiator(tm.ProviderName).CreateCommand();
command.Connection = tm.Connection;
command.Transaction = tm.Transaction;
IList<string> parameterNamesList = new List<string>(ParameterParser.Parse(queryString));
if (parameterNamesList.Count > 0 && argumentsList.Length == argumentsList.Length)
{
int i = 0;
foreach (string paramName in parameterNamesList)
{
Attach(command, paramName, argumentsList[i]);
++i;
}
}
return command;
}
public static void Attach(IDbCommand command, string paramName, object dbParam)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = paramName;
param.Value = (dbParam==null) ? ((object)DBNull.Value) : dbParam;
//param.DbType = dbParam.DbType;
command.Parameters.Add(param);
}
}
string or binary data would be truncated. the statement has been terminated
This could occur because you're trying to store too much data into a column. For example, if you have an nvarchar(5) column in a database and you try to store "this is a string" in there, you might get that error because 5 characters can't hold all of "this is a string".
You can avoid this problem by limiting the fields in your UI to the same length as those in the database. Or, you can perform a check in the validation methods.
Implicit conversion from data type nvarchar to binary is not allowed.
This seems fairly obvious: you're trying to store a character value in a binary column. You haven't provided your database schema, so I can't tell for sure where this could be; but, if you have a column or sproc parameter in the database set as binary but your C# details it as DbType.String you might get this error.
UPDATE:
You never set your DbType in ParameterAttacher.Attach. This means the Parameter will default to DbType.AnsiString for the parameter type. If you pass it a byte[] it may convert that to an ansi string, but when the parameter is given to ADO, it will see DbType.AnsiString and compare that to varbinary(50) (or money, or datetime, or int, etc.) and throw an exception detailing that it doesn't know how to convert to binary (e.g. an implicit conversion).
Also, do yourself a favour and get rid of:
catch(Exception ex)
{
throw ex;
}
That will just force you to loose the real location of the exception and cause you to waste time trying to figure out where the real problem is.
When you must catch (e.g. when you want to rollback the transaction, just throw, don't throw ex. Just throw won't lose the stack information and you can track down the location of the exception. For example:
catch (Exception ex)
{
tm.RollbackTransaction();
throw;
}