Below is a modified implementation of MvxLang.
My goal is to be able to implement screen reader text concisely with existing string values stored in <ViewModelName>.json files in our projects Resources, as well as dynamically generated text retrieved from <ViewModelName>.cs files.
I wish to use the following syntax:
xct:SemanticEffect.Description="{mvx:MvxLang ViewModel.SomeStringFromCsFile | SomeStringFromJsonFile | ViewModel.SomeOtherStringFromCsFile}"
This way our ViewModels/Xaml will not be bloated with screen reader text logic/markup.
My implementation works fine when only retrieving string value from <ViewModelName>.json files, but I wish to use a variety of values from <ViewModelName>.cs files as well...
My troubles occur in this block of code when calling GetValue(), I can return the value, but it appears IMarkupExtensions are called before the the ViewModel:
var prefix = "ViewModel.";
if (str.Contains(prefix))
{
var vm = (rootObject is MvxContentPage)
? ((MvxContentPage)rootObject).GetViewModel()
: ((MvxContentView)rootObject).GetViewModel();
PropertyInfo prop = vm.GetType().GetProperty(str.Replace(prefix, string.Empty));
var propValue = prop.GetValue(vm);
return propValue as string ?? string.Empty;
}
Is there a way to return the runtime values here?
Here is the rest of the code:
[ContentProperty("Source")]
public class MvxLang : IMarkupExtension
{
readonly static IMvxTextProvider _textProvider = Mvx.IoCProvider.Resolve<IMvxTextProvider>();
public static string TransitioningViewModel { private get; set; }
public string Source { set; get; }
public object ProvideValue(IServiceProvider serviceProvider)
{
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
var rootProvider = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
object rootObject = null;
if (rootProvider == null)
{
var propertyInfo = valueProvider.GetType()
.GetTypeInfo()
.DeclaredProperties
.FirstOrDefault(dp => dp.Name.Contains("ParentObjects"));
var parentObjects = (propertyInfo.GetValue(valueProvider) as IEnumerable<object>).ToList();
rootObject = parentObjects.Last();
}
else
rootObject = rootProvider.RootObject;
var name = string.Empty;
if (!(rootObject is MvxContentPage || rootObject is MvxContentView))
{
// Transitioning
name = TransitioningViewModel;
}
else
{
var page = (VisualElement)rootObject;
name = page.GetType().BaseType.GetGenericArguments()[0].Name;
}
if (!string.IsNullOrEmpty(name))
{
var value = string.Empty;
(bool, string) targetPropertyCheck = this.TargetPropertyCheck_ADA(valueProvider.TargetProperty);
if (targetPropertyCheck.Item1)
{
value = ProvideValue_ADA(targetPropertyCheck.Item2, _textProvider, rootObject, name, Source);
return value;
}
else
{
value = _textProvider.GetText(name, Source);
return value;
}
}
return string.Empty;
}
public (bool, string) TargetPropertyCheck_ADA(object targetProperty)
{
var propertyName = string.Empty;
var isADA = false;
if (targetProperty is BindableProperty _targetProperty)
{
if (_targetProperty.DeclaringType.Name.Equals("SemanticEffect"))
{
propertyName = _targetProperty.PropertyName;
isADA = propertyName.Equals("Description") || propertyName.Equals("Hint");
}
}
return (isADA, propertyName);
}
public string ProvideValue_ADA( string propertyName, IMvxTextProvider textProvider, object rootObject, string name, string keyString)
{
if (!string.IsNullOrEmpty(keyString) && !string.IsNullOrEmpty(propertyName))
{
switch (propertyName)
{
case "Description":
if (keyString.Contains('|'))
{
var parameters = keyString.Split(new char[] { '|' });
IEnumerable<string> appliedStrings = parameters.Select(s =>
{
var str = s.Trim();
var prefix = "ViewModel.";
if (str.Contains(prefix))
{
var vm = (rootObject is MvxContentPage)
? ((MvxContentPage)rootObject).GetViewModel()
: ((MvxContentView)rootObject).GetViewModel();
PropertyInfo prop = vm.GetType().GetProperty(str.Replace(prefix, string.Empty));
var propValue = prop.GetValue(vm);
return propValue as string ?? string.Empty;
}
else
{
return textProvider.GetText(name, str);
}
});
return string.Join(", ", appliedStrings);
}
else
{
return textProvider.GetText(name, keyString);
}
case "Hint":
var appliedText = textProvider.GetText(name, keyString);
return $"Double tap to {appliedText}";
default:
break;
}
}
return string.Empty;
}
}
Ultimately landed on this solution after realizing that the IMarkupExtension class is triggered BEFORE the ViewModel has set its properties.
[ContentProperty(nameof(Values))]
public class Provider : IMarkupExtension<MultiBinding>
{
readonly static IMvxTextProvider _textProvider = Mvx.IoCProvider.Resolve<IMvxTextProvider>();
public string Values { set; get; }
IList<BindingBase> Bindings { get; set; } = new List<BindingBase>();
string StringFormat { get; set; } = "{0}";
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) => ProvideValue(serviceProvider);
public MultiBinding ProvideValue(IServiceProvider serviceProvider)
{
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
var _property = valueProvider?.TargetProperty as BindableProperty;
object rootObject = null;
var propertyInfo = valueProvider.GetType()
.GetTypeInfo()
.DeclaredProperties
.FirstOrDefault(dp => dp.Name.Contains("ParentObjects"));
var parentObjects = (propertyInfo.GetValue(valueProvider) as IEnumerable<object>).ToList();
rootObject = parentObjects.Last();
var name = string.Empty;
var page = (VisualElement)rootObject;
name = page.GetType().BaseType.GetGenericArguments()[0].Name;
if (!string.IsNullOrEmpty(name))
{
var propertyName = _property.PropertyName;
if (SemanticPropertyCheck(_property, propertyName))
{
BuildBindings(propertyName, name, Values);
}
else
{
Bindings.Add(new Binding()
{
Path = $"[{name}|{Values}]",
Source = InternalValue.Instance
});
}
}
return new MultiBinding()
{
Bindings = Bindings,
StringFormat = StringFormat
};
}
bool SemanticPropertyCheck(BindableProperty property, string name) =>
property.DeclaringType.Name.Equals("SemanticEffect") && (name.Equals("Description") || name.Equals("Hint"));
void BuildBindings(string propertyName, string name, string valueString)
{
if (!string.IsNullOrEmpty(valueString) && !string.IsNullOrEmpty(propertyName))
{
switch (propertyName)
{
case "Description":
if (valueString.Contains('|'))
{
var values = (valueString.Split(new char[] { '|' }) as IEnumerable<string>).ToList();
values.ForEach(s =>
{
var index = values.IndexOf(s);
Bindings.Add(CreateBinding(name, s.Trim(), false));
if (index > 0)
StringFormat += $", {{{index}}}";
});
}
else
{
Bindings.Add(CreateBinding(name, valueString, false));
}
break;
case "Hint":
Bindings.Add(CreateBinding(name, valueString, true));
break;
default:
break;
}
}
}
BindingBase CreateBinding(string name, string key, bool isHint)
{
var prefix = "ViewModel.";
return (key.Contains(prefix))
? new Binding() { Path = key.TrimStart(prefix.ToCharArray()) }
: new Binding() { Path = $"[{name}|{key}]", Source = InternalValue.Instance };
}
sealed class InternalValue
{
readonly static IMvxTextProvider _textProvider = Provider._textProvider;
public static InternalValue Instance { get; } = new InternalValue();
public static InternalValue HintInstance { get; } = new InternalValue() { _isHint = true };
bool _isHint { get; set; } = false;
public string this[string _nameKey] => GetText(_nameKey.Split('|'));
private string GetText(string[] nameKey)
{
var name = nameKey[0];
var key = nameKey[1];
var prefix = _isHint
? "Double tap to "
: string.Empty;
var appliedText = _textProvider.GetText(name, key);
return $"{prefix}{appliedText}";
}
}
}
Related
Let's say we have a class Root that has BasicDetails class property and then basicDetails has two properties. A string "Name" and a dynamic object called "CustomAttributes".
I want to get the value in the following manner:
var root = new Root();
root.BasicDetails.Name = "Samurai";
root.BasicDetails.CustomAttributes.phone = "12345";
string res1 = GetDeepPropertyValue(root, "BasicDetails.CustomAttributes.Phone").ToString();
string res2 = GetDeepPropertyValue(root, "BasicDetails.Name").ToString();
The following is the code I have tried (based on another answer on SO):
public static object GetDeepPropertyValue(object src, string propName)
{
if (propName.Contains('.'))
{
string[] Split = propName.Split('.');
string RemainingProperty = propName.Substring(propName.IndexOf('.') + 1);
return GetDeepPropertyValue(src.GetType().GetProperty(Split[0]).GetValue(src, null), RemainingProperty);
}
else
return src.GetType().GetProperty(propName).GetValue(src, null);
}
The following are the classes:
public class Root
{
public Root()
{
BasicDetails = new BasicDetails();
}
public BasicDetails BasicDetails { get;set;}
}
public class BasicDetails
{
public BasicDetails()
{
CustomAttributes = new ExpandoObject();
}
public string Name { get; set; }
public dynamic CustomAttributes { get; set; }
}
The function that I have tried is throwing null reference error. Unfortunately I do not understand reflection too well so at the moment I am monkey patching. If someone could please explain c#how this can be done it would be great. Thank you in advance.
Following method gets values from nested properties:
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if (propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
if (src is ExpandoObject)
{
var expando = src as IDictionary<string, object>;
if (expando != null)
{
object obj;
expando.TryGetValue(propName, out obj);
return obj;
}
return null;
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
}
Usage:
string res1 = GetPropertyValue(root, "BasicDetails.CustomAttributes.phone") as string;
string res2 = GetPropertyValue(root, "BasicDetails.Name") as string;
I have a list of key/values in following format
var temp = new Dictionary<string, string>();
temp["Prop1"] = "Test1";
temp["Prop2"] = "Test2";
temp["SubProperty.Sub1"] = "Test3";
temp["SubProperty.Sub2"] = "Test4";
temp["ListData[0].List1"] = "Test5";
temp["ListData[0].List2"] = "Test6";
temp["ListData[1].List1"] = "Test7";
temp["ListData[1].List2"] = "Test8";
Is there an easy way to convert this list int a new object such as below.
public class MainClass
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public SubClass SubProperty { get; set; }
public ListClass[] ListData { get; set; }
}
public class ListClass
{
public string List1 { get; set; }
public string List2 { get; set; }
}
public class SubClass
{
public string Sub1 { get; set; }
public string Sub2 { get; set; }
}
I know ASP MVC does something like this, taking form names and values and auto parsing into instantiated property. But couldn't find what they use. Any help appreciated. Looking to either convert to class or even JSON would be useful and can convert to object that way
Created following using ExpandoObjects which seemed to do the trick for what I need. Will update if come accross any other scenarios
var temp = new Dictionary<string, string>();
temp["Prop1"] = "Test1";
temp["Prop2"] = "Test2";
temp["SubProperty.Sub1"] = "Test3";
temp["SubProperty.Sub2"] = "Test4";
temp["ListData[0].List1"] = "Test5";
temp["ListData[0].List2"] = "Test6";
temp["ListData[1].List1"] = "Test7";
temp["ListData[1].List2"] = "Test8";
temp["ListData[5].List1"] = "Test9";
temp["ListData[5].List2"] = "Test10";
var justjson = PropertiesToObject.ToJson(temp);
var myobj = PropertiesToObject.ToObject<MainClass>(temp);
public static class PropertiesToObject
{
public static T ToObject<T>(Dictionary<string, string> dict)
{
var json = ToJson(dict);
return JsonConvert.DeserializeObject<T>(json);
}
public static string ToJson(Dictionary<string, string> dict)
{
dynamic expando = new ExpandoObject();
foreach (var item in dict)
{
AddProperty(expando, item.Key, item.Value);
}
return JsonConvert.SerializeObject(expando);
}
private static ExpandoObject SetValueIfListOrNot(ExpandoObject expando,string propertyName, object propertyValue)
{
bool isList = propertyName.Contains("[");
var listName = propertyName.Split('[').FirstOrDefault();
int listindex = isList ? int.Parse(propertyName.Split('[').LastOrDefault()?.Split(']').FirstOrDefault()) : 0;
var expandoDict = expando as IDictionary<string, object>;
if (expandoDict.ContainsKey(listName) == false)
{
if (!isList)
{
expandoDict.Add(listName, propertyValue);
}
else
{
var lobj = new dynamic[0];
expandoDict.Add(listName, lobj);
}
}
var exi = expandoDict[listName];
if(exi.GetType().IsArray)
{
var ma = exi as dynamic[];
var len = ma.Length;
if (len < listindex + 1)
{
Array.Resize(ref ma, listindex + 1);
expandoDict[listName] = ma;
}
var item = ma[listindex];
if(item == null)
{
ma[listindex] = propertyValue;
}
return ma[listindex];
} else
{
return exi as ExpandoObject;
}
}
private static void AddProperty(ExpandoObject expando, string propertyName, object propertyValue)
{
var subprops = propertyName.Split('.');
var propname = subprops.First();
var rest = string.Join(".", subprops.Skip(1).ToList());
if (rest == "")
{
SetValueIfListOrNot(expando, propname, propertyValue);
} else
{
var expa = SetValueIfListOrNot(expando, propname, new ExpandoObject());
AddProperty(expa, rest, propertyValue);
}
}
}
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; }
}
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;
}
}
}
}
I have the following class Franchise:
public class Franchise
{
public string FolderName { get; set; }
public string InstallerExeName { get; set; }
}
I have a method that checks specific property value for uniqness among all franchises in the db.
public bool ValidateFolderName(string folderName)
{
var allFranchises = _franchiseService.GetAll();
var result = allFranchises.Any(f => f.FolderName == folderName);
return result;
}
The problem is I have to check another property for uniqness:
public bool ValidateInstallerExeName(string installerExeName)
{
var allFranchises = _franchiseService.GetAll();
var result = allFranchises.Any(f => f.InstallerExeName == installerExeName);
return result;
}
I want to avoid code duplication by making a generic method. Something like:
public bool ValidateProperty(string propertyName)
{
var allFranchises = _franchiseService.GetAll();
// Not sure how to write this line
var result = allFranchises.Any(f => f.[propertyName] == propertyName);
return result;
}
The problem is I am not sure how to re-write this line of code so that it can get the property name and check its value by the provided parameter:
var result = allFranchises.Any(f => f.[propertyName] == propertyName);
I know I can do something like this with reflection:
franchise.GetType().GetProperty(propertyName).GetValue(franchise, null);
but I am not sure how can I make this to fit my case. Any help with working example will be greatly appreciated. Thanks!
Here is a full working example using reflection:
class Program
{
private static List<Franchise> allFranchises;
static void Main(string[] args)
{
allFranchises = new List<Franchise>
{
new Franchise() { FolderName=#"c:\1", InstallerExeName="1.exe" },
new Franchise() { FolderName=#"c:\2", InstallerExeName="2.exe" },
new Franchise() { FolderName=#"c:\3", InstallerExeName="3.exe" },
new Franchise() { FolderName=#"c:\4", InstallerExeName="4.exe" },
new Franchise() { FolderName=#"c:\5", InstallerExeName="5.exe" },
};
Console.WriteLine(ValidateProperty("FolderName", #"c:\2", allFranchises));
Console.WriteLine(ValidateProperty("InstallerExeName", "5.exe", allFranchises));
Console.WriteLine(ValidateProperty("FolderName", #"c:\7", allFranchises));
Console.WriteLine(ValidateProperty("InstallerExeName", "12.exe", allFranchises));
}
public static bool ValidateProperty(string propertyName, object propertyValue, IEnumerable<Franchise> validateAgainst)
{
PropertyInfo propertyInfo = typeof(Franchise).GetProperty(propertyName);
return validateAgainst.Any(f => propertyInfo.GetValue(f, null) == propertyValue);
}
}
public class Franchise
{
public string FolderName { get; set; }
public string InstallerExeName { get; set; }
}
It will print out:
True
True
False
False
as expected.
public bool ValidateProperty<TType, TPropertyType>(Func<TType, TPropertyType> propertySelector, TPropertyType propertyValue)
{
return _franchiseService.GetAll().Any(f => propertySelector(f) == propertyValue);
}
You can call it like this:
if( ValidateProperty(x => x.FirstName, "Joe") )
This does not use reflection and you have intellisense for your propertyname as well.
You may use an extension method:
public static bool ValidateProperty(
this IEnumerable<Franchise> franchises,
string property,
object value)
{
var prop = typeof(Franchise).GetProperty(property);
if (prop == null)
throw new ArgumentException("Property does not exist");
return franchises.Any(f =>
prop.GetValue(f) == value);
}
Use it like this:
var r = _franchiseService.GetAll().ValidateProperty("FolderName", "myfolder1");
You can build the function you want using System.Linq.Expressions
public class Franchise
{
public string FolderName { get; set; }
public string InstallerExeName { get; set; }
bool ValidateProperty(string propertyName) {
var allFranchises = new List<Franchise>();
var parameter = Expression.Parameter(typeof(Franchise));
var property = Expression.Property(parameter, propertyName);
var thisExpression = Expression.Constant(this, typeof(Franchise));
var value = Expression.Property(thisExpression, propertyName);
var notThis = Expression.ReferenceNotEqual(thisExpression, property);
var equal = Expression.Equal(value, property);
var lambda = Expression.Lambda<Func<Franchise, bool>>(Expression.And(notThis, equal));
// lamda is now the equivalent of:
// x => !object.ReferenceEquals(this, x) && object.Equals(x.Property, this.Property)
return allFranchises.Any(lambda.Compile());
}
}
If allFranchises is of type IQueryable you use allFranchises.Any(lambda)
You could also caches the expression for later use if you are worried about performance.
}