Casting IEnumerable<dynamic> with custom class - c#

I've been using dapper lately and wanted to make a dynamic method that would make it easier to select things out with sql.
This is my code:
Class:
public class Method
{
public static IEnumerable<dynamic> QueryAll(string table)
{
dynamic dyn = table;
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DbCon"].ToString());
using (con)
{
string sql = String.Format("SELECT * FROM {0}", table);
IEnumerable<dynamic> ie = con.Query<dynamic>(sql, null);
return ie;
}
}
}
Calling function here:
IEnumerable<posts> = Method.QueryAll("posts");
It gives me an error that I cannot convert IEnumerable dynamic to IEnumerable Models.Posts.
How can I cast it to make it work.

Technically you could just use the IEnumerable.Cast-method to cast every instance returned from the method to the actual instance.
IEnumerable<posts> = Method.QueryAll("posts").Cast<posts>();
However if you allready know the the actual type why not use it in the query allready? dynamic should be handled with care and only if your really have to, which I doubt is the case here. You could for example add the actual type as type-parameter to the method:
public static IEnumerable<T> QueryAll<T>(string table)
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DbCon"].ToString());
using (con)
{
string sql = String.Format("SELECT * FROM {0}", table);
IEnumerable<T> ie = con.Query<T>(sql, null);
return ie;
}
}
Now you can call it as follows:
var result = Method.QueryAll<posts>("posts");
As an aside Method is a really bad class-name, as posts also. You should give your class names that describe what they serve for and what they are doing. Moreover there are naming-conventions suggesting to use PascalCase-names for class making your class Posts. At last when you have an enumeration of posts, I suppose the class itself represents one single post, not many, so you actual want a class named Post which you can store into an IEnumerable<Post>.

Related

How to get a list of object from sql connection query?

I have sql query written in c#.(DataType class has str_name and int_something). Code is like the following
List<DataType> allDataType=SQLConnection.Query<DataType>("select str_name,int_something from sqlite_master where type = ?", type_name);
I already got parameterless constructor inside DataType, but still I got a list of object that hasn't been initialized. str_name is empty string while int_something is always 0. But I can get the correct number of rows of data. Any hint?
What actually SQLConnection.Query<T>(string s) is? SQLConnection doesn't have such a method, so I assume it's an extension method that you have defined in your project. The problem is that method. Try searching a bug in the implementation of that method.
This is what you looking for I gather
public List<NumberPattern> GetNumberPattern(int number)
{
using var cnn = new System.Data.SqlClient.SqlConnection(Server.ConnectionString);
var resp = cnn.Query<NumberPattern>($"exec GetNumberPatternRows {number};");
if (resp != null)
{
return resp.ToList();
}
return null;
}

C# ADO.NET IBM DB2 named parameters with same name throws Not enough parameters specified Exception

I have a fairly agnostic ADO.NET application that connects to a number of databases and is able to extract the necessary information to run. I have hit a snag with DB2 and how it handles named parameters, particularly when I reuse a named parameter in the same query. I know of a couple of ways to get around this by simply adding more parameters, but in theory it should work as it does on other databases that I connect to as the parameter name is the same.
What I'm doing is a bit more complicated and involves subqueries etc, but to demonstrate, take the following query:
select value from test.table where cola=#key1 and colb=#key1;
The named parameter #key1 is used twice.
My code is as follows:
try
{
DbProviderFactory dbfFactory = DbProviderFactories.GetFactory("IBM.Data.DB2.iSeries");
using (DbConnection dbConnection = dbfFactory.CreateConnection())
{
dbConnection.ConnectionString = "DataSource=xxx.xxx.xxx.xxx;UserID=xxxxxxxx;password=xxxxxxxxx";
using (DbCommand dbCommand = dbConnection.CreateCommand())
{
IDbDataParameter iddpParameter1 = dbCommand.CreateParameter();
iddpParameter1.ParameterName = "#key1";
iddpParameter1.DbType = DbType.String;
iddpParameter1.Value = "1";
dbCommand.Parameters.Add(iddpParameter1);
dbCommand.CommandType = CommandType.Text;
dbCommand.CommandText = "select value from test.table where cola=#key1 and colb=#key1";
dbConnection.Open();
using (IDataReader idrReader = dbCommand.ExecuteReader())
{
while (idrReader.Read())
{
...
}
}
}
} // end dbConnection
} // end try
catch (Exception ex)
{
Console.Write(ex.Message);
}
When I run this I get an exception that tells me:
System.InvalidOperationException: Not enough parameters specified. The command requires 2 parameter(s), but only 1 parameter(s) exist in the parameter collection.
I get what it is telling me, but I'm looking for help in figuring out how I can have the provider use the named parameter for both parameters as they are the same. It seems that it is doing a blind count of named parameters and not realizing that they are the same named parameters. SQL Server seems to allow me to do this with the same code above. I'm guessing it's just one of those differences in the providers, but hoping someone has run into this and has a solution for DB2 that doesn't get into specific DB2 code.
Thanks, appreciate the assistance.
well I did a little more digging, and I wonder if it might be the connector that you are using. So I'm doing the following (which is very similar to what you are doing)
in my app config file I have
<connectionStrings>
<add name="AWOLNATION" providerName="Ibm.Data.DB2" connectionString="Server=sail:50000;Database=Remix;" />
</connectionStrings>
in my Databasemanager class I would initialize it like so
public static DatabaseManager Instance(string connectionStringName)
{
var connectionStringSettings = ConfigurationManager.ConnectionStrings[connectionStringName];
if (connectionStringSettings == null) throw new MissingMemberException("[app.config]", string.Format("ConnectionStrings[{0}]", connectionStringName));
return new DatabaseManager(connectionStringSettings);
}
private DatabaseManager(ConnectionStringSettings connectionInformation)
{
_connectionInformation = connectionInformation;
_parameters = new Dictionary<string, object>();
}
private void Initialize()
{
_connection = DbProviderFactories.GetFactory(_connectionInformation.ProviderName).CreateConnection();
_connection.ConnectionString = _connectionInformation.ConnectionString;
_command = _connection.CreateCommand();
}
I add parameters a little different though. I have a Dictionary<string,object> that I add too when setting up my query. To use your example I would have had this
public IEnumerable<object> GetSomething(string key)
{
var sql = "select value from test.table where cola = #key1 and colb = #key1";
_manager.AddParameter("#key1", key);
return _manager.ExecuteReader<object>(sql, ToSomethignUseful);
}
private object ToSomethignUseful(DatabaseManager arg)
{
return new { Value = arg.GetArgument<object>("value") };
}
then reading is where the OP and I have similar code
public IEnumerable<T> ExecuteReader<T>(string sql, Func<DatabaseManager, T> conversionBlock)
{
Initialize();
using (_connection)
{
_connection.Open();
_command.CommandText = sql;
_command.CommandType = CommandType.Text;
if (_parameters.Count > 0)
AddParameters(_command, _parameters);
_parameters.Clear();
using (_reader = _command.ExecuteReader())
{
while (_reader.Read())
{
yield return conversionBlock(this);
}
}
}
}
private static void AddParameters(DbCommand command, Dictionary<string, object> parameters)
{
foreach (var param in parameters)
{
command.Parameters.Add(CreateParameter(command, param.Key, param.Value));
}
}
private static DbParameter CreateParameter(DbCommand command, string key, object value)
{
var parameter = command.CreateParameter();
parameter.ParameterName = key;
parameter.Value = value;
return parameter;
}
running said code is working for me, so I wonder if the difference is in the provider that we are using. I'm using named parameters in production and have been for atleast a year now, possibly closer to 2 years.
I will say that I did get the same error when essentially running the same code twice, as shown in this code
public static void IndendedPrintForEach<T>(this IEnumerable<T> array, string header, Func<T, string> consoleStringConverterMethod)
{
var list = array.ToList();
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine($"<<<{header}>>>");
Console.ForegroundColor = color;
if (!list.Any())
{
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine(" ************NoItemsFound************");
Console.ForegroundColor = color;
}
else
{
foreach (var item in list)
Console.WriteLine($" {consoleStringConverterMethod(item)}");
}
}
on line 3 var list = array.ToList() was the fix to the problem that you were seeing for me. before I had if (!array.Any()) which would run the query and use the parameters (which I clear out before I execture the query) then when I go to enumerate through and print each item in the array I was then getting the error. For me the problem was that it was re-running the query which I had no more parameters. The fix was to enumerate the query with the ToList() and then do my checking and printing on the list.
You answered you own question: "Unfortunately, I have not found a solution. I had to create another named parameter and just assign it the same value"
Oracle/DB2/Sybase especially are difficult with SQL queries and parameters.
Parameters in the SQL query should be in the same order they are added to the C# parameters are added to the C# SQL command (Oracle, Sybase)
Put parenthesis around the SQL query where clause parts using parameters (all)
Make sure the SQL data types are matching the C# parameter data types (all)
Check for overflow/underflow of parameter data so that the SQL query does not error
Pass in null/empty string in the appropriate format for the database. Ideally, create C# SQLParameter create methods to create the parameter in the correct format for the database
Oracle is particularly finicky about this. Take the time to build a C# wrapper library to construct a C# query object correctly, construct C# parameters correctly and add the C# SQL parameters to the query.
Put in notes that the query parameter add order should match the order of "#" parameters in the SQL query.
This wrapper library is you documentation for you and the next developer to avoid the problems you've encountered.

Casting the Result of TableOperation.Retrieve

I recently got into C# / Azure and have a small problem that I'd like to work out. The application works as intended but I'd like to refactor a bunch of classes because I'm sure that there is a simpler solution.
I currently have a bunch of functions to retrieve Entities from the Azure that only vary in the Type that gets retrieved, but optimally I'd only want one class like so:
public static Object Do(string RowKey, string partitionKey, string tableName)
{
var theTable = Connect.Initialize(tableName);
var retrieveOperation = TableOperation.Retrieve(
Base64.EncodeTo64(partitionKey),
Base64.EncodeTo64(RowKey));
var retrievedResult = theTable.Execute(retrieveOperation);
if (retrievedResult.Result != null) {
return retrievedResult.Result;
}
throw new ArgumentException(
String.Format("{0} not found in the Table", RowKey));
}
This, in itself, works and retrieves the required entities. However, I cannot cast the returned Object without an Error.
The Object Type that I want to cast it to implements the TableEntity-Type and matches the Result out of the table.
I know that I can cast it in the sense of
TableOperation.Retrieve<Type>(...)
but I'd really like to use a single function for this purpose which requires me to cast it on calling the function.
I suspect that the problem is related to the fact that the Result is of the Type DynamicTableEntity but I am pretty lost as to why.
Is there any way to solve this problem elegantly / is there a way to establish a parameter that holds the Type that I want as a result? (I tried it with "Type ..." but that doesn't work).
You could try something like this:
return (Entity)retrievedResult.Result;
class Entity : TableEntity
{
public string PartKey => PartitionKey;
public string RowKey => RowKey;
}
Try this,
Specify TEntity as TableEntity explicitly then cast it to generalized one
public async Task<TEntity> Get<TEntity>(string tableName, string partitionKey, string rowkey) where TEntity : TableEntity, new()
{
var table = this._tableClient.GetTableReference(tableName);
var result = await table.ExecuteAsync(TableOperation.Retrieve<TEntity>(partitionKey, rowkey));
return (TEntity)result.Result;
}
I had the same problem and the reason my cast was failing was because the Retrieve operation did not get all the columns needed by my type; by default it only got the PartitionKey, RowKey (and maybe ETag etc.)
To make my cast succeed, I specified the extra columns in the Retrieve call:
// Define the additional columns we want, and pass them into Retrieve
var columns = new List<string>(){ "Name", "Status" };
var retrieve = TableOperation.Retrieve<MyEntity>(partitionKey, rowKey, columns);
var returnedObject = await cloudTable.ExecuteAsync(retrieve).Result
// The cast succeeds
var myThing = (MyEntity)returnedObject;
For a more flexible approach, each of your TableEntity subtypes could have a static list of the columns they need public static List<string> Columns = new List<string>() { "Name", "Status" };
Then, to make your Do() generic, I think it would look something like this (not tested, you may need to do some more casting and look up some Generic magic...):
public static T Do<T>(string RowKey, string partitionKey, string tableName)
{
var theTable = Connect.Initialize(tableName);
var retrieveOperation = TableOperation.Retrieve<T>(
Base64.EncodeTo64(partitionKey),
Base64.EncodeTo64(RowKey),
T.Columns);
var retrievedResult = theTable.Execute(retrieveOperation);
if (retrievedResult.Result != null) {
return retrievedResult.Result;
}
throw new ArgumentException(
String.Format("{0} not found in the Table", RowKey));
}
For better integrity, each of your TableEntity classes should also conform to a new Interface, like ITableWithColumns stipulating that the TableEntity can provide a List<string> Columns as described above.
There's a little bit more detail on my blog post, Failure to cast an object from Azure TableOperation.Retrieve.
You can use functions with Generic Types in c#
Refer this link
You can also refer the stackoverflow example here

Writing SQL queries without table access full

TL;DR I'm using EntityFramework 5.0 with Oracle and need to query a table for two columns only using index with NVL of two columns.
Details after hours of attempts... I'll try to organize it as possible.
The desired SQL query should be:
SELECT t.Code, NVL(t.Local, t.Global) Description
FROM Shows t
Where t.Code = 123
So what is the problem? If I want to use Context.Shows.Parts.SqlQuery(query) I must return the whole row(*), but then I get Table Access Full, so I must return only the desired columns.
The next thing(Actually there were a lot of tries before the following...) that I've tried which gives a very close results was using the null-coalescing operator(??) :
Context.Shows.Where(x => x.Code == 123)
.Select(x => new { x.Code, Description = x.Local ?? x.Global);
But the SQL it's using is complicated using case & when and not using my Index on Code, Nvl(Local, Global) which is critical!
My next step was using Database.SqlQuery
context.Database.SqlQuery<Tuple<int, string>>("the Raw-SQLQuery above");
But I get an error that Tuple must not be abstract and must have default ctor(it doesn't).
Final step which I dislike is creating a class which has only those two properites(Code, Description), now... it works great, but I don't want to write a class for each query like that.
Ideas?
This is a no-solution answer.
I think whatever you try, you can't do that. Even if you define your own mutable generic Tuple, it will failed since the name of the property must match the name of the column:
SqlQuery(String, Object[]): Creates a raw SQL query that will
return elements of the given generic type. The type can be any type
that has properties that match the names of the columns returned from
the query, or can be a simple primitive type.
I think the best you can do is creating your own generic method for querying the database via classic Command and ExecuteReader pattern. Untested, but you get the idea:
public static IEnumerable<Tuple<T>> SqlQuery<T>(this DbContext context, string sql)
{
using(var connection = new SqlConnection(context.Database.Connection.ConnectionString))
using (var command = new SqlCommand(sql, connection))
{
var reader = command.ExecuteReader();
while (reader.NextResult())
{
yield return new Tuple<T>((T)reader[0]);
}
}
}
public static IEnumerable<Tuple<T1, T2>> SqlQuery<T1, T2>(this DbContext context, string sql)
{
using (var connection = new SqlConnection(context.Database.Connection.ConnectionString))
using (var command = new SqlCommand(sql, connection))
{
var reader = command.ExecuteReader();
while (reader.NextResult())
{
yield return new Tuple<T1, T2>((T1)reader[0], (T2)reader[1]);
}
}
}

Linq to sql convert IQueryable to Dataset

What is the easiest way to convert an IQueryable object to a dataset?
modelshredder has exactly what you need. If you have the datacontext around and don't need the data in terms of your model also, nitzmahone' solution is fine performance wise (if it matches your setup, which is not clear to me)
(yourDatacontext).GetCommand(yourIQueryableHere), pass command text to a DbCommand object, call ExecuteReader, pass reader to dataset's .Load method.
The easiest thing to do might be to write an implementation of IDataReader that can wrapper an IEnumerable (IQueryable is an IEnumberable). There is an implementation here. Then you can call DataTable.Load(IDataReader).
If you create a DataTable from schema so it matches your LINQ to Sql, I have an extension method that takes the IQueryable and fills the DataTable:
public static DataTable AsDataTable(this IQueryable value, DataTable table)
{
var reader = value.GetEnumerator();
while (reader.MoveNext())
{
var record = (Customer)reader.Current;
table.Rows.Add(record.CustomerID, record.City);
}
return table;
}
Note that the cast to Customer is my TEntity:
[Table(Name = "Customers")]
public class Customer
{
[Column(IsPrimaryKey = true)]
public string CustomerID;
[Column]
public string City;
}
There are other options that use reflection to build your DataTable from the Customer class. Given the performance hit from reflection, I chose to use the following method to build my table:
public DataTable GetSchema(params string[] columns)
{
string col_list;
if (columns.Length == 0)
col_list = "*";
else
col_list = String.Join(", ", columns);
return Provider.QueryAsDataTable(string.Format("select top 0 {0} from customers", col_list));
}
Putting all that together, I was able to push the results into my DataGridView:
dgridRO.DataSource = new DataView(rows.AsDataTable(dmap.GetSchema("CustomerID", "City")));
**
ID10TException:
**
I spent all this effort getting a conversion from the IQueryable to a DataTable just so I could create a DataView as a source to the DataGridView. Well, I know I didn't need to do that now.

Categories

Resources