creating actions based on specific fields having specific values - c#

Imagine I have a C# app sitting on a server somewhere that is creating instances of the Item class and publishing them on a messaging service.
class Item
{
public int ID1, ID2, ID3;
public double Value1, Value2, Value3;
}
Now I have another C# app on a desktop somewhere listening to these messages. I want to be able to create callbacks based on specified values of the ID# fields. For example, "call this method whenever a message comes in where ID1 = 2, ID2 = 160, and ID3 = anything". I think this would be straightforward if I used string key-value pairs, but ideally I could do this without giving up static typing. I imagine this requires reflection, but I'm not entirely sure where to start.
I'm picturing the app creating an instance of Item with the required ID# values (let's say -1 means unspecified), and passing that into a RegisterCallback method of some object ItemListener. Then whenever ItemListener receives a new Item message, it can check for any callbacks that match, and act accordingly. Is this a reasonable design? Any suggestions on what to look at to implement it?

class ConditionalCallback()
{
public Predicte<Item> Predicate {get; set;}
public Action<Item> Callback {get; set;}
}
List<ConditionalCallback> callbacks = new List<ConditionalCallback>();
public AddCallBack(Predicte<Item> pred, Action<Item> callback)
{
callbacks.Add(new ConditionalCallback {
Predicate = pred,
Callback = callback
});
}
void HandleItem(Item item)
{
foreach(var cc in callbacks)
if (cc.Predicate(item))
cc.Callback(item);
}
//
AddCallBack( i=> i.ID1 = 2 && i.ID2 = 160 && i.ID3 = anything", MyCallback);

It's possible to do without giving up type safety. What you basically need is a tuple type (.NET 4.0 has this, but if you are not using that, you can create your own easily) where the members are nullable types of the id types that you want to match on.
You would then use instances of these types as keys in a dictionary where the value is the delegate that you would execute on a match. For example, your key type would look like this:
struct ItemKey
{
public int? ID1, ID2, ID3;
public double? Value1, Value2, Value3;
}
Then, for your example of processing a message where ID1 = 1, ID2 = 160, and ID3 = anything, you would instantiate the key for the delegate in the dictionary like this:
new ItemKey { ID1 = 1, ID2 = 160 }
Note, it's very important here that ItemKey is a structure, as it provides the correct implementations of GetHashCode and Equals which are essential to being keyed in the dictionary correctly.
A drawback of this design is that you have to be explicit for all the kinds of partial matches that are possible.
For example, if you wanted a delegate to be processed when the ID1 = 2, and another one where you want to process when ID1 = 2 and ID2 = 3, you have to specify those cases specifically. This can cause the dictionary to grow very very fast if you have many permutations.
If this is the case, you might want to look into a database solution of some kind where you store the ID values in separate columns, and then do a lookup on the appropriate columns (filtering on selective columns). Then, you would have a type name in a field on the row, which you could use to create a type instance through reflection, which implements an interface or derives from a base type, which you can create a variable of and call the method which performs the functionality.

Related

Add Like restriction to dictionary

I set an nHibernate restriction for a project Id like so:
var attachmentProperties = new System.Collections.Generic.Dictionary<string, object>();
attachmentProperties.Add("Id", this.project.Id);
so it returns anything with that project Id by setting the Restrictions:
NHibernate.Criterion.Restrictions.AllEq(attachmentProperties));
This works. I now want to add another restriction using the equivalent of a SQL Like. I tried:
attachmentProperties.Add(NHibernate.Criterion.Restrictions.Like("Type", "%dog%"));
There is no argument given that corresponds to required formal
parameter value
I want to get anything in which the Type contains "dog" (in addition to the Id matching). How do I do this?
You have two options:
Have all the Dog classes implement a common interface (IDog). Then, the dictionary will be:
new System.Collections.Generic.Dictionary<string, IDog>();
or, you could do this, which might be a little strange, if what's up top isn't available for some reason where you validate the Add method. You'd similarly have to have checks on other methods.
class DogDictionary : Dictionary<string, object>
{
public virtual void Add(KeyValuePair<string, object> item)
{
if (item.Value.GetType().ToString().ToUpper().Contains("DOG"))
throw new ApplicationException("Invalid Data Type for Value. Should Have 'Dog' in the Object Name");
}
}
I'd go with the first option though.
Another way is to create a Restrictions.Conjunction for the Criteria:
Conjunction conjunction = Restrictions.Conjunction();
conjunction.Add(Restrictions.Eq("Id", this.project.Id));
conjunction.Add(Restrictions.Like("Type", "%dog%"));
conjunction.Add(Restrictions.Not(Restrictions.Like("Type", "%cat%")));

How to pass variable entities to a generic function?

If i generate my entities through Entity Framework Database First, and i want to use a function like that:
AuditManager.DefaultConfiguration.Exclude<T>();
considering that the number of times i want to call it should be equal to the number of entities
ex:
AuditManager.DefaultConfiguration.Exclude<Employee>();
AuditManager.DefaultConfiguration.Exclude<Department>();
AuditManager.DefaultConfiguration.Exclude<Room>();
Now how to Loop through selected number of entities and pass every one to the Exclude function ?
The obvious solution would be to call the method for every entity-type you want to hide. Like this:
AuditManager.DefaultConfiguration.Exclude<Employee>();
AuditManager.DefaultConfiguration.Exclude<Department>();
AuditManager.DefaultConfiguration.Exclude<Room>();
You can add conditional statements (ifs) around them to do it dynamically.
Howevery, if you want a fully flexible solution, where you call the Exclude method based on metadata, you need something else. Something like this:
var types = new[] { typeof(Employee), typeof(Department), typeof(Room) };
var instance = AuditManager.DefaultConfiguration;
var openGenericMethod = instance.GetType().GetMethod("Exclude");
foreach (var #type in types)
{
var closedGenericMethod = openGenericMethod.MakeGenericMethod(#type);
closedGenericMethod.Invoke(instance, null);
}
This assumes that the Exclude<T> method is an instance method on whatever instance DefaultConfiguration points to.
An alternative to looping through your entity types is to make the entities you don't want audited implement the same interface and exclude that. For example:
public interface IExcludeFromAudit
{ }
And your entities:
public class Order : IExcludeFromAudit
{
//snip
}
And now just exclude the interface:
AuditManager.DefaultConfiguration.Exclude<IExcludeFromAudit>();
The benefit of this is that it's now easy to control which ones are excluded.

C# call variables by formatting their name on the fly

I have 12 columns in a datagridview (they're 12 properties going from v1 - v12). Is it possible to create a dynamic system that does something like this:
int i = 5;
var variablename = "v" + i;
String content = product.variablename;
This would be a generic function of
if(i == 5) {
content = product.v5
}
Yes, I know that naming my properties v1-v12 isn't good practice, but it has its uses. I could write 12 if clauses, but I'm just wondering if it's possible or not.
EDIT:
In the specific example I have an array of 8000 products with each v1-v12 properties. I want to dynamically get the values of specific cells, so
product[row].(v+column) should become products[23].v5 for example.
It's more of an example case than it is actually needed, just want to figure out if it can be done.
Dictionary will give you flexibility and control to store as many as you like and retrieve them without using Reflection etc.
var values = new Dictionary<string, string>();
values.Add("v" + i, "somevalue");
and to retrieve it:
var storedValue = values["v" + i];
On a side note, if you are using Data Grid View to have multiple lines. You can also use DataTable to store information. Or if you have a fixed structure, why not make a class that will represent that information and use List<T>
var variablename = "v" + i;
MethodInfo method = product.GetType().GetMethod(variablename);
object result = method.Invoke(product, new object[] {}); // pass in the parameters if you need to
You can use reflection for that purpose
So you have a class Product that has 12 properties that got no Usefull name?
In that case i would just create a class Product that has an Array of Properties.
public class Product
{
public string[] Properties {get;set;}
}
Then to access the 5t generic nameless property of the product 23 you call.
products[23].Properties[5]
While I still doubt that the products properties can not be named properly.

How to sort a list using my own logic (not alphabetically or numerically)

I've spent the last 30 mins looking through existing answers for what I think is a common question, but nothing quite hits it for me. Apologies if this is a dupe.
I've got a list of objects.
List<Journey> journeys;
The object has a 'status' property - this is a string.
class Journey
{
public string Name;
public string Status;
}
I want to sort based on this string, however not alphabetically. The status depicts the object's position through a journey, either "Enroute", "Finished", or "Error". When sorted ascending I want them to appear in the following order: "Error", "Enroute", "Finished". (In practice there are more statuses than this so renaming them to fall in alphabetical order isn't an option)
Aside from creating a class for 'status' with value and sort order properties, and then sorting based on that, how do I do this? Or is that the best method?
You can define the you sorting logic inside of custom function which is provided to Comparison delegate:
List<Journey> list = new List<Journey>();
list.Sort(new Comparison<Journey>((Journey source, Journey compare) =>
{
// here is my custom compare logic
return // -1, 0 or 1
}));
Just another thought:
class Journey
{
public enum JourneyStatus
{
Enroute,
Finished,
Error
}
public string Name;
public JourneyStatus Status;
}
Used with OrderBy:
var journeys = new List<Journey>();
journeys.Add(new Journey() { Name = "Test1", Status = Journey.JourneyStatus.Enroute });
journeys.Add(new Journey() { Name = "Test2", Status = Journey.JourneyStatus.Error });
journeys.Add(new Journey() { Name = "Test3", Status = Journey.JourneyStatus.Finished });
journeys.Add(new Journey() { Name = "Test4", Status = Journey.JourneyStatus.Enroute });
journeys = journeys.OrderBy(x => x.Status).ToList();
foreach (var j in journeys)
Console.WriteLine("{0} : {1}", j.Name, j.Status);
Output:
Test1 : Enroute
Test4 : Enroute
Test3 : Finished
Test2 : Error
Or you might modify the lambda passed to OrderBy to map the value of Status string to an int.
In some situations you might want to implement IComparer<T>, like Jon said. It can help keeping the sorting logic and the class definition itself in one place.
You need to create a class that implements IComparer<Journey> and implement the Compare method accordingly.
You don't mention how you are sorting exactly, but pretty much all methods of the BCL that involve sorting have an overload that accepts an IComparer so that you can plug in your logic.
Aside from creating a class for 'status' with value and sort order properties, and then sorting based on that, how do I do this?
As this is some custom order you need creating such a class is a good idea.
Derive from Comparer<T> or StringComparer class, implement class with your custom logic, pass instance of this class to sort method.
One way to do this is to assign an order number to each item. So create a database table to hold your items and their order:
Column Name, Column Order
Enrout , 0
Finished, 1
Error, 2
Then when I populate a drop down I can sort by the Order in my SQL select rather than the name.
Or if you don't want to use a database, you could change Status to include the order and then just parse out the Status name: so Status values might look like this: "0,Enrout","1,Finished","2,Error" then it will naturally sort. Then just use split to separate the order from the name:
string[] statusArr = status.split(',');
string order = statusArr[0];
string statusname = statusArr[1];
There's a lot of ways to skin this cat.

Select new object as a parameter while preserving it's run-time-generated type

Consider the following:
// select a subset of the DataTable
var subset = DataTable.Where(...).Select(row => new
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
// or create a new object
var subset = new {
Id = 1,
Name = "something random",
Email = "name#domain.tld"
};
Is there any way to use the subset variable as a parameter to a method, without it being cast as a plain Object? Can you somehow carry the auto-generated type of the variable?
I am trying to avoid having to create new classes every time I want to pass LINQ subsets to methods.
Random generic approaches are welcome.
No, passing anonymous types about isn't generally a good idea because you lose the type information*. You should create a concrete type and use that instead.
var subset = DataTable.Where(...).Select(row => new SomeType
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
Alternatively you can use the Tuple type if you are using .NET 4. This is a simple way to create "disposable" types and still get some type-safety.
*Actually there is a workaround, but I regard it is an ugly hack and would advise that you don't do it.
If I need to do this, I use resharper's "Replace Anonymous Type With Named Class" refactoring option. Then you have an appropriate named type to expose over the API, and you haven't had to do any work. This also gives you options to create it immutable (like anonymous types) or mutable, nested vs top-level, etc.
BTW, I don't recommend struct here (from the question).
Another option is to pass the behaviour into the method - i.e. an Action<int,string,string> callback - then do something like:
foreach(item in query) callback(item);
However, I don't like this as it is not obvious that there is a likely error in:
DoSomething(args, (id, email, name) => Email(To: email, Subject: name));
(the error being that it should probably be (id, name, email), if you see what I mean)
You can use a generic method:
public static void Foo<T>(T item)
{
// Do whatever
}
Then if you call
Foo(subset);
the compiler will infer T for you. Whether or not that actually helps you is another matter... it depends on what the method is meant to do. Obviously Foo can't refer to Id, Name, Email etc.
In general, if multiple methods should know about the same members, then you should use a named type. The usual case for passing them to generic methods is where the method really doesn't care about what type is involved, such as in LINQ.
I've made a feature request for C# 5 that we should be able to create types which have all the same features as anonymous types (immutability, equality, hash code generation, ToString dumping) but for simple named types. We'll see if it actually happens...
Anonymous Types don't provide much help outside of the context they where created.
If you need to pass an Anonymous Type to a method, either this method is very generic like (Example)
void PrintAllObjectProperties(object obj);
witch you would use reflection to do the work, or you are doing something wrong.
Here's what I came up with...
Extension method on Object:
public static class ObjectExtensions
{
/// <summary>
/// Cast Object to anonymous type.
/// E.G.: new Object().ToAnonymousType(new { Property = new Type() });
/// </summary>
public static T ToAnonymousType<T>(this Object o, T t)
{
return (T)o;
}
}
Usage:
public void HandleAnonymousTypeAsParameter(Object o)
{
var anonymousType = o.ToAnonymousType(new
{
Id = new Int32(),
Foo = new String(),
Bar = new String()
});
// ... You can do this in even less characters:
var anonymousType = o.ToAnonymousType(new { Id = 0, Foo = "", Bar = "" });
}
HandleAnonymousTypeAsParameter(new
{
Id = 1,
Foo = "foo",
Bar = "bar"
});
Credits goes to John Skeet and Thomas P.

Categories

Resources