How to make the below code generic? - c#

I have a method as under
private int SaveRecord(PartnerViewLog partnerViewLog, PortalConstant.DataSourceType DataSourceType, Func<IDataAccess, PartnerViewLog, int> chooseSelector)
{
int results = -1;
var dataPlugin = DataPlugins.FirstOrDefault(i => i.Metadata["SQLMetaData"].ToString() == DataSourceType.EnumToString());
if (dataPlugin != null)
{
results = chooseSelector(dataPlugin.Value, partnerViewLog);
}
return results;
}
I am invoking it as under
public int SavePartnerViewLog(PartnerViewLog partnerViewLog, PortalConstant.DataSourceType DataSourceType)
{
return SaveRecord(partnerViewLog, DataSourceType, (i, u) => i.SavePartnerViewLog(partnerViewLog));
}
As can be figured out that PartnerViewLog is a class. Now I want to make the function SaveRecord as generic where the class name can be anything?
How to do so?

Try the following:
private int SaveRecord<T>(T record, PortalConstant.DataSourceType dataSourceType, Func<IDataAccess, T, int> chooseSelector)
{
int results = -1;
var dataPlugin = DataPlugins.FirstOrDefault(i => i.Metadata["SQLMetaData"].ToString() == dataSourceType.EnumToString());
if (dataPlugin != null)
{
results = chooseSelector(dataPlugin.Value, record);
}
return results;
}

private int SaveRecord<T>(T partnerViewLog, PortalConstant.DataSourceType dataSourceType, Func<IDataAccess, T, int> chooseSelector)
{
...
}
Every thing else remains the same

Related

C# Pass entity property to a function to use in LINQ

Imagine I have a class like this:
public class BaseRepo<T> where T : class
{
private readonly DbSet<T> table;
public BaseRepo(MyDbContext context)
{
this.table = context.Set<T>();
}
}
I want to implement the following logic in to this class.
string GenerateUniqueStringFor(PropertyName)
{
string val = string.Empty;
do
{
val = GenerateRandomString();
}
while (this.table.FirstOrDefault(x => x.PropertyName == val) is not null);
return val;
}
The problem is I don't know how to pass Property/PropertyName. The ideal way for calling it would be:
string val = _myRepo.GenerateUniqueStringFor(x => x.PropertyName);
If I understand correctly, you can try to pass a delegate Func
string GenerateUniqueStringFor(Func<T,string> func)
{
string val = string.Empty;
do
{
val = GenerateRandomString();
}
while (this.table.FirstOrDefault(x => func(x) == val) is not null);
return val;
}

Using reflection on generics recursively

I have the following classes:
public class BaseDataEntity
{
private List<string> _Changes = new List<string>();
public IEnumerable<string> GetChanges()
{
return _Changes;
}
public bool HasDataChanged
{
get { return (GetChanges().Count() > 0); }
}
public bool HasChildRecords
{
get { return (GetType().GetChildRecords().Count() > 0); }
}
}
public class ChildRecords : IList<T> where T : BaseDataEntity
{
}
And a few helper methods:
public static PropertyInfo[] GetChildRecords(this Type aType)
{
return aType.GetProperties().Where(pi => pi.IsChildRecords()).ToArray();
}
public static bool IsChildRecords(this PropertyInfo info)
{
return (info.GetCustomAttributes(typeof(ChildRecordsAttribute), false).Length > 0);
}
What I'm trying to do is implement a property called HaveChildRecordsChanged using reflection. My question is how would I go about using reflection to check the HasDataChanged property of all ChildRecords of arbitrary depth?
I tried something like:
var isChanged = false;
foreach (var info in GetType().GetChildRecords())
{
var childRecordObject = info.GetValue(this, null);
var childRecords = childRecordObject as ChildRecords<BaseDataEntity>; //cannot unbox this, it evaluates as null
if (null != childRecords && childRecords.Any(x => x.HasDataChanged))
{
isChanged = true; //never hit
}
}
return isChanged;
ChildRecords<T> is generic so ChildRecords<Company> can't be cast to ChildRecords<BaseDataEntity>.
Since you already filter the property marked with the ChildRecordsAttribute the simplest solution would be to cast to IEnumerable and use OfType<BaseDataEntity>()
var childRecords = childRecordObject as IEnumerable; // IList<T> will be IEnumerable
if (null != childRecords && childRecords.OfType<BaseDataEntity>().Any(x => x.HasDataChanged))
{
isChanged = true;
}

Enum and IEnumerable in C#

my TIME Enum contains Annual, Monthly, weekly, daily and Hourly.
Here I want to decide which is the minimum and want to return that.
How can I do this ? Here is the code I tried.
private Time DecideMinTime(IEnumerable<Time> g)
{
var minTime = Time.Hourly;
foreach (var element in g)
{
minTime = element;
}
return minTime;
}
Assuming that the numeric value of the enum elements decides what the minimum is:
private Time DecideMinTime(IEnumerable<Time> g)
{
if (g == null) { throw new ArgumentNullException("g"); }
return (Time)g.Cast<int>().Min();
}
If the numeric values indicate the opposite order then you would use .Max() instead of .Min().
As indicated, the numeric order is not consistent. This can be worked around simply by using a mapping indicating the correct order:
static class TimeOrdering
{
private static readonly Dictionary<Time, int> timeOrderingMap;
static TimeOrdering()
{
timeOrderingMap = new Dictionary<Time, int>();
timeOrderingMap[Time.Hourly] = 1;
timeOrderingMap[Time.Daily] = 2;
timeOrderingMap[Time.Weekly] = 3;
timeOrderingMap[Time.Monthly] = 4;
timeOrderingMap[Time.Annual] = 5;
}
public Time DecideMinTime(IEnumerable<Time> g)
{
if (g == null) { throw new ArgumentNullException("g"); }
return g.MinBy(i => timeOrderingMap[i]);
}
public TSource MinBy<TSource, int>(
this IEnumerable<TSource> self,
Func<TSource, int> ordering)
{
if (self == null) { throw new ArgumentNullException("self"); }
if (ordering == null) { throw new ArgumentNullException("ordering"); }
using (var e = self.GetEnumerator()) {
if (!e.MoveNext()) {
throw new ArgumentException("Sequence is empty.", "self");
}
var minElement = e.Current;
var minOrder = ordering(minElement);
while (e.MoveNext()) {
var curOrder = ordering(e.Current);
if (curOrder < minOrder) {
minOrder = curOrder;
minElement = e.Current;
}
}
return minElement;
}
}
}
To make it easier you can assign int values to your enum:
enum Time : byte {Hourly=1, Daily=2, Weekly=3, Monthly=4, Annual=5};
and then
private static Time DecideMinTime(IEnumerable<Time> g)
{
return g.Min();
}
That way you avoid casting back and forth.

Simplifying Locating an element in a list, perhaps using LINQ

I have the following code:
class TestClass
{
public string StringValue {
get; set;
}
public int IntValue {
get; set;
}
}
class MainClass
{
private readonly List<TestClass> MyList;
public MainClass()
{
MyList = new List<TestClass>();
}
public void RemoveTestClass(string strValue)
{
int ndx = 0;
while (ndx < MyList.Count)
{
if (MyList[ndx].StringValue.Equals(strValue))
break;
ndx++;
}
MyList.RemoveAt(ndx);
}
public void RemoveTestClass(int intValue)
{
int ndx = 0;
while (ndx < MyList.Count)
{
if (MyList[ndx].IntValue == intValue)
break;
ndx++;
}
MyList.RemoveAt(ndx);
}
}
What I would like to know is if there is a simpler way, perhaps using LINQ, to replace the while loops in the 2 RemoveTestClass functions, rather then iterating through each element, like I'm doing?
You can use List<T>.FindIndex:
myList.RemoveAt(MyList.FindIndex(x => x.StringValue == strValue));
You may also want to handle the case where the element is not found:
int i = myList.FindIndex(x => x.StringValue == strValue);
if (i != -1)
{
myList.RemoveAt(i);
}
Simplest possible way I can think is finding first item, which matches the criteria and then use List.Remove to do it:
myList.Remove(myList.FirstorDefault(x=>x.StringValue == stringValue))
because Remove doesn't throw an exception when it can't find the item, above works fine. except you permited to have null values in list, which will be deleted, and I think it's not so good to have them in list.
I would do it in that way:
public void RemoveTestClass(string strValue)
{
MyList.RemoveAll(item => item.StringValue.Equals(strValue));
}
and:
public void RemoveTestClass(int intValue)
{
MyList.RemoveAll(item => item.IntValue == intValue);
}
Update:
If you only want to remove the first occurrance:
public void RemoveTestClass(int intValue)
{
var itemToRemove = MyList.FirstOrDefault(item => item.InValue == intValue);
if (itemToRemove != null)
{
MyList.Remove(itemToRemove);
}
}

Using TryParse for Setting Object Property Values

I'm currently refactoring code to replace Convert.To's to TryParse.
I've come across the following bit of code which is creating and assigning a property to an object.
List<Person> list = new List<Person>();
foreach (DataRow row in dt.Rows)
{
var p = new Person{ RecordID = Convert.ToInt32(row["ContactID"]) };
list.Add(p);
}
What I've come up with as a replacement is:
var p = new Person { RecordID = Int32.TryParse(row["ContactID"].ToString(), out RecordID) ? RecordID : RecordID };
Any thoughts, opinions, alternatives to what I've done?
Write an extension method.
public static Int32? ParseInt32(this string str) {
Int32 k;
if(Int32.TryParse(str, out k))
return k;
return null;
}
I'd use an alternative implementation TryParse which returns an int?:
public static int? TryParseInt32(string x)
{
int value;
return int.TryParse(x, out value) ? value : (int?) null;
}
Then you can write:
var p = new Person { RecordID = Helpers.TryParseInt32(row["ContactID"].ToString()) ?? 0 };
(Or use a different default value, if you want - either way it'll be visible in your code.)
I suggest separate the TryParse part from initializer. It will be more readable.
int recordId;
Int32.TryParse(row["ContactID"].ToString(), out recordID)
foreach (DataRow row in dt.Rows)
{
var p = new Person{ RecordID = recordId };
list.Add(p);
}
private static void TryToDecimal(string str, Action<decimal> action)
{
if (decimal.TryParse(str, out decimal ret))
{
action(ret);
}
else
{
//do something you want
}
}
TryToDecimal(strList[5], (x) => { st.LastTradePrice = x; });
TryToDecimal(strList[3], (x) => { st.LastClosedPrice = x; });
TryToDecimal(strList[6], (x) => { st.TopPrice = x; });
TryToDecimal(strList[7], (x) => { st.BottomPrice = x; });
TryToDecimal(strList[10], (x) => { st.PriceChange = x; });

Categories

Resources