EF cannot update column when success savechanges - c#

I can not update my database using EF (UpdateSpecified() method is not working). My Add() method works fine.
Edit: I observed the SaveChanges Method it always return 1 ,it let me confused because I updated 3 tables.
I discovered the RepositoryFactory's IQueryable code, it had changed:
My code:
public class GoodsModel
{
private IRepositoryFactory _repositoryFactory;
private IServiceFactory _serviceFactory;
private IGoodsService _goodsService;
private IGoodsTypeService _goodsTypeService;
private IUsersService _userService;
private IOrderService _orderService;
private IOrdersRepository orderRepo;
private IUsersRepository userRepo;
private IGoodsRepository goodsRepo;
public GoodsModel()
{
_repositoryFactory = new RepositoryFactory();
_repositoryFactory.OpenSession();
_serviceFactory = new ServiceFactory(_repositoryFactory);
_goodsService = _serviceFactory.CreateGoodsService();
_goodsTypeService = _serviceFactory.CreateGoodsTypeService();
_userService = _serviceFactory.CreateUsersService();
_orderService = _serviceFactory.CreateOrderService();
userRepo = _repositoryFactory.CreateUsersRepository();
orderRepo = _repositoryFactory.CreateOrdersRepository();
goodsRepo = _repositoryFactory.CreateGoodsRepository();
orderRepo = _repositoryFactory.CreateOrdersRepository();
}
public bool BuyProduct(BuyProductDto model)
{
string name = HttpContext.Current.User.Identity.Name;
// _repositoryFactory.OpenSession();
using (_repositoryFactory)
{
var user = _userService.Filter(x => x.UserName == name).FirstOrDefault();
var good = _goodsService.Filter(x => x.GoodNumber == model.GoodNumber).FirstOrDefault();
//var userRepo = _repositoryFactory.CreateUsersRepository();
//var goodRepo = _repositoryFactory.CreateGoodsRepository();
Users u = new Users();
Goods g = new Goods();
try
{
//substract when buy product.
int remainScore = user.UserScore - (int)good.GoodScore;
if (remainScore < 0)
{
return false;
}
u.UserId = user.UserId;
u.UserScore = remainScore;
userRepo.Attach(u);
userRepo.UpdateSpecified(u);
g.Id = good.Id;
//same as above syntax
g.GoodsQuantity = good.GoodsQuantity - 1;
goodsRepo.Attach(g);
goodsRepo.UpdateSpecified(g);
//orderRepo = _repositoryFactory.CreateOrdersRepository();
orderRepo.Add(new Orders
{
GoodId = good.Id,
InsertTime = DateTime.Now,
Status = 0,
UserId = user.UserId,
OrderNo = DateTime.Now.ToString("yyyyMMddss"),
UpdateTime = DateTime.Now
});
_repositoryFactory.Commit();
}
catch (Exception ex)
{
_repositoryFactory.RollBack();
return false;
}
}
return true;
}
}
The code of framework is here:
public void UpdateSpecified(T entity)
{
var type = entity.GetType();
if (type.IsPrimitive || type == typeof(string))
{
var props = type.GetProperties();
foreach (var prop in props)
{
string propValue = prop.GetValue(entity, null) != null ? prop.GetValue(entity, null).ToString() : string.Empty;
if (!string.IsNullOrEmpty(propValue))
{
DataContext.Entry<T>(entity).Property(prop.Name).IsModified = true;
}
else
{
DataContext.Entry<T>(entity).Property(prop.Name).IsModified = false;
}
}
}
}
public void Attach(T entity)
{
DataContext.Set<T>().Attach(entity);
}
}
The entity code like this, it contains the navigation attribute:
public class Users
{
public int UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public DateTime InsertTime { get; set; }
public ICollection<UsersUserGroup> UsersUserGroups { get; set; }
public int UserScore { get; set; }
}

oaky.... I have solved it ,it is fool question ,I admit; Correct Code:
public void UpdateSpecified(T entity)
{
var props = entity.GetType().GetProperties();
foreach (var prop in props)
{
if (prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(string))
{
string propValue = prop.GetValue(entity, null) != null ? prop.GetValue(entity, null).ToString() : string.Empty;
if (!string.IsNullOrEmpty(propValue))
{
DataContext.Entry<T>(entity).Property(prop.Name).IsModified = true;
}
else
{
DataContext.Entry<T>(entity).Property(prop.Name).IsModified = false;
}
}
}
}

Related

Assigning redis values/redis hash entries to Type T returns null value

I am getting NULL properties on the below code, while returning the hashed value in cacheData
Line where I get Null values for all properties:
var val = RedisUtils.ToHashEntries(db.HashGet(hashkey + ":" + clsobjname, id));
cacheData = RedisUtils.ConvertFromRedis(e);
Please help me to assign the hashedValues(HashGet/HashGetAll values) to type T CacheData
Code:
public class class1
{
public class SampleModel
{
public SampleModel()
{
EmployeeDetails = new List<employees>();
CountryDetails = new List<country>();
StateDetails = new List<State>();
}
public List<employees> EmployeeDetails { get; set; }
public List<country> CountryDetails { get; set; }
public List<State> StateDetails { get; set; }
}
[Serializable]
public class employees
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
[Serializable]
public class country
{
public int Id { get; set; }
public string countryName { get; set; }
}
[Serializable]
public class State
{
public int Id { get; set; }
public int Name { get; set; }
}
private readonly IConnectionMultiplexer _connectionMultiplexer;
private readonly IConfiguration _Configuration;
private int cacheTypeAll;
private int cacheTypeMaster;
public class1(IConnectionMultiplexer connectionMultiplexer, IConfiguration configuration)
{
_connectionMultiplexer = connectionMultiplexer;
_Configuration = configuration;
cacheTypeAll = _Configuration.GetSection("AppSettings").GetValue<int>("CacheInstance");
cacheTypeMaster = _Configuration.GetSection("AppSettings").GetValue<int>("CacheInstanceMaster");
}
public async Task<T> GetRedisHash<T>(string hashkey, string id, SampleModel obj) where T : class
{
var db = _connectionMultiplexer.GetDatabase(cacheTypeAll);
T cacheData = null;
foreach (var a in obj.GetType().GetProperties())
{
var clsobjname = a.Name;
var HashGetAll = db.HashGetAll(hashkey + ":" + clsobjname);
var hashget = db.HashGet(hashkey + ":" + clsobjname, id);
if (HashGetAll.ToHashSet().Count > 0)
{
if (clsobjname != null)
{
if (id != "")
{
var e = RedisUtils.ToHashEntries(db.HashGet(hashkey + ":" + clsobjname, id));
cacheData = RedisUtils.ConvertFromRedis<T>(e);
}
else
{
var d = RedisUtils.ToHashEntries(db.HashGetAll(hashkey + ":" + clsobjname));
cacheData = RedisUtils.ConvertFromRedis<T>(d);
}
}
}
else
{
//cacheData = await computeData();
// string serializedData = JsonConvert.SerializeObject(cacheData);
string serializedData = "{'EmployeeDetails':[{'Id':1,'FirstName':'Anderson','LastName':'Felipe'},{'Id':2,'FirstName':'Anita','LastName':'Kay'},{'Id':3,'FirstName':'Karte','LastName':'Hare'}],'CountryDetails':[{'Id':1,'Name':'UK'}],'StateDetails':[{'Id':1,'Name':'UK'}]}";//I have set it static
dynamic parseJson = JsonConvert.DeserializeObject(serializedData);
foreach (var h in parseJson)
{
var c = ((Newtonsoft.Json.Linq.JProperty)((Newtonsoft.Json.Linq.JToken)h)).Value;
var objname = ((Newtonsoft.Json.Linq.JProperty)h).Name;
int i = 0;
foreach (var b in c)
{
var Id = c[i]["Id"];
db.HashSet(hashkey + ":" + objname, new HashEntry[] {
new HashEntry(Id.ToString(),JsonConvert.SerializeObject(c[i]))});
i++;
}
}
}
clsobjname = "";
}
return cacheData;
}
}
public static class RedisUtils
{
// Serialize in Redis format:
public static HashEntry[] ToHashEntries(this object obj)
{
PropertyInfo[] properties = obj.GetType().GetProperties();
return properties.Select(property => new HashEntry(property.Name, property.GetValue(obj).ToString())).ToArray();
}
//Deserialize from Redis format
public static T ConvertFromRedis<T>(this HashEntry[] hashEntries)
{
PropertyInfo[] properties = typeof(T).GetProperties();
var obj = Activator.CreateInstance(typeof(T));
foreach (var property in properties)
{
HashEntry entry = hashEntries.FirstOrDefault(g => g.Name.ToString().Equals(property.Name));
if (entry.Equals(new HashEntry())) continue;
property.SetValue(obj, Convert.ChangeType(entry.Value.ToString(), property.PropertyType));
}
return (T)obj;
}
}

Generic Property Setter for Generic Type in C# Class

I am trying to Generically set the Field Values in the ItemsRow class (and others that are similar in format - but properties and fields are different.
I want to do something like what I do with hard coding
public Dictionary<object, Action<T, object>> SetFieldValues = new Dictionary<object, Action<T, object>>
{
{"Description", (m,v) => m.Description = (string)v};
}
public class Mapping<T> where T:Row
{
public Dictionary<object, Action<T, object>> SetFieldValues(string[] headers)
{
Dictionary<object, Action<T, object>> myDict = new Dictionary<object, Action<T, object>>();
// Activator
var objectType = typeof(T); // Type.GetType(T);
var tRow = Activator.CreateInstance(objectType) as Row;
foreach (var item in headers)
{
var myfield = tRow.FindFieldByPropertyName(item);
//myDict[item] = myfield =
//myDict[item] = (m, v) => m.FindFieldByPropertyName(item) = (Type.GetType(myfield.Type.ToString()))v;
}
// I want this to be dynamic header[i],(m,v) => m.(property to set for object) = (cast to type m.property type)v
//{"ItemName", (m,v) => m.ItemName = (string)v},
//{"Description", (m,v) => m.Description = (string)v},
return myDict;
}
// Class names will be different, properties and fields will be different..
public sealed class ItemsRow : Row
{
public String ItemName
{
get { return Fields.ItemName[this]; }
set { Fields.ItemName[this] = value; }
}
public String Description
{
get { return Fields.Description[this]; }
set { Fields.Description[this] = value; }
}
public static readonly RowFields Fields = new RowFields().Init();
public ItemsRow()
: base(Fields)
{
}
public class RowFields : RowFieldsBase
{
public StringField ItemName;
public StringField Description;
}
}
Base Class
=================================================================================
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
namespace Serenity.Data
{
[JsonConverter(typeof(JsonRowConverter))]
public abstract partial class Row : IEntityWithJoins,
INotifyPropertyChanged, IEditableObject
#if !COREFX
, IDataErrorInfo
#endif
{
internal RowFieldsBase fields;
internal bool[] assignedFields;
internal Hashtable dictionaryData;
internal bool ignoreConstraints;
internal object[] indexedData;
internal bool tracking;
internal bool trackWithChecks;
protected Row(RowFieldsBase fields)
{
if (fields == null)
throw new ArgumentNullException("fields");
this.fields = fields.InitInstance(this);
TrackAssignments = true;
}
public void CloneInto(Row clone,
bool cloneHandlers)
{
clone.ignoreConstraints = ignoreConstraints;
foreach (var field in GetFields())
field.Copy(this, clone);
clone.tracking = tracking;
if (tracking)
{
if (assignedFields != null)
{
clone.assignedFields = new bool[assignedFields.Length];
Array.Copy(assignedFields, clone.assignedFields, assignedFields.Length);
}
}
else
clone.assignedFields = null;
clone.trackWithChecks = trackWithChecks;
clone.originalValues = originalValues;
if (dictionaryData != null)
clone.dictionaryData = (Hashtable)this.dictionaryData.Clone();
else
clone.dictionaryData = null;
if (indexedData != null)
{
clone.indexedData = new object[indexedData.Length];
for (var i = 0; i < indexedData.Length; i++)
clone.indexedData[i] = indexedData[i];
}
else
clone.indexedData = null;
if (previousValues != null)
clone.previousValues = previousValues.CloneRow();
else
clone.previousValues = null;
if (cloneHandlers)
{
clone.postHandler = this.postHandler;
clone.propertyChanged = this.propertyChanged;
if (this.validationErrors != null)
clone.validationErrors = new Dictionary<string, string>(this.validationErrors);
else
clone.validationErrors = null;
}
}
public Row CloneRow()
{
var clone = CreateNew();
CloneInto(clone, true);
return clone;
}
public virtual Row CreateNew()
{
if (fields.rowFactory == null)
throw new NotImplementedException();
return fields.rowFactory();
}
internal void FieldAssignedValue(Field field)
{
if (assignedFields == null)
assignedFields = new bool[fields.Count];
assignedFields[field.index] = true;
if (validationErrors != null)
RemoveValidationError(field.PropertyName ?? field.Name);
if (propertyChanged != null)
{
if (field.IndexCompare(previousValues, this) != 0)
{
RaisePropertyChanged(field);
field.Copy(this, previousValues);
}
}
}
public Field FindField(string fieldName)
{
return fields.FindField(fieldName);
}
public Field FindFieldByPropertyName(string propertyName)
{
return fields.FindFieldByPropertyName(propertyName);
}
public RowFieldsBase GetFields()
{
return fields;
}
public int FieldCount
{
get { return fields.Count; }
}
public bool IsAnyFieldAssigned
{
get
{
return tracking && assignedFields != null;
}
}
public bool IgnoreConstraints
{
get { return ignoreConstraints; }
set { ignoreConstraints = value; }
}
public string Table
{
get { return fields.TableName; }
}
public bool TrackAssignments
{
get
{
return tracking;
}
set
{
if (tracking != value)
{
if (value)
{
if (propertyChanged != null)
previousValues = this.CloneRow();
tracking = value;
}
else
{
tracking = false;
trackWithChecks = false;
assignedFields = null;
}
}
}
}
public bool TrackWithChecks
{
get
{
return tracking && trackWithChecks;
}
set
{
if (value != TrackWithChecks)
{
if (value && !tracking)
TrackAssignments = true;
trackWithChecks = value;
}
}
}
private Field FindFieldEnsure(string fieldName)
{
var field = FindField(fieldName);
if (ReferenceEquals(null, field))
throw new ArgumentOutOfRangeException("fieldName", String.Format(
"{0} has no field with name '{1}'.", this.GetType().Name, fieldName));
return field;
}
public object this[string fieldName]
{
get
{
var field = FindFieldByPropertyName(fieldName) ??
FindField(fieldName);
if (ReferenceEquals(null, field))
{
if (dictionaryData != null)
return dictionaryData[fieldName];
return null;
}
return field.AsObject(this);
}
set
{
(FindFieldByPropertyName(fieldName) ??
FindFieldEnsure(fieldName)).AsObject(this, value);
}
}
public void SetDictionaryData(object key, object value)
{
if (value == null)
{
if (dictionaryData == null)
return;
dictionaryData[key] = null;
}
else
{
if (dictionaryData == null)
dictionaryData = new Hashtable();
dictionaryData[key] = value;
}
}
public object GetDictionaryData(object key)
{
if (dictionaryData != null)
return dictionaryData[key];
return null;
}
internal void SetIndexedData(int index, object value)
{
if (value == null)
{
if (indexedData == null)
return;
indexedData[index] = null;
}
else
{
if (indexedData == null)
indexedData = new object[this.FieldCount];
indexedData[index] = value;
}
}
internal object GetIndexedData(int index)
{
if (indexedData != null)
return indexedData[index];
return null;
}
public bool IsAssigned(Field field)
{
if (assignedFields == null)
return false;
return assignedFields[field.index];
}
public void ClearAssignment(Field field)
{
if (assignedFields == null)
return;
assignedFields[field.index] = false;
for (var i = 0; i < assignedFields.Length; i++)
if (assignedFields[i])
return;
assignedFields = null;
}
public bool IsAnyFieldChanged
{
get
{
if (originalValues == null)
return false;
for (var i = 0; i < fields.Count; i++)
if (fields[i].IndexCompare(originalValues, this) != 0)
return true;
return false;
}
}
IDictionary<string, Join> IHaveJoins.Joins
{
get { return fields.Joins; }
}
}
}
Sorry I couldn't get back to you yesterday, but a promise is a promise :). And while writing this code I began to doubt about what your headers actually look like. How are you going to determine which property belongs to which header? Unless you have some type of recognition for that which I might be missing from your code.
But the general idea is this:
Of course this is based on the premise that headers are keyvaluepairs of which the key is the identifier for which property to use.
static void Main(string[] args)
{
var values = new Dictionary<string, string>()
{
["Title"] = "Test",
["Amount"] = "5",
["Description"] = "Some description"
};
var target = new TestClass();
var setters = GetPropertySetters(target);
foreach(KeyValuePair<string, string> value in values)
{
if (setters.ContainsKey(value.Key))
setters[value.Key].Invoke(value.Value);
}
Console.WriteLine(JsonConvert.SerializeObject(target));
Console.ReadLine();
}
public static Dictionary<string, Action<string>> GetPropertySetters<T>(T source)
{
var result = new Dictionary<string, Action<string>>(StringComparer.OrdinalIgnoreCase);
foreach (PropertyInfo pi in typeof(T).GetProperties())
result.Add(pi.Name, (string value) => { pi.SetValue(source, Convert.ChangeType(value, pi.PropertyType)); });
return result;
}
public class TestClass
{
public string Title { get; set; }
public int Amount { get; set; }
public string Description { get; set; }
}

ValueInjecter How do I update code from V2.4 to V3.2, no Convention Injection

I'm working on the upgrade of an application from net framework to .net core 3.0. I am using windows presentation foundation Wpf and I want to implement ValueInjecter in its last version 3.2... The application is currently running on version 2.4 of ValueInjecter help please!. There is not much documentation on the web.
How would this code look?
My CloneInjection.cs:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Omu.ValueInjecter;
namespace Infrastructure.Data.Injection
{
public class CloneInjection : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Value != null;
}
protected override object SetValue(ConventionInfo c)
{
//for value types and string just return the value as is
if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string))
return c.SourceProp.Value;
//handle arrays
if (c.SourceProp.Type.IsArray)
{
var arr = c.SourceProp.Value as Array;
var clone = arr.Clone() as Array;
for (int index = 0; index < arr.Length; index++)
{
var a = arr.GetValue(index);
if (a.GetType().IsValueType || a is string) continue;
clone.SetValue(Activator.CreateInstance(a.GetType()).InjectFrom<CloneInjection>(a), index);
}
return clone;
}
if (c.SourceProp.Type.IsGenericType)
{
//handle IEnumerable<> also ICollection<> IList<> List<>
if (c.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
{
var t = c.SourceProp.Type.GetGenericArguments()[0];
if (t.IsValueType || t == typeof(string)) return c.SourceProp.Value;
var tlist = typeof(List<>).MakeGenericType(t);
var list = Activator.CreateInstance(tlist);
var addMethod = tlist.GetMethod("Add");
foreach (var o in c.SourceProp.Value as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(o);
addMethod.Invoke(list, new[] { e }); // in 4.0 you can use dynamic and just do list.Add(e);
}
return list;
}
//unhandled generic type, you could also return null or throw
return c.SourceProp.Value;
}
//for simple object types create a new instace and apply the clone injection on it
return Activator.CreateInstance(c.SourceProp.Type)
.InjectFrom<CloneInjection>(c.SourceProp.Value);
}
}
}
My EntityInjection.cs:
using System.Collections;
using System.Diagnostics;
using System.Linq;
using Omu.ValueInjecter;
namespace Infrastructure.Data.Injection
{
public class EntityInjection : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
var propertyMatch = c.SourceProp.Name == c.TargetProp.Name;
var sourceNotNull = c.SourceProp.Value != null;
bool targetPropertyIdWritable = !(propertyMatch && c.TargetProp.Name == "Id" && !(c.Target.Value is IEntityClass));
return propertyMatch && sourceNotNull && targetPropertyIdWritable;
}
protected override object SetValue(ConventionInfo c)
{
if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string))
return c.SourceProp.Value;
if (c.SourceProp.Type.IsGenericType)
{
var td = c.SourceProp.Type.GetGenericTypeDefinition();
if (td != null && td.GetInterfaces().Contains(typeof(IEnumerable)))
{
var targetChildType = c.TargetProp.Type.GetGenericArguments()[0];
if (targetChildType.IsValueType || targetChildType == typeof(string)) return c.SourceProp.Value;
if (targetChildType.GetInterfaces().Any(x => x == typeof(IValueClass)))
{
var deleteMethod = c.TargetProp.Value.GetType().GetMethod("Remove");
var rmvItems = (c.TargetProp.Value as IEnumerable).Cast<IValueClass>()
.Where(x => x.Id > 0 && !(c.SourceProp.Value as IEnumerable).Cast<IValueClass>().Any(y => y.Id == x.Id));
rmvItems.ToList().ForEach(x => deleteMethod.Invoke(c.TargetProp.Value, new[] { x }));
rmvItems = (c.TargetProp.Value as IEnumerable).Cast<IValueClass>()
.Where(x => !(c.SourceProp.Value as IEnumerable).Cast<IValueClass>().Contains(x));
rmvItems.ToList().ForEach(x => deleteMethod.Invoke(c.TargetProp.Value, new[] { x }));
var sourceCollection = (c.SourceProp.Value as IEnumerable).Cast<IValueClass>();
foreach (var s in sourceCollection)
{
var sv = s;
var target = (c.TargetProp.Value as IEnumerable).Cast<IValueClass>().SingleOrDefault(z => z.Id == sv.Id && z.Id != 0);
if (target != null) target.InjectFrom<EntityInjection>(sv);
else if (!(c.TargetProp.Value as IEnumerable).Cast<IValueClass>().Contains(sv))
{
if (!(sv is IEntityClass))
{
sv = Activator.CreateInstance(targetChildType) as IValueClass;
Debug.Assert(sv != null);
sv.InjectFrom<EntityInjection>(s);
sv.Id = 0;
}
var addMethod = c.TargetProp.Value.GetType().GetMethod("Add");
addMethod.Invoke(c.TargetProp.Value, new[] { sv });
}
}
}
}
return c.TargetProp.Value;
}
if (c.SourceProp.Value is IEntityClass)
{
return c.SourceProp.Value;
}
if (c.TargetProp.Value == null)
{
try
{
c.TargetProp.Value = Activator.CreateInstance(c.TargetProp.Type);
}
catch (Exception)
{
return null;
}
}
return c.TargetProp.Value.InjectFrom<EntityInjection>(c.SourceProp.Value);
}
}
}
if you can't switch to LoopInjection you could also try to add this class into your project:
public abstract class ConventionInjection : ValueInjection
{
protected abstract bool Match(ConventionInfo c);
protected virtual object SetValue(ConventionInfo c)
{
return c.SourceProp.Value;
}
protected override void Inject(object source, object target)
{
var sourceProps = source.GetProps();
var targetProps = target.GetProps();
var ci = new ConventionInfo
{
Source =
{
Type = source.GetType(),
Value = source
},
Target =
{
Type = target.GetType(),
Value = target
}
};
for (var i = 0; i < sourceProps.Count; i++)
{
var s = sourceProps[i];
ci.SourceProp.Name = s.Name;
ci.SourceProp.Value = s.GetValue(source);
ci.SourceProp.Type = s.PropertyType;
for(var j=0; j < targetProps.Count; j++)
{
var t = targetProps[j];
ci.TargetProp.Name = t.Name;
ci.TargetProp.Value = t.GetValue(target);
ci.TargetProp.Type = t.PropertyType;
if (Match(ci))
t.SetValue(target, SetValue(ci));
}
}
}
}
public class ConventionInfo
{
public ConventionInfo()
{
Source = new TypeInfo();
Target = new TypeInfo();
SourceProp = new PropInfo();
TargetProp = new PropInfo();
}
public TypeInfo Source { get; set; }
public TypeInfo Target { get; set; }
public PropInfo SourceProp { get; set; }
public PropInfo TargetProp { get; set; }
public class PropInfo
{
public string Name { get; set; }
public Type Type { get; set; }
public object Value { get; set; }
}
public class TypeInfo
{
public Type Type { get; set; }
public object Value { get; set; }
}
}

loop over fields of a C# object

In a project there is a reporting class as follows:
public class ReportRowDataContract
{
public ReportDataDataContract ReportData1 { get; set; }
public ReportDataDataContract ReportData2 { get; set; }
public ReportDataDataContract ReportData3 { get; set; }
public ReportDataDataContract ReportData4 { get; set; }
public ReportDataDataContract ReportData5 { get; set; }
public ReportDataDataContract ReportData6 { get; set; }
}
Then there is a method that works with objects from the above class. Here is the first part of this method:
public ReportGrid(List<ReportRowDataContract> items , List<ReportDataDataContract> summaryData)
: base(items)
{
passedInSummaryData = summaryData;
if (items[0].ReportData1 != null)
{
if (items[0].ReportData1.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(items[0].ReportData1.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData1.DisplayFormat)) ? Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString("N") : Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (items[0].ReportData1.SumValue || items[0].ReportData1.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData1.Name,
AvgValue = items[0].ReportData1.AvgValue,
DecimalValue = 0
});
}
}
else if (items[0].ReportData1.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(items[0].ReportData1.Name);
if (items[0].ReportData1.SumValue || items[0].ReportData1.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData1.Name,
AvgValue = items[0].ReportData1.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(items[0].ReportData1.Name);
}
}
if (items[0].ReportData2 != null)
{
if (items[0].ReportData2.DecimalValue != null)
{
Columns.Add(m => m.ReportData2.DecimalValue).Titled(items[0].ReportData2.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData2.DisplayFormat)) ? Convert.ToDecimal(#m.ReportData2.DecimalValue).ToString("N") : Convert.ToDecimal(#m.ReportData2.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (items[0].ReportData2.SumValue || items[0].ReportData2.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData2.Name,
AvgValue = items[0].ReportData2.AvgValue,
DecimalValue = 0
});
}
}
else if (items[0].ReportData2.IntValue != null)
{
Columns.Add(m => m.ReportData2.IntValue).Titled(items[0].ReportData2.Name);
if (items[0].ReportData2.SumValue || items[0].ReportData2.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData2.Name,
AvgValue = items[0].ReportData2.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData2.StringValue).Titled(items[0].ReportData2.Name);
}
}
This method consists of code that repeats itself out to ReportData6, changing only the ReportData field name with each repetition. Is there a way that this method can be rewritten to process each ReportData field by looping somehow? Besides making for a shorter method, this would be extremely useful to have in order to avoid manually updating the method if additional ReportData fields need to be added to the ReportRowDataContract class in the future.
Edit #1: I am fairly new to C# so detailed answers of how to go about this would be immensely helpful.
Edit #2: Thanks to Zohar Peled's post below, the following code feels very close. However, m.ReportData1 is causing problems in the AddGridColumn() method. The error message is 'ReportRowDataContract' does not contain a definition for 'item'...
I tried passing in ReportData 1 as a second argument when AddGridColumn() is called, but to no avail. Is there a way to modify the code so it works?
code that calls method:
// create columns for grid
AddGridColumn(items[0].ReportData1);
AddGridColumn(items[0].ReportData2);
AddGridColumn(items[0].ReportData3);
AddGridColumn(items[0].ReportData4);
AddGridColumn(items[0].ReportData5);
AddGridColumn(items[0].ReportData6);
method:
private void AddGridColumn(ReportDataDataContract item)
{
if (item != null)
{
if (item.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(item.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData1.DisplayFormat)) ?
Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString("N") :
Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (item.SumValue || item.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = item.Name,
AvgValue = item.AvgValue,
DecimalValue = 0
});
}
}
else if (item.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(item.Name);
if (item.SumValue || item.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = item.Name,
AvgValue = item.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(item.Name);
}
}
}
Edit #3: This is the ReportDataDataContract class definiton:
public class ReportDataDataContract
{
public string Name { get; set; }
public string StringValue { get; set; }
public decimal? DecimalValue { get; set; }
public int? IntValue { get; set; }
public bool SumValue { get; set; }
public bool AvgValue { get; set; }
public int? Index { get; set; }
public string DisplayFormat { get; set; }
}
Can you add an indexed property like this to ReportRowDataContract in order to essentially access the ReportData values as an indexed property?
public ReportDataDataContract this[int i]
{
get
{
return new ReportDataDataContract[] { ReportData1, ReportData2, ReportData3, ReportData4, ReportData5, ReportData6 }[i];
}
}
The you can use items[0][i].SumValue, for example, instead of items[0].ReportData1.SumValue.
If you can, you should simply change the ReportRowDataContract to only hold a single property that is an array of ReportDataDataContract. Then you can use a simple loop for each of the data contracts.
If you can't change the ReportRowDataContract table, you could use a method that takes in an argument of type ReportDataDataContract and have all the repeated code there - and just call it one time for each property.
The method should look something like that (based on the code you've posted):
void DoSomething(ReportDataDataContract dataContranct)
{
if (dataContranct != null)
{
if (dataContranct.DecimalValue != null)
{
Columns.Add(m => dataContranct.DecimalValue).Titled(dataContranct.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#dataContranct.DisplayFormat)) ?
Convert.ToDecimal(#dataContranct.DecimalValue).ToString("N") :
Convert.ToDecimal(#dataContranct.DecimalValue).ToString(#dataContranct.DisplayFormat));
if (dataContranct.SumValue || dataContranct.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = dataContranct.Name,
AvgValue = dataContranct.AvgValue,
DecimalValue = 0
});
}
}
else if (dataContranct.IntValue != null)
{
Columns.Add(m => dataContranct.IntValue).Titled(dataContranct.Name);
if (dataContranct.SumValue || dataContranct.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = dataContranct.Name,
AvgValue = dataContranct.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => mdataContranct.StringValue).Titled(dataContranct.Name);
}
}
}

How to mock protected members with multiple interfaces

How to mock this class in nUnit Tests?
public class OpenDataQuery: PagedQuery, IOpenDataQuery
{
private static Dictionary<string, SortItem> m_sortModes;
protected override Dictionary<string, SortItem> SortModes
{
get
{
if (m_sortModes == null)
{
m_sortModes = new Dictionary<string, SortItem>();
AddSortMode(m_sortModes, new SortItem(ObjectExtensions.GetNameFromExpression<OpenDataCategoriesModel, string>(m => m.Name), "Наименование ↑", true) { IsDefault = true });
AddSortMode(m_sortModes, new SortItem(ObjectExtensions.GetNameFromExpression<OpenDataCategoriesModel, string>(m => m.Name), "Наименование ↓"));
}
return m_sortModes;
}
}
public IEnumerable<OpenDataCategoriesModel> OpenDataCategories { get; set; }
public string OpenDataTags { get; set; }
}
and
public abstract class PagedQuery : IPagedQuery
{
private const int DEFAULT_PAGE = 1;
private const int DEFAULT_COUNT = 5;
private int? m_page;
private int? m_count;
private int? m_total;
private string m_sort;
public int? Page
{
get
{
if (m_page == null || m_page <= 0)
{
return DEFAULT_PAGE;
}
return m_page;
}
set { m_page = value; }
}
public int? Count
{
get
{
if (m_count == null || m_count <= 0)
{
return DEFAULT_COUNT;
}
return m_count;
}
set { m_count = value; }
}
public int? Total
{
get
{
if (m_total == null || m_total <= 0)
{
return 0;
}
return m_total;
}
set { m_total = value; }
}
public string SearchQuery { get; set; }
protected virtual Dictionary<string, SortItem> SortModes
{
get { return null; }
}
public string Sort
{
get
{
var sortMode = GetSortMode(m_sort);
if (sortMode == null)
{
var defaultSort = (from i in SortModes where i.Value.IsDefault select i).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(defaultSort.Key))
{
return defaultSort.Key;
}
return (from i in SortModes select i.Key).First();
}
return m_sort;
}
set
{
m_sort = value;
}
}
protected void AddSortMode(Dictionary<string, SortItem> sortModes, SortItem sortItem)
{
sortModes.Add(
String.Format(
"{0}{1}",
sortItem.FieldName.ToLower(),
sortItem.Asc ? "asc" : "desc"
),
sortItem
);
}
private SortItem GetSortMode(string sort)
{
if (SortModes == null || string.IsNullOrWhiteSpace(sort) ||
!SortModes.ContainsKey(sort.ToLower()))
{
return null;
}
return SortModes[sort.ToLower()];
}
public IOrderBy GetOrderBy()
{
var item = GetCurrentSortItem();
if (item == null)
{
return null;
}
if (item.Asc)
{
return new OrderBy(item.FieldName);
}
return new OrderByDesc(item.FieldName);
}
public SortItem GetCurrentSortItem()
{
return GetSortMode(Sort);
}
public Dictionary<string, SortItem> GetSortItems()
{
return SortModes;
}
}
and
public interface IOpenDataQuery : IPagedQuery
{
string OpenDataTags { get; set; }
}
I have some service method, that used openDataQuery class in parameters and in unit test i am trying mock this class, but this doesn't work:
public partial class OpenDataQueryRepository : Mock<OpenDataQuery>
{
public OpenDataQueryRepository(MockBehavior mockBehavior = MockBehavior.Strict)
: base(mockBehavior)
{
var opendataQuery = new Mock<IOpenDataQuery>();
var pagedQuery = opendataQuery.As<IPagedQuery>();
this.Setup(p=>p.GetOpenDataCategoriesMain(pagedQuery.Object,outttl)).Returns(OpenDataCategories);
}
}
I know that i should use Moq.Protected() for protected methods, but i don't know how use it correctly in this case. Please help me.
UPDATE:
I am testing this controller:
public class ODOpenDataController : ODBaseController
{
private readonly IOpenDataProvider m_openDataProvider;
public ODOpenDataController(IOpenDataProvider openDataProvider)
{
m_openDataProvider = openDataProvider;
}
public ActionResult Index(OpenDataQuery query)
{
int total;
query.OpenDataCategories = m_openDataProvider.GetOpenDataCategoriesMain(query, out total)
query.Total = total;
return View(query);
}
}
Test:
[Test]
public void Index_Test()
{
var opendataController = new ODOpenDataController(new OpenDataRepository().Object);
var result = opendataController.Index(new OpenDataQuery()) as ViewResult;
var model = result.Model as OpenDataQuery;
Assert.IsTrue(model.OpenDataCategories.Count() == 1);
}

Categories

Resources