I'm C# newbie, and have code that uses .net CommandLineParser like this:
return CommandLine.Parser.Default
.ParseArguments<
cmd1, cmd2, cmd3
>(args)
.MapResult(
(cmd1 cmd) => cmd.Execute(),
(cmd2 cmd) => cmd.Execute(),
(cmd3 cmd) => cmd.Execute(),
errs => -1);
I don't really like to duplicate every command twice - first as a type in ParseArguments, and in MapResult, cos my every class has .Execute method.
Is there a way to not duplicate it? Idk, something reflection-related?
Related
I'm learning SQL and decided to use a data structure I've never used before, a dictionary, to create some methods to perform basic CRUD operations. The first method that I am working on is a method to delete a person given a table name and a dictionary containing the strings to be used in the SQL statement. Here is the code.
class QueryBuilder
{
public void DeleteMemer(string tableName, Dictionary<string, string> columns)
{
var sqliteCommand = new SQLiteCommand($"delete from '{tableName}' where {columns.Keys} = '{columns.Values}'", connection);
sqliteCommand.ExecuteNonQuery();
}
}
class Program
{
static void Main(string[] args)
{
DBManagerSQLite memes = new DBManagerSQLite("SqliteDataBase.db");
QueryBuilder queryBuilder = new QueryBuilder(memes.connection);
Dictionary<string, string> dict = new Dictionary<string, string>();
//------------------DELETE TEST------------------
dict.Add("Memer", "Ryan");
queryBuilder.DeleteMemer("Memers", dict);
}
}
Edited for clarity. I get a run time error on the sqliteCommand.ExecuteNonQuery() line. I suspect this is because of the dictionary I am using. I previously was using all my CRUD operations with simple strings but decided to change it and use a dictionary. I think I am not understand how to use a dictionary in this way. If I remove the dictionary and just hard code strings, the methods work just fine.
This can lead to SQL injection - I'd suggest you sanitize input using SqlParameter
When injected like this, {columns.Keys} will transfrom to something like System...ICollection, which is definetely not the column name.
It would be good if you clarified the question. Assuming the problem is exception on syntax error:
You can iterate over key value pairs to create a WHERE clause for your query.
Example (without sanitizing!):
var whereClause = string.Join(" OR ", columns.Select(kvp => $"{kvp.Key} = '{kvp.Value}'")); // join with AND to create more restrictive query
var sqliteCommand = new SQLiteCommand($"delete from '{tableName}' where {whereClause}", connection);
UPD:
Apart from question, why do you try to use the db that way? It's fairly easy to implement code-first EF context, and you will get, along with simplicity, compile-time checks on your queries. And only once you will need more speed, you can switch to any micro-ORM and carefully optimize performance-critical queries
I'm using Entity Framework 6 (POCO, code first approach) and I have the following situation...
I have an entity that has a property which is an enumeration (called Status) - the value in the database is an integer and what I want to do is show to the user a localized description of this value.
I, therefore, have code that looks like this (actually, it calls into a ResourceManager and there are more status values but it is indicative):
public static string ToDisplayable(Status status)
{
switch(status)
{
case Status.One:
return "Status ONE";
case Status.Two:
return "Status TWO";
default:
return "Unknown Status";
}
}
...
var results = someIqueryable
.ToList()
.Select(r => new
{
Status = r.Status,
DisplayableStatus = ToDisplayable(r.Status)
});
Now, that works fine, because of course the good ol' toList has meant that all my stuff's been pulled back from the database before the Select is called. If I remove the ToList I get the following exception of course:
An exception of type 'System.NotSupportedException' occurred in
mscorlib.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method
'System.String ToDisplayable(Status)' method, and this method cannot
be translated into a store expression.
OK, fair enough, it is not able to translate my function call to SQL and I don't really blame it.
The thing is I really, really, really don't want that ToList in there because later on some Where clauses may (or may not) be added and, besides, I just don't need all the columns on my entity.
I think, therefore, that my only option is to do this:
var results = someIqueryable
.Select(r => new
{
Status = r.Status,
DisplayableStatus = r.Status == Status.One
? "Status ONE"
: r.Status == Status.Two
? "Status TWO"
: "Unknown Status"
});
That is kind of ugly and tedious (there are about 15 status values in my real code) and, worst of all, I cannot reuse it (but it does work at least).
The beauty of the simple method at the beginning was that, because there are other entities with Status columns, that have the same possible values I could just call the same method. Now if I add a new status value for example I have to go a huntin' through my code for all the places that do this translation. Horrible.
So, does anyone know how I can create a reusable piece of code that can be translated to a SQL expression that I can plug into a Select method?
Can I weave some magic with an Expression perhaps?
Isn't translating the enum value to human readable text something that the display code should take care of? Return the Enum and translate the Enumeration to a displayable value just before binding the data to the View.
In the worst case you can always do:
var results = someIqueryable
.Where(...)
.Select(r => new Status = r.Status, /* other column you're after */)
.ToList()
.Select(r => new
{
Status = r.Status,
/* other column you're after */,
DisplayableStatus = ToDisplayable(r.Status)
});
I have figured out a way to do this. The project LinqKit has exactly what I need and even has a handy Nuget package so I did:
Install-Package LinqKit
I think the code in this library that I am using originally came from Tomas Petricek. I wish it were inside the main Entity Framework project, but anyway...
Now I have that library referenced I can create a helper:
public static class StatusHelper
{
public static readonly Expression<Func<Status, string>> ToDisplayable = s =>
s == Status.One
? "Status ONE"
: s == Status.Two
? "Status TWO"
: "Unknown Status";
}
And then use that helper like so:
// It is important to assign to a local variable here and
// not attempt to use the StatusHelper in the query
// see - http://stackoverflow.com/q/22223944/1039947
// Not ideal, of course, but I can live with it
var toDisplayable = StatusHelper.ToDisplayable;
var results = someIqueryable
.AsExpandable() // <-- This is needed for LinqKit
.Select(x => new
{
Status = x.Status,
DisplayableStatus = toDisplayable.Invoke(x.Status)
})
.ToList();
As you can see, I can Invoke the expression in the Select and exactly the right thing happens in the query against the database.
Like this, I can easily share my little method around. I am, therefore, a happy bunny and, as you can see, I do not need to evaluate the query early either.
Suppose I have the following line of code:
context.Load(itemCollection, item => item.Include(i => i["Title"], i => i["Name"]));
Is there any way I can dynamically specify the parameters to the item.Include() function instead of hard-coding them as above?
I would ideally like to allow users to select properties they want to retrieve of an object such as Title, Name, Description, etc.
FYI, here is the ClientContext.Load function. This function is coming from Microsoft.SharePoint.Client.dll
public void Load<T>(T clientObject, params Expression<Func<T, object>>[] retrievals) where T : ClientObject
{
if ((object) clientObject == null)
throw new ArgumentNullException("clientObject");
ClientAction.CheckActionParameterInContext(this, (object) clientObject);
DataRetrieval.Load<T>(clientObject, retrievals);
}
I don't have the necessary setup to test it, but will something like this work?
String[] keys = ...;
context.Load( itemCollection
, item => item
.Include(keys
.Select(key => { i => i[key] })
.ToArray()
);
Have a look at the Answer for this question.
I had a similar problem with creating dynamic where clauses, and this is how I got around it.
Use the following syntaxt (works in JavaScript, but haven't tested it in C#):
context.load(this.list, 'Include(Title, Name)');
'Include(Title, Name)' is a string and is easier to generate based on user input.
this.list refers to a sharepoint list that is loaded before.
I am having a problem binding my DataGridView to a LINQ result that is based off of a strongly typed datatable. Binding Directly to the DataTable works fine. WORKING Example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// TODO: This line of code loads data into the 'MYDSDataSet.MYTABLE' table. You can move, or remove it, as needed.
this.MYTABLETableAdapter.Fill(this.MYDSDataSet.MYTABLE);
this.dataGridView1.DataSource = this.MYDSDataSet.MYTABLE;
}
}
Using the following line does NOT work:
this.dataGridView1.DataSource = this.MYDSDataSet.MYTABLE.Select(x => x);
By working/not working I mean in the first example my datagridview will autogenerate all the columns of the datatable and show me all results. In the second example I will not get any errors but my datagridview will remain completely empty, as if I assigned it no DataSource at all.
From what I have read on this site and others I should have no problems with what I am doing but I can not get the correct results anyway I try. Any ideas? If you need any more info that could help, please let me know.
Edit1: Additional Details.
I tried the following lines, all without success:
this.dataGridView1.DataSource = this.MYDSDataSet.Tables["MYTABLE"].AsEnumerable().Select(x => x);
this.dataGridView1.DataSource = this.MYDSDataSet.MYTABLE.AsEnumerable().Select(x => x);
this.dataGridView1.DataSource = this.MYDSDataSet.MYTABLE.Select(x => x).ToList();
this.dataGridView1.DataSource = from x in this.MYDSDataSet.MYTABLE select x;
All the results data subsets from above contain the proper data/rowcount when debugging.
My strongly typed datatables are the ones provided by the Visual Studio 2010 Data Source Wizard.
Write it using:
this.MYDSDataSet.MYTABLE.AsEnumerable().Select(x => x)
DataTable RowCollection does not implement IEnumerable, therefore you can not run query against it unless you convert it using AsEnumerable()
EDIT - Can not add comments yet to discussion in other answer.
Using MYTABLE.Select(x => x) does not work for me in VS2010. It returns the following errors:
Cannot convert lambda expression to type 'string' because it is not a delegate type. Select() expects a string expression, not lambda.
I am using Linq to Sql. When My StoredProcedure Executing its result ll returned in form of IMultipleResults.
I am converting it to ISingleResults and It can be Casted as List.
So I need to Convert it to DataTable so That I can Bind it to Dataset and Pass the values to UI.
But I want such method where I can convert it without LOOP.
Please Any body Help me.
For Any Clarification Contact me.
Thanking you.
First you might want to see if you could cast your list to a BindingList, or do something like this:
BindingListX = New BindingList(Of elementX)(QueryResult.ToList())
You might be able to bind with that. If that doesn't work though here's another question: Do you want to not use a loop in your code at all, or just not see a loop in your code? It is possible to create an extension function that does the conversion:
public static class HelperFunctions
{
public static void Map<T>(this IEnumerable<T> source, Action<T> func)
{
foreach (T i in source)
func(i);
}
public static DataTable ToDataTable<T>(this IEnumerable<T> source)
{
var dt = new DataTable();
var properties = typeof(T).GetProperties();
dt.Columns.AddRange(properties.Select(x => new DataColumn(x.Name, x.PropertyType)).ToArray());
source.Select(x => dt.NewRow().ItemArray = properties.Select(y => y.GetValue(x, null)).ToArray()).Map(x => dt.Rows.Add(x));
return dt;
}
}
But as you can see, the other extension function 'Map' is nothing more than a foreach that can be used inline. If that is not what you want then I apologize and will try to come up with a different way.
It is also worth noting that I'm not sure if assigning to Item Array is the best way to populate a row so if it doesn't work then please let me know so that I may draft another version of the function.
Hope this helps!