Modify Insert for general use - c#

Hello as of writing this inserting to MongoDB is done with the following code using a class named Game.
var games = database.GetCollection<Game>("Games");
Game newGame = new Game()
{
Name = "Monopoly",
Price = 22,
Category = "Board Game"
};
games.InsertOne(newGame);
I am trying to create a function that takes these as parameters:
void insert(Game game, string collection)
{
var games = database.GetCollection<Game>(collection);
games.InsertOne(game);
}
// use would be like
Game game=new Game();
//...properties etc
test(game,"Games");
But I would like to use this function as a general-purpose insert function:
MyClass1 c1=new MyClass1();
//...properties etc
insert(c1, "MyClass1");
MyClass2 c2=new MyClass2();
//...properties etc
insert(c2,"MyClass2");
How should I modify the insert method so it accepts any given class and inserts it into the given MongoDB collection?
void insert(object obj, string collection)
{
//...code to get obj's class?
var mongoObject = database.GetCollection<obj_class>(collection);
mongoObject.InsertOne(game);
}

You can modify the Insert method to support the generic type as below:
void Insert<T>(T obj, string collectionName) where T : new()
{
var collection = database.GetCollection<T>(collectionName);
collection.InsertOne(obj);
}

Related

Func to get the property instead of just the value

Is it possible to input an object and a second parameter to a method so that the second parameter can be used (without using strings and reflection) to get the a property of the object and use the property to both read and write the value?
I've written two methods below that I can use but each one has drawbacks. The first DoStuff is typesafe ("good") but it needs three parameters ("bad"). The second needs two parameters (optimal), but it is not typesafe ("bad") as it relies on a string to specify the property. Maybe there is some solution using Expression that I have not thought of?
Background: The usecase is that I want to be able to "extend" the value of any object (in my case I have lists of objects from several object reposites and these objects may have serveral properties containing userids as strings. another repository conatins the users and I want add info about the user to the strings in the previous repositories)
public class Foo
{
public string Bar {get; set;}
}
void Main()
{
var x = new Foo { Bar = "hello" };
Console.WriteLine(x.Bar); // prints "hello"
// the second paramter (Func<..>) gets the value, the third parameter Action<...>) sets the value
DoStuff(x, y => y.Bar, (z, val) => z.Bar = val);
Console.WriteLine(x.Bar); // prints "hello goodbye"
// just one parameter to get the property, but I would like this to be more type safe, preferably a Func
DoStuff2(x, nameof(x.Bar));
Console.WriteLine(x.Bar); // prints "hello goodbye again"
}
public void DoStuff<T>(
T obj,
Func<T, string> getProp,
Action<T, string> setProp)
{
var x = getProp(obj);
setProp(obj, x + " goodbye");
}
public void DoStuff2<T>(
T obj,
string propName)
{
var propInfo = typeof(T).GetProperty(propName);
var currValue = propInfo.GetValue(obj) as string;
propInfo.SetValue(obj, currValue + " again");
}
Well i did something like that awhile ago. here is an example:
public void SetValue<T, TP>(T obj, Expression<Func<T, TP>> action, TP value) where T : class
{
var member = action.Body is UnaryExpression
? ((MemberExpression)((UnaryExpression)action.Body).Operand)
: (action.Body is MethodCallExpression
? ((MemberExpression)((MethodCallExpression)action.Body).Object)
: (MemberExpression)action.Body);
var key = member?.Member.Name;
typeof(T).GetProperty(key).SetValue(obj, value);
}
You call it like this.
SetValue<User>(x=> x.UserName, "Admin");

Standard way to handle a series of lists of custom classes

Sorry about the vague title. My problem is as follows:
I have a series of classes (classA, classB, classC...) and I have a list for each class. I want to be able to let the user Import, Export, Add, and Remove on these lists.
The way I attempted to handle this problem is:
Create a public, static class to work as a global class.
For each class create a list ( public static List aList = new List(); ) inside the global class.
Use JSON to save & load the lists between instances (and also for import/export in the future)
Use reflection and generate a FieldInfo for each list.
Send the FieldInfo for classA to the deleteObjectFromList page
Try to use Interfaces to create a list and cast list to list
I obviously can't cast list to list because I am not allowed to add objects of classB to a list of classA, even though they are both implementing the interface.
I have considered creating a custom listClass and try to handle the casting in that way, but it seems messy.
What is the best way to handle this problem? I don't want to have to handle each class individually because there will realistically be +20 classes.
Since code snippets were requested, here is my global class.
public static class globalClass
{
public static List<classA> characterList = new List<classA>();
public static List<classB> raceList = new List<classB>();
public static List<classC> deityList = new List<classC>();
}
One of my custom classes used for a list,
public class classA: IInterface
{
public string getDisplayName()
{
return name;
}
//general info
public String name;
}
How I am saving a list
private async Task saveItem<T>(List<T> list, string p)
{
var serializer = new DataContractJsonSerializer(list.GetType());
//changed "LocalFolder" to "RoamingFolder"
using (var stream = await ApplicationData.Current.RoamingFolder.OpenStreamForWriteAsync(
p,
CreationCollisionOption.ReplaceExisting))
{
serializer.WriteObject(stream, list);
}
System.Diagnostics.Debug.WriteLine("done writing!");
}
How I am getting the FieldInfo and passing it to the next page on my App.
TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(typeof(globalClass));
IEnumerable<FieldInfo> FieldInfoList = typeInfo.DeclaredFields;
foreach (FieldInfo f in FieldInfoList)
{
Button deleteButton = new Button();
deleteButton.Click += delegate(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(deleteObjectFromLibraryPage), f);
};
}
and how I was trying to get the actual list from the fieldinfo I sent.
private List<IDisplayable> list;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedTo(e);
FieldInfo f = (FieldInfo)e.Parameter;
String s = f.GetValue(null).ToString();
TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(typeof(globalClass));
IEnumerable<FieldInfo> FieldInfoList = typeInfo.DeclaredFields;
var v = FieldInfoList.Where(c => c.GetType().Equals(f.GetType()));
this.list = globalClass.getList(f);
}
The last line of the above code is where I became stuck, and wanted to figure out the standard approach to this problem.

Use Action delegate to call correct function based on generic type

I have seen this pattern/approach used before, and I'm trying to recreate it to make some of my existing code more efficient.
The Use Case:
A complex object is retrieved from a source system. Only a subset of the information will be used by the client, so we must 'map' this complex object to a simple POCO for JSON serialization; additionally, in this mapping method, some other data formatting is done. First, we pass our complex object into a generic method that does some basic processing
// Generic Method, Entry Point for mapping
static void GenericEntry<T, TK>(string userid, string environment, DBContext context) {
.... // do stuff with userid and environment to set a context
.... // query results, which return a complex object of Type TK
// Here is where I would like to use an Action delegate to call the appropriate map
// method.. there could hundreds of objects that map and process down to a POCO,
// Currently, this logic is using reflection to find the appropriate method with
// the appropriate signature... something like:
Type functionType = typeof(DTOFunctions);
var methods = functionType.GetMethods(BindingFlags.Public | BindingFlags.Static);
var mi = methods.FirstOrDefault(x => x.Name == "MapObject" &&
x.ReturnType == typeof(T));
if (mi == null) throw new ArgumentException(string.Format("Unable to find method MapObject for {0}", typeof(TK).Name));
var resultList = new ArrayList();
foreach (var row in results)
{
var poco = mi.Invoke(functionType, new object[] { row });
resultList.Add(poco);
}
if (resultCount == -1) resultCount = resultList.Count;
return SerializeDTO(resultList, ResponseDataTypes.JSON, resultCount);
// THERE HAS TO BE A BETTER WAY STACKOVERFLOW! HALP!
}
public Class DTOFunctions {
// Mapping Method from Complex to Simple object
static SimplePOCO_A MapObject(ComplexObject_A cmplx){
var poco = new SimplePOCO_A();
.... // mapping from cmplx field to SimplePOCO field
}
static SimplePOCO_B MapObject(ComplexObject_B cmplx) {
var poco = new SimplePOCO_B();
.... // mapping from cmplx field to SimplePOCO fiel
}
}
I'm not quite sure what you're asking, but is something like this what you want?
static void GenericEntry<T, TK>(string userid, string environment,
DBContext context, Func<T, TK> conversion)
{
//....
var resultList = new List<TK>();
foreach (var row in results)
{
var poco = conversion(row);
resultList.Add(poco);
}
//....
}
Called as:
GenericEntry<ComplexObject, SimplePOCO>(userid, environment, context, DTOFunctions.MapObject)
(Note the lack of () in the argument).
It's looks like you could maybe implement the Proxy Pattern here.
Otherwise, maybe move the logic to the actual objects themselves and add a ToJSON() method in each of the ComplexObjects that knows how to serialize itself. Then append them together to make the JSON array. This will depend on what you're using to serialize the JSON, so in the example below, I've just done it manually.
The common JSONSerializable interface
public interface IJsonSerializable
{
string ToJson();
}
The Complex and simple objects:
public class ComplexObjectA : IJsonSerializable
{
public string ToJson()
{
var simpleObject = new SimpleObjectA();
// Map away
// Then serialize
return SerializeDTO(simpleObject);
}
private string SerializeDTO(SimpleObjectA simpleObject)
{
throw new NotImplementedException();
}
}
public class SimpleObjectA
{
// simple properties
}
And then entry point
static void GenericEntry<T, TK>(string userid, string environment, DBContext context)
{
// Magic happens here
var results = GetResults();
// More magic
var resultList = new List<string>();
foreach (var row in results)
{
var poco = row.ToJson();
resultList.Add(poco);
}
return String.Format("[{0}]", String.Join(resultList, ", "));
}

Property Type as Generic parameter

I'm trying to figure out how I can make a Generics call take a variable for the Type. In the call below it take a type "DAL.Account" and works fine.
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
I want to change that so that I can pass a variable in place of the "DAL.Account". Something like this but I know that won't work as you can't pass property as a Type.
ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray())
Below is the shell pieces of code I think explains what I'm trying to do. Generics is not my strong suit so I'm looking for some help. Is there anyway that I can make this happen?
//Stores a "Type" that indicates what Object is a Criteria for.
public class AccountCriteria : IGeneratedCriteria
{
...
public Type EntityType
{
get {return typeof(DAL.Account);}
}
}
//I have added a function to the DataContext called "GetTable"
// And then used it as an example in a Console App to test its functionality.
public class ADRPDataContext : NHibernateDataContext
{
...
public CodeSmith.Data.NHibernate.ITable<T> GetTable<T>() where T : EntityBase
{
var tb = new CodeSmith.Data.NHibernate.Table<T>(this);
return tb;
}
}
// console application that uses DataContext.GetTable
class Program
{
static void Main(string[] args)
{
using (var ctx = new ADRPDataContext())
{
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
}
}
}
//ExistsCommand class that uses the EntityType property of the Critera to generate the data.
public class ExistsCommand
{
private IGeneratedCriteria Criteria { get; set; }
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
//This was my first attempt but doesn't work becuase you can't pass a property in for a Type.
//But I can figure out how to write this so that it will work.
Result = ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
You are looking to instantiate a generic type. Some info can be found here
This is a simple example demonstrating how to instantiate a List with a capacity of 3. Here is a method that you can call to create a generic when you don't know the type:
public static Object CreateGenericListOfType(Type typeGenericWillBe)
{
//alternative to the followin:
//List<String> myList = new List<String>(3);
//build parameters for the generic's constructor (obviously this code wouldn't work if you had different constructors for each potential type)
object[] constructorArgs = new Object[1];
constructorArgs[0] = 3;
//instantiate the generic. Same as calling the one line example (commented out) above. Results in a List<String> with 3 list items
Type genericListType = typeof(List<>);
Type[] typeArgs = { typeGenericWillBe };
Type myNewGeneric = genericListType.MakeGenericType(typeArgs);
object GenericOfType = Activator.CreateInstance(myNewGeneric, constructorArgs);
return GenericOfType;
}
And here is some sample code that will show you the example method works:
List<String> Strings = (List<String>)InstantiateGenericTypeWithReflection.CreateGenericListOfType(typeof(String));
//demonstrate the object is actually a List<String> and we can do stuff like use linq extensions (isn't a good use of linq but serves as example)
Strings.Add("frist");
Strings.Add("2nd");
Strings.Add("tird");
Console.WriteLine("item index 2 value: " + Strings.Where(strings => strings == "2").First());
In your example, replace your GetTable<Criteria.EntityType>() with CreateGenericTableOfType(Criteria.EntityType). This will return a generic table of whatever type you pass in. You will of course need to implement the method properly (handle constructor args, change List to Table etc).
I think you need to change the way you're doing this slightly, and instead use generics instead of the EntityType property. Perhaps something along the lines of the following:
// Create an abstract class to be used as the base for classes that are supported by
// ExistsCommand and any other classes where you need a similar pattern
public abstract class ExtendedCriteria<T> : IGeneratedCriteria
{
public ExistsCommand GetExistsCommand()
{
return new ExistsCommand<T>(this);
}
}
// Make the non-generic ExistsCommand abstract
public abstract class ExistsCommand
{
protected abstract void DataPortal_Execute();
}
// Create a generic sub-class of ExistsCommand with the type parameter used in the GetTable call
// where you were previously trying to use the EntityType property
public class ExistsCommand<T> : ExistsCommand
{
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
Result = ctx.GetTable<T>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
// Derive the AccountCriteria from ExtendedCriteria<T> with T the entity type
public class AccountCriteria : ExtendedCriteria<DAL.Account>
{
...
}

Purpose of Activator.CreateInstance with example?

Can someone explain Activator.CreateInstance() purpose in detail?
Say you have a class called MyFancyObject like this one below:
class MyFancyObject
{
public int A { get;set;}
}
It lets you turn:
String ClassName = "MyFancyObject";
Into
MyFancyObject obj;
Using
obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))
and can then do stuff like:
obj.A = 100;
That's its purpose. It also has many other overloads such as providing a Type instead of the class name in a string. Why you would have a problem like that is a different story. Here's some people who needed it:
Createinstance() - Am I doing this right?
C# Using Activator.CreateInstance
Creating an object without knowing the class name at design time
Well i can give you an example why to use something like that. Think of a game where you want to store your level and enemies in an XML file. When you parse this file, you might have an element like this.
<Enemy X="10" Y="100" Type="MyGame.OrcGuard"/>
what you can do now is, create dynamically the objects found in your level file.
foreach(XmlNode node in doc)
var enemy = Activator.CreateInstance(null, node.Attributes["Type"]);
This is very useful, for building dynamic enviroments. Of course its also possible to use this for Plugin or addin scenarios and alot more.
My good friend MSDN can explain it to you, with an example
Here is the code in case the link or content changes in the future:
using System;
class DynamicInstanceList
{
private static string instanceSpec = "System.EventArgs;System.Random;" +
"System.Exception;System.Object;System.Version";
public static void Main()
{
string[] instances = instanceSpec.Split(';');
Array instlist = Array.CreateInstance(typeof(object), instances.Length);
object item;
for (int i = 0; i < instances.Length; i++)
{
// create the object from the specification string
Console.WriteLine("Creating instance of: {0}", instances[i]);
item = Activator.CreateInstance(Type.GetType(instances[i]));
instlist.SetValue(item, i);
}
Console.WriteLine("\nObjects and their default values:\n");
foreach (object o in instlist)
{
Console.WriteLine("Type: {0}\nValue: {1}\nHashCode: {2}\n",
o.GetType().FullName, o.ToString(), o.GetHashCode());
}
}
}
// This program will display output similar to the following:
//
// Creating instance of: System.EventArgs
// Creating instance of: System.Random
// Creating instance of: System.Exception
// Creating instance of: System.Object
// Creating instance of: System.Version
//
// Objects and their default values:
//
// Type: System.EventArgs
// Value: System.EventArgs
// HashCode: 46104728
//
// Type: System.Random
// Value: System.Random
// HashCode: 12289376
//
// Type: System.Exception
// Value: System.Exception: Exception of type 'System.Exception' was thrown.
// HashCode: 55530882
//
// Type: System.Object
// Value: System.Object
// HashCode: 30015890
//
// Type: System.Version
// Value: 0.0
// HashCode: 1048575
You can also do this -
var handle = Activator.CreateInstance("AssemblyName",
"Full name of the class including the namespace and class name");
var obj = handle.Unwrap();
A good example could be next: for instance you have a set of Loggers and you allows user to specify type to be used in runtime via configuration file.
Then:
string rawLoggerType = configurationService.GetLoggerType();
Type loggerType = Type.GetType(rawLoggerType);
ILogger logger = Activator.CreateInstance(loggerType.GetType()) as ILogger;
OR another case is when you have a common entities factory, which creates entity, and is also responsible on initialization of an entity by data received from DB:
(pseudocode)
public TEntity CreateEntityFromDataRow<TEntity>(DataRow row)
where TEntity : IDbEntity, class
{
MethodInfo methodInfo = typeof(T).GetMethod("BuildFromDataRow");
TEntity instance = Activator.CreateInstance(typeof(TEntity)) as TEntity;
return methodInfo.Invoke(instance, new object[] { row } ) as TEntity;
}
The Activator.CreateInstance method creates an instance of a specified type using the constructor that best matches the specified parameters.
For example, let's say that you have the type name as a string, and you want to use the string to create an instance of that type. You could use Activator.CreateInstance for this:
string objTypeName = "Foo";
Foo foo = (Foo)Activator.CreateInstance(Type.GetType(objTypeName));
Here's an MSDN article that explains it's application in more detail:
http://msdn.microsoft.com/en-us/library/wccyzw83.aspx
Building off of deepee1 and this, here's how to accept a class name in a string, and then use it to read and write to a database with LINQ. I use "dynamic" instead of deepee1's casting because it allows me to assign properties, which allows us to dynamically select and operate on any table we want.
Type tableType = Assembly.GetExecutingAssembly().GetType("NameSpace.TableName");
ITable itable = dbcontext.GetTable(tableType);
//prints contents of the table
foreach (object y in itable) {
string value = (string)y.GetType().GetProperty("ColumnName").GetValue(y, null);
Console.WriteLine(value);
}
//inserting into a table
dynamic tableClass = Activator.CreateInstance(tableType);
//Alternative to using tableType, using Tony's tips
dynamic tableClass = Activator.CreateInstance(null, "NameSpace.TableName").Unwrap();
tableClass.Word = userParameter;
itable.InsertOnSubmit(tableClass);
dbcontext.SubmitChanges();
//sql equivalent
dbcontext.ExecuteCommand("INSERT INTO [TableNme]([ColumnName]) VALUES ({0})", userParameter);
Coupled with reflection, I found Activator.CreateInstance to be very helpful in mapping stored procedure result to a custom class as described in the following answer.
Why would you use it if you already knew the class and were going to cast it?
Why not just do it the old fashioned way and make the class like you always make it? There's no advantage to this over the way it's done normally.
Is there a way to take the text and operate on it thusly:
label1.txt = "Pizza"
Magic(label1.txt) p = new Magic(lablel1.txt)(arg1, arg2, arg3);
p.method1();
p.method2();
If I already know its a Pizza there's no advantage to:
p = (Pizza)somefancyjunk("Pizza"); over
Pizza p = new Pizza();
but I see a huge advantage to the Magic method if it exists.
We used it for something like e.g.
public interface IExample
{
void DoSomethingAmazing();
}
public class ExampleA : IExample
{
public void DoSomethingAmazing()
{
Console.WriteLine("AAAA");
}
public void DoA()
{
Console.WriteLine("A")
}
}
public class ExampleB : IExample
{
public void DoSomethingAmazing()
{
Console.WriteLine("BBBB");
}
public void DoB()
{
Console.WriteLine("B")
}
}
and then provided the type serialized from a settings file
=> Even after compilation we can still change the applications behaviour by using different settings
Something like e.g.
public static class Programm
{
public static void Main()
{
var type = MagicMethodThatReadsASerializedTypeFromTheSettings();
var example = (IExample) Activator.CreateInstance(type);
example.DoSomethingAmazing();
switch(example)
{
case ExampleA a:
a.DoA();
break;
case ExampleB b:
b.DoB();
break;
}
}
}
And I use it in a custom multi-user serialization where I send RPC (Remote Procedure Calls) to other devices with parameters.
Extremly cropped to the necesarry it basically does
public ISendable
{
public byte[] ToBytes();
public void FromBytes(byte[] bytes);
}
// Converts any ISendable into a byte[] with the content
// typeBytes + contentBytes
public byte[] ToBytes(ISendable toSend)
{
var typeBytes = Encoding.ASCII.GetBytes(toSend.GetType().AssemblyQualifiedName);
var contentBytes = ISendable.ToBytes();
return MagicMethodToCombineByteArrays(typeBytes, contentBytes);
}
// Coonverts back from byte[] to the according ISendable
// by first reading the type, creating the instance and filling it with
// contentBytes
public T FromBytes<T>(byte[] bytes) where T : ISendable
{
MagicMethodToSplitInputBytes(out var typeBytes, out var contentBytes);
var type = Encoding.ASCII.GetString(typeBytes);
var instance = (T) Activator.CreateInstance(type);
instance.FromBytes(contentBytes);
return instance;
}

Categories

Resources