Ok so I have ZERO idea how to word it in a title so I will explain what I am looking to achieve here.
I have a .cs file that houses a connection code utilizing dapper listed below.
namespace DBConnector
{
public static class Helper
{
public static string CnnVal(string name)
{
return ConfigurationManager.ConnectionStrings[name].ConnectionString;
}
}
public class DataAccess<T>
{
public List<T> GetInfo(string query, object parameters = null)
{
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(Helper.CnnVal("ProxyRPDB")))
{
return connection.Query<T>(string, object);
}
}
}
}
What I am trying to achieve, is have a query executed from a DIFFERENT .cs file through the above code. BUT! I am trying to make it so that above code accepts query executions from multiple .cs files that all need different data from different tables. I have tried forever to find the information... so this is truly last resort. I am using Dapper for .NET 4.5.2.
First, we need to fix the basic syntax errors in the GetInfo() method:
public static class DataAccess
{
public static IEnumerable<T> GetInfo<T>(string query, object parameters = null)
{
using (var connection = new System.Data.SqlClient.SqlConnection(Helper.CnnVal("ProxyRPDB")))
{
return connection.Query<T>(query, parameters);
}
}
}
Now, assuming you have the proper reference and using directives in other *.cs files, you can do things like this inside those files:
var results = DataAccess.GetInfo<MyTableType>("SELECT * FROM [MyTable]");
foreach(var record in results)
{
//...
}
Dapper will now try to map the query results to instances of the MyTableType class, so make sure you've created such a class.
Note this works with IEnumerable instead of List. If you really need a List (hint: you usually don't, and IEnumerable can perform much better) you can always put a .ToList() at the end of the function call:
var results = DataAccess.GetInfo<MyTableType>("SELECT * FROM [MyTable]").ToList();
Related
This question already has answers here:
How enumerate all classes with custom class attribute?
(8 answers)
Closed 3 years ago.
I'm working on a command tool in C#, although not for a terminal command-line. I have read the documentation on reflection and attributes but I'm not sure exactly what the "right" way to go about this is.
The problem isn't very complicated, but it needs to be easily extended. I need to just have Commands that are picked up and loaded in where their triggering strings are checked and if they match, methods are called. How I went about it just as a proof-of-concept was:
[System.AttributeUsage(System.AttributeTargets.Class)]
public class CommandAttribute : Attribute
{
public string Name { get; private set; } //e.g Help
public string TriggerString { get; private set; } //e.g. help, but generally think ls, pwd, etc
public CommandAttribute(string name, string triggerStrings)
{
this.Name = name;
this.TriggerString = triggerString;
}
}
Now, I decorated the class and it will implement methods from an interface. Eventually there will be many commands and my idea is to make it easy for someone with minimal programming experience to jump in and make a command.
using Foo.Commands.Attributes;
using Foo.Infrastructure;
namespace Foo.Commands
{
[Command("Help", "help")]
public class Help : IBotCommand
{
// as an example, if the message's contents match up with this command's triggerstring
public async Task ExecuteAction()
}
}
This gets injected into the console app where it will load the commands and get passed messages
public interface ICommandHandler
{
Task LoadCommands();
Task CheckMessageForCommands();
}
Then, everything with a matching attribute will get loaded in and when a message is received, it will check its contents against all CommandAttribute decorated classes' triggering strings, and if it matches, call the method ExecuteAction on that command class.
What I've seen/tried: I understand how to use reflection to get custom attribute data, however I'm confused as to getting the methods and calling them, and how all of this should be configured to be fairly performant with reflection being used. I see CLI tools and chat bots that use a similar method, I just cannot peek into their handlers to see how these get loaded in and I can't find a resource that explains how to go about accessing the methods of these classes. Attributes may not be the right answer here but I'm not sure how else to go about it.
Really, my main question is:
How do I setup The CommandHandler to load all of the attribute-decorated classes and call their methods, and how they should be instantiated within it. I know the second piece may be a bit more subjective but would newing them up be improper? Should they somehow be added to DI?
My solution ended up just using the Activator and lists. I still need to tweak this for performance and run more extensive stress tests, but here is my quick code for it:
// for reference: DiscordCommandAttribute is in Foo.Commands library where all the commands are, so for now it's the target as I removed the base class
// IDiscordCommand has every method needed, so casting it as that means down the line I can call my methods off of it. The base class was just for some reflection logic I was testing and was removed, so it's gone
public void LoadCommands() // called in ctor
{
var commands =
from t in typeof(DiscordCommandAttribute).Assembly.GetTypes()
let attribute = t.GetCustomAttribute(typeof(DiscordCommandAttribute), true)
where attribute != null
select new { Type = t, Attribute = attribute };
foreach (var obj in commands)
{
_commandInstances.Add((IDiscordCommand)Activator.CreateInstance(obj.Type));
_commandAttributes.Add(obj.Attribute as DiscordCommandAttribute);
}
}
There is probably a more sugary way to handle adding the objects to the lists, and some other data structure besides Lists might be more suitable, I'm just not sure if HashSet is right because it's not a direct Equals call. Eventually I will genericize the interface for this class and hide all of this logic in a base class. Still a lot of work to do.
Currently, just putting a stopwatch start before calling LoadCommands shows that the entire load takes 4ms. This is with 3 classes and a pretty anemic attribute, but I'm not too worried about the scale as I want any overhead on launch and not during command handling.
Using some code I wrote for this answer, you can find all types that implement an interface, e.g. IBotCommand, and then retrieve the custom attribute:
public static class TypeExt {
public static bool IsBuiltin(this Type aType) => new[] { "/dotnet/shared/microsoft", "/windows/microsoft.net" }.Any(p => aType.Assembly.CodeBase.ToLowerInvariant().Contains(p));
static Dictionary<Type, HashSet<Type>> FoundTypes = null;
static List<Type> LoadableTypes = null;
public static void RefreshLoadableTypes() {
LoadableTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetLoadableTypes()).ToList();
FoundTypes = new Dictionary<Type, HashSet<Type>>();
}
public static IEnumerable<Type> ImplementingTypes(this Type interfaceType, bool includeAbstractClasses = false, bool includeStructs = false, bool includeSystemTypes = false, bool includeInterfaces = false) {
if (FoundTypes != null && FoundTypes.TryGetValue(interfaceType, out var ft))
return ft;
else {
if (LoadableTypes == null)
RefreshLoadableTypes();
var ans = LoadableTypes
.Where(aType => (includeAbstractClasses || !aType.IsAbstract) &&
(includeInterfaces ? aType != interfaceType : !aType.IsInterface) &&
(includeStructs || !aType.IsValueType) &&
(includeSystemTypes || !aType.IsBuiltin()) &&
interfaceType.IsAssignableFrom(aType) &&
aType.GetInterfaces().Contains(interfaceType))
.ToHashSet();
FoundTypes[interfaceType] = ans;
return ans;
}
}
}
public static class AssemblyExt {
//https://stackoverflow.com/a/29379834/2557128
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
if (assembly == null)
throw new ArgumentNullException("assembly");
try {
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null);
}
}
}
Note: If you create types at runtime, you will need to run RefreshLoadableTypes to ensure they get returned.
If you are concerned about IBotCommand implementors existing without the CommandAttribute, you can filter the ImplementingTypes, otherwise:
var botCommands = typeof(IBotCommand)
.ImplementingTypes()
.Select(t => new { Type = t, attrib = t.GetTypeInfo().GetCustomAttribute<CommandAttribute>(false) })
.Select(ta => new {
ta.Type,
TriggerString = ta.attrib.TriggerString
})
.ToDictionary(tct => tct.TriggerString, tct => tct.Type);
With an extension method for your command Types:
public static class CmdTypeExt {
public static Task ExecuteAction(this Type commandType) {
var cmdObj = (IBotCommand)Activator.CreateInstance(commandType);
return cmdObj.ExecuteAction();
}
}
You can use the Dictionary like:
var cmdString = Console.ReadLine();
if (botCommands.TryGetValue(cmdString, out var cmdType))
await cmdType.ExecuteAction();
Overall, I might suggest having a method attribute and having static methods in static classes for commands, so multiple (related?) commands can be bundled in a single class.
PS My command interpreters have help associates with each command, and categories to group commands, so perhaps some more attribute parameters and/or another IBotCommand method to return a help string.
I would like to create a reusable method that I can use in Select() projections. Here is a simple example:
public partial class Product
{
public IQueryable<ProductImage> GetProductImages(bool ActiveOnly)
{
return this.ProductImages.Where(i => !ActiveOnly || i.IsActive);
}
}
public class App
{
public void Main()
{
var product = db.Products.Select(p => new {
p.Name,
p.GetProductImages(true)
}
}
}
The above will fail with the standard LINQ to Entities does not recognize the method ... and I get that I can call db.Products.ToList().Select(...), but that is not what I am looking for, as in my case, I am projecting in order to avoid pulling unnecessary data into memory.
I have successfully been using Microsoft.Linq.Translations for custom properties for a while now, however that will not work in my example because I need to pass in a parameter, so a method is really the only way I can see this working. Any help is greatly appreciated.
I have a WinForms app written in C# which has a lot of custom Classes.
Many of those Clases share similar Properties and Methods, and I have been able to cut down on the Coding by using Inheritance.
However, my Classes also have a lot of similar Static Methods (often the only thing that differs from one implementation to another between Classes is the reference to the Class itself. A very dumbed down example of this might be -
public class MyClass1()
{
public string IdentifyYourself()
{
return "This is the IdentifyYourself method in " + typeof(MyClass1).Name;
}
}
public class MyClass2()
{
public string IdentifyYourself()
{
return "This is the IdentifyYourself method in " + typeof(MyClass2).Name;
}
}
Is there a way to generalize the code in the IdentifyYourself() method so that there is no need to keep restating the Class in each implementation?
If these were not Static Methods then I could do something like
this.GetType().Name;
But of course the 'this' keyword is unavailable in a Static Method.
You may be wondering why these need to be Static methods. However the above example is not my actual code but a simplified version of the issue I am having. An actual example from my code (but still one of the shorter examples) is as follows -
public static DataTable List(bool active = true)
{
try
{
string table = typeof(MyClass).Name;
string sqlText = "SELECT * FROM [" + table + "] WHERE Active = #Active;";
SqlCommand sqlCom = new SqlCommand(sqlText);
sqlCom.Parameters.AddWithValue("#Active", active);
DataTable results = Express.GetTable(sqlCom);
return results;
}
catch (Exception eX)
{
...
}
}
The Code for the List() implementation differs from one Class to the next only in the first line
string table = typeof(MyClass).Name;
I'm thinking that if I could generalize this in some way, I could re-factor the code as follows -
public static DataTable List(bool active = true)
{
try
{
string nameOfClass = //...some generalized way of obtained the Name of the class...//
return UtilityClass.List(nameOfClass);
}
...
}
It is then a straight copy and paste of just a couple of lines of code each time I want to implement this in a new class and all the more detailed code can be placed in a Utility Class. The advantage of this isn't just avoiding having to type the name of each Class in each implementation, but also, if the details of the SQL operations need to change, it only has to be changed in one place.
The classic way to do that would be generics:
public static DataTable List<T>(bool active = true)
{
try
{
string table = typeof(T).Name;
// ...
} ....
and
var table = YourStaticClass.List<MyClass1>();
Frankly, however, I would also recommend making it return List<T> rather than DataTable. That depends a bit on what Express is, however (but: "dapper" would be trivial to use to do the query into a List<T>)
I created a class where the main task is get data from the DB and mapping it to some object. The problem is the same class needs to map different datareader to different object. So, what I tried to do is to get out the mapping method using delegates.
Here is part of my code. See the important rows in bold.
public class GetDetails<T>
{
**public delegate void DelegateMapping(T position, IDataReader reader);**
**public DelegateMapping mappingMethod;**
public T Get(T instance)
{
//Get IDs and Add to list
_db.ExecuteReader(storedProcedure.ToString(), CommandType.StoredProcedure, reader =>
{
while ( reader.Read() )
{
**mappingMethod(instance, reader);**
}
}, parameterList.ToArray());
return instance;
}
}
And this is the class which is calling and using the "GetDetails" class
public class PositionDB : DbBase
{
public Position GetPositionDetails(string IDs)
{
GetDetails<Position> getIDs = new GetDetails<Position>(base.db);
getIDs.storedProcedure = StoredProcedure.NET_ADM_GetPositionDetails;
//Set the Delegated Method
**getIDs.mappingMethod = MappingPositionDetails;**
//Set Parameters
getIDs.parameterList.AddInParam("PositionIds", DbType.String, IDs);
//Return the PositionId Collection
return getIDs.Get(new Position());
}
**private void MappingPositionDetails(Position position, IDataReader reader)
{
position.Id = reader["CompPositionId"];
position.Description = reader["Description"];
position.ExpirationDate = reader["ExpirationDate"];
position.Title = reader["Title"];
}**
}
The code is working OK.
The questios are:
Did I use delegate correctly?
This kind of solution can cause problems in the future (performance)?
There is another better solution?
Thank you very much
Sebastian
To specifically answer your questions:
Yes, you did use delegates correctly
Yes, it can cause problems due to concurrency issues while multithreading
I think so, I detailed one possible solution below
I would propose three changes:
Move the delegate call into the method (concurrency issues, one thread could change the mapping delegate while another thread tries to access it, now trying to map a reader to completely different object than provided)
Use the already present generic Action/Func delegates, no need to define your own.
Use lambda expressions to define the mapping, no need for extra methods
Notice: 2 and 3 will need at least .net 3.5.
Employing these two proposals, your code would look like this:
public class GetDetails<T>
{
public T Get (T instance, Action<T, IDataReader> mappingMethod)
{
//Get IDs and Add to list
_db.ExecuteReader(storedProcedure.ToString(), CommandType.StoredProcedure, reader =>
{
while ( reader.Read() )
{
mappingMethod(instance, reader);
}
}, parameterList.ToArray());
return instance;
}
}
Now you can use this method in a multi-threaded environment as well.
Edit
just realized it's just part of the code. I corrected my proposal to take this into account.
Yes (There's some improvements you could make, see 3)
Not performance wise, maybe some issues in discoverability.
I would use polymorphism to eliminate the delegate completely for discoerability. Perhaps using an abstract method/class. Also depending on which .NET version you're developing for you can use lambdas and simpler types.
public Action<Position, IDataReader> Mapping { get; set; }
Then
getIDs.Mapping = (position, reader) =>
{
position.Id = reader["CompPositionId"];
position.Description = reader["Description"];
position.ExpirationDate = reader["ExpirationDate"];
position.Title = reader["Title"];
};
My Title may be slightly off but here is what I am trying to do. I have a L2S Method that would be for every table that I would like to write once. This is to set a soft lock column where I will also need a Read and UnLock method. Here is what I have so far:
public static void LockRow(string TableName, int TablePrimaryKey)
{
using (var context = McpDataContext.Create())
{
var tableToLock = (from lockTable in context.tblPlans
where lockTable.PlanID == TablePrimaryKey
select lockTable).Single();
tableToLock.Locked = true;
context.SubmitChanges();
}
}
What I would like to do is replace context.tblPlans with context.TableName. Is this possible in LINQ? How so? I am assumming that I am going about it the wrong way so I'd be grateful for some direction/pointers.
Thanks
Update becuase the first example would not work.
You could do it with a generic method and an interface:
public interface IPlanTable
{
int PlanID { get; set; }
}
public static void LockRow<TEntity>(int TablePrimaryKey) where TEntity : class, IPlanTable
{
using (var context = McpDataContext.Create())
{
var tableToLock = (from lockTable in context.GetTable<TEntity>()
where lockTable.PlanID == TablePrimaryKey
select lockTable).Single();
tableToLock.Locked = true;
context.SubmitChanges();
}
}
You will also have to use the fact that the Linw2SQL tables are created as partial classes to extend them so all the relevent table implement IPlanTable
You would use it like below:
LockRow<tblPlan>(23);
simply replace tblPlan with whatever the name of your table class is.
However this won't allow you to set the table at runtime, LinqToSQL is object orientated and type safe, specifying the table you want to retreive is contrary to how it si designed to work.