Emit mapper and Generic method - c#

I have some problem with Emit mapper when I try to save in database properties.
In first I mapped this class (it work good):
[Serializable]
public class ProfileProperty
{
public string PropertyValue { get; set; }
public bool IsVisible { get; set; }
public ProfileProperty(string value, bool isVisible = true)
{
this.PropertyValue = value;
this.IsVisible = isVisible;
}
public ProfileProperty()
{
this.IsVisible = true;
}
}
I mapped here:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<PollmericaProfile, ProfileModel>();
ProfileModel prof = new ProfileModel();
if (x.User != null)
{
prof = mapper.Map(x);
}
But some of the requirements need not a string property. That's why I decided to write this:
[Serializable]
public class ProfileProperty
{
public object PropertyValue { get; set; }
public bool IsVisible { get; set; }
public ProfileProperty(object value, bool isVisible = true)
{
this.PropertyValue = value;
this.IsVisible = isVisible;
}
public ProfileProperty()
{
this.IsVisible = true;
}
public T GetValue<T>()
{
return (T)this.PropertyValue;
}
}
And all mapping is not worked =(
If you ccan, help me please. If you want I can provide the necessary information.
P.S. To be honest, I want to transfer to a string and back, so at least works
UPD: I tried without method public T GetValue<T>()... It works...

Sorry for this, but I find answer very quicly.
in mapping I must to write this:
var mapper = ObjectMapperManager
.DefaultInstance
.GetMapper<PollmericaProfile, ProfileModel>( new DefaultMapConfig()
.IgnoreMembers<PollmericaProfile, ProfileModel>(new string[] { "GetValue" }));
ProfileModel prof = new ProfileModel();
if (x.User != null)
{
prof = mapper.Map(x);
}

Related

How to mask sensetive data for particular requests (NLog)

Some of my actions accept models like:
public class PaymentRequest
{
public decimal Amount { get; set; }
public bool? SaveCard { get; set; }
public int? SmsCode { get; set; }
public BankCardDetails Card { get; set; }
}
public class BankCardDetails
{
public string Number { get; set; }
public string HolderName { get; set; }
public string ExpiryDate { get; set; }
public string ValidationCode { get; set; }
}
And the action method looks like:
[HttpPost]
[Route("api/v1/payment/pay")]
public Task<BankCardActionResponse> Pay([FromBody] PaymentRequest request)
{
if (request == null)
throw new HttpResponseException(HttpStatusCode.BadRequest);
return _paymentService.PayAsync(DataUserHelper.PhoneNumber, request);
}
I use Nlog. I think it's clear this is a bad idea to log all this bank data. My log config file contained the following line:
<attribute name="user-requestBody" layout="${aspnet-request-posted-body}"/>
I logged the request. I decided to refactor that and planned the following strategy. Actions that contain sensitive data into their requests I will mark with an attribute like
[RequestMethodFormatter(typeof(PaymentRequest))]
then take a look at my custom renderer:
[LayoutRenderer("http-request")]
public class NLogHttpRequestLayoutRenderer : AspNetRequestPostedBody
{
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
base.DoAppend(builder, logEvent);
var body = builder.ToString();
// Get attribute of the called action.
var type = ... // How can I get "PaymentRequest" from the [RequestMethodFormatter(typeof(PaymentRequest))]
var res = MaskHelper.GetMaskedJsonString(body, type);
// ... and so on
}
}
I think you understand the idea. I need the type from the method's RequestMethodFormatter attribute. Is it even possible to get it into the renderer? I need it because I'm going to deserialize request JSON into particular models (it's gonna be into the MaskHelper.GetMaskedJsonString), work with the models masking the data, serialize it back into JSON.
So, did I choose a wrong approach? Or it's possible to get the type from the attribute into the renderer?
After some research, I ended up with the following solution:
namespace ConsoleApp7
{
internal class Program
{
private static void Main()
{
var sourceJson = GetSourceJson();
var userInfo = JsonConvert.DeserializeObject(sourceJson, typeof(User));
Console.WriteLine("----- Serialize without Resolver-----");
Console.WriteLine(JsonConvert.SerializeObject(userInfo));
Console.WriteLine("----- Serialize with Resolver-----");
Console.WriteLine(JsonConvert.SerializeObject(userInfo, new JsonSerializerSettings
{
ContractResolver = new MaskPropertyResolver()
}));
}
private static string GetSourceJson()
{
var guid = Guid.Parse("3e92f0c4-55dc-474b-ae21-8b3dac1a0942");
return JsonConvert.SerializeObject(new User
{
UserId = guid,
Age = 19,
Name = "John",
BirthDate = new DateTime(1990, 5, 12),
Hobbies = new[]
{
new Hobby
{
Name = "Football",
Rating = 5,
DurationYears = 3,
},
new Hobby
{
Name = "Basketball",
Rating = 7,
DurationYears = 4,
}
}
});
}
}
public class User
{
[MaskGuidValue]
public Guid UserId { get; set; }
[MaskStringValue("***")] public string Name { get; set; }
public int Age { get; set; }
[MaskDateTimeValue]
public DateTime BirthDate { get; set; }
public Hobby[] Hobbies { get; set; }
}
public class Hobby
{
[MaskStringValue("----")]
public string Name { get; set; }
[MaskIntValue(replacement: 11111)]
public int Rating { get; set; }
public int DurationYears { get; set; }
}
public class MaskPropertyResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = base.CreateProperties(type, memberSerialization);
var allowedPropertyTypes = new Type[]
{
typeof(Guid),
typeof(DateTime),
typeof(string),
typeof(int),
};
foreach (var prop in props.Where(p => allowedPropertyTypes.Contains(p.PropertyType)))
{
if (prop.UnderlyingName == null)
continue;
var propertyInfo = type.GetProperty(prop.UnderlyingName);
var attribute =
propertyInfo?.GetCustomAttributes().FirstOrDefault(x => x is IMaskAttribute) as IMaskAttribute;
if (attribute == null)
{
continue;
}
if (attribute.Type != propertyInfo.PropertyType)
{
// Log this case, cause somebody used wrong attribute
continue;
}
prop.ValueProvider = new MaskValueProvider(propertyInfo, attribute.Replacement, attribute.Type);
}
return props;
}
private class MaskValueProvider : IValueProvider
{
private readonly PropertyInfo _targetProperty;
private readonly object _replacement;
private readonly Type _type;
public MaskValueProvider(PropertyInfo targetProperty, object replacement, Type type)
{
_targetProperty = targetProperty;
_replacement = replacement;
_type = type;
}
public object GetValue(object target)
{
return _replacement;
}
public void SetValue(object target, object value)
{
_targetProperty.SetValue(target, value);
}
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskStringValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(string);
public object Replacement { get; }
public MaskStringValueAttribute(string replacement)
{
Replacement = replacement;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskIntValueAttribute : Attribute, IMaskAttribute
{
public object Replacement { get; }
public Type Type => typeof(int);
public MaskIntValueAttribute(int replacement)
{
Replacement = replacement;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskGuidValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(Guid);
public object Replacement => Guid.Empty;
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskDateTimeValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(DateTime);
public object Replacement => new DateTime(1970, 1, 1);
}
public interface IMaskAttribute
{
Type Type { get; }
object Replacement { get; }
}
}
I hope somebody will find it helpful.
You can try nuget package https://www.nuget.org/packages/Slin.Masking and https://www.nuget.org/packages/Slin.Masking.NLog.
It can easily be integrated with DotNet projects with slight changes, and you can define your rules for it. But the document needs some improvement.
As a suggestion, you can use two files:
masking.json (can be a generic one, that shared across all projects)
masking.custom.json (can be used with particular rules for specific projects)

"The type * was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."

In my WPF application, I have a DataGrid which grabs values from a database, these values are used as configuration parameters for the robotic components of a machine.
I am trying to add a feature to Import/Export the values in the grid to/from an XML Document using XML Serialization (which I have never used before) and I am running into a few issues, which I cannot comprehend for the life of me.
I have an Export() method which should take care of exporting the values to an XML Document:
public void Export()
{
XmlSerializer serialiser = new XmlSerializer(MasterDataGrid.ItemsSource.GetType());
using (TextWriter writer = new StreamWriter("output.txt"))
{
serialiser.Serialize(writer, MasterDataGrid.ItemsSource);
writer.Close();
}
}
And here is the class which defines the contents of MasterDataGrid:
[Serializable]
[XmlRoot]
public class CmcdConfigurationParameter : INotifyPropertyChanged, IDisposable
{
[XmlElement(ElementName = "Configuration Parameter ID")]
[Key] public Guid ConfigurationParameterID { get; set; }
[XmlElement(ElementName = "Owning Device")]
[ForeignKey("OwningDevice")] public Guid DeviceDefinitionID { get; set; }
[XmlElement]
public virtual CmcdDeviceDefinition OwningDevice { get; set; }
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
[XmlElement(ElementName = "Type Assembly Qualified Name")]
public string TypeAssemblyQualifiedName { get; set; }
private string _storedValue;
[XmlElement(ElementName = "Stored Value")]
public string StoredValue
{
get { return _storedValue;}
set
{
_storedValue = value;
}
}
[NotMapped]
[XmlElement(ElementName = "Value")]
public string Value
{
get { return StoredValue; }
set { SetValue(value); }
}
private string _temporaryValueFromUser;
[NotMapped]
[XmlElement(ElementName = "Temporary Value")]
public string TemporaryValueFromUser
{
get { return _temporaryValueFromUser; }
set { _temporaryValueFromUser = value; }
}
public string Minimum { get; set; }
public string Maximum { get; set; }
public string Units { get; set; }
public string DefaultValue { get; set; }
public string Description { get; set; }
public string DisplayName { get; set; }
public EmcdConfigurationParameterType ConfigurationParameterType { get; set; }
public virtual List<CmcdConfigurationParameterChangeLog>
ChangeLogs
{
get;
set;
} //Need to confirm what is required to set up this relationship, is foreign key relationship needed?
public CmcdConfigurationParameter(string strName)
{
ConfigurationParameterID = Guid.NewGuid();
Name = strName;
}
public CmcdConfigurationParameter()
{
}
private void ConfirmValueAboveMin<T>(T objNewValue)
{
if (Minimum != null)
{
var min = Convert.ChangeType(Minimum, Type.GetType(TypeAssemblyQualifiedName));
var iCompareAgainstMinimumResult = ((IComparable) objNewValue).CompareTo((IComparable) min);
if (iCompareAgainstMinimumResult < 0)
throw new ArgumentException(
"CmcdConfigurationParameter.SetValue() failed because argument was below the minimum allowed.");
}
}
private void ConfirmValueBelowMax(IComparable objNewValue)
{
if (Maximum != null)
{
var max = Convert.ChangeType(Maximum, Type.GetType(TypeAssemblyQualifiedName));
var iCompareAgainstMaximumResult = objNewValue.CompareTo((IComparable) max);
if (iCompareAgainstMaximumResult > 0)
throw new ArgumentException(
"CmcdConfigurationParameter.SetValue() failed because argument was above the maximum allowed.");
}
}
private bool IsValueSameAsExisting(string objNewValue)
{
if (Value != null)
{
var iCompareAgainstCurrentValue = ((IComparable) objNewValue).CompareTo(Value);
if (iCompareAgainstCurrentValue == 0)
return true;
}
return false;
}
public void SetValue(string objNewValueAsString, string changedBy = "unknown")
{
if (!IsValueSameAsExisting(objNewValueAsString))
{
if (TypeAssemblyQualifiedName == null)
{
StoredValue = objNewValueAsString;
return;
}
try
{
var objNewValue = Convert.ChangeType(objNewValueAsString, Type.GetType(TypeAssemblyQualifiedName));
}
catch
{
throw new ArgumentException(string.Format(
"CmcdConfigurationParameter.SetValue was unable to convert the string value to its actual type. Expected type was {0}.",
TypeAssemblyQualifiedName));
}
ConfirmValueAboveMin(objNewValueAsString);
ConfirmValueBelowMax(objNewValueAsString);
try
{
if (ChangeLogs == null)
ChangeLogs = new List<CmcdConfigurationParameterChangeLog>();
}
catch (ObjectDisposedException e)
{
Console.WriteLine(e);
}
ChangeLogs?.Add(new CmcdConfigurationParameterChangeLog
{
ConfigurationParameterChangeLogID = Guid.NewGuid(),
DateChanged = DateTime.Now,
OwningConfigParameter = this,
OldValue = Value,
NewValue = objNewValueAsString,
ChangedBy = changedBy
});
//TemporaryValueFromUser = Convert.ToString(objNewValueAsString);
//ORIGINAL
StoredValue = Convert.ToString(objNewValueAsString);
}
}
public dynamic GetValue<T>()
{
if (Type.GetType(TypeAssemblyQualifiedName).IsEnum)
return Enum.Parse(Type.GetType(TypeAssemblyQualifiedName), Value);
return Convert.ChangeType(Value, Type.GetType(TypeAssemblyQualifiedName));
}
public override string ToString()
{
return Name;
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property = "")
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(property));
}
public void Dispose()
{
}
//This is required for Entity Framework to map the private property StoredValue (by default EF will not map non-public properties)
public static readonly Expression<Func<CmcdConfigurationParameter, string>> StoredValueExpression =
p => p.StoredValue;
}
Sorry to include the whole class. I know it is a lot, but I have absolutely no clue how to mark the class for serialization, so I figured I would post all of it so I don't accidentally leave something important out. I am new to Serialization and Attributes in general, so I think that class might be the root of the problem.
So on calling the Export() method, I get the following error:
"The type System.Data.Entity.DynamicProxies.CmcdConfigurationPar_66DFCBBA0ADF57C8F10B1E857261CD31C86C9556A2A09017262FF2C56200C356 was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."
And I can't make heads or tails of what that means. I thought maybe the long string of alphanumeric characters was a key for one of the Parameters in my database, but I couldn't find anything. I thought that maybe the serializer needed to operate on individual elements rather than a list, but I got the same error when trying to serialize a specific element of the datagrid.
I am incredibly lost here, so any guidance would be greatly appreciated.
EDIT
I think I turned off Dynamic Proxies/Lazy Loading, and now the error is different.
"The type "CmcdPickerDefinition" was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."
I can't show the full path of that class, but it is one of my classes. I have not added any attributes to that class, but I did not think that it was part of the control I am working on.

Change Previously Saved Array

Before deserialize existing .proto file i need to check whether the array property (HtmlCleanerEngineProfile[]) has already satisfied condition. If not, then i should recreate the property.
And here's the problem, after HtmlCleanerEngineProfile[] has been recreated (inside the constructor) this new property still has same value (and length) with old property.
[ProtoContract]
public class HtmlCleanerTemplate : ModelBase
{
private string _templateName;
private int _recursiveLevel = 3;
[ProtoMember(1)]
public string TemplateName
{
get => _templateName;
set => SetProperty(ref _templateName, value);
}
[ProtoMember(2), DefaultValue(3)]
public int RecursiveLevel
{
get => _recursiveLevel;
set => SetProperty(ref _recursiveLevel, value);
}
// This one..
[ProtoMember(3, OverwriteList = true)]
public HtmlCleanerEngineProfile[] EngineProfiles { get; private set; }
public HtmlCleanerTemplate()
{
var engineTypes = RetrieveEngineTypes();
if (EngineProfiles != null && EngineProfiles.Length == engineTypes.Count) return;
// 1. Clone existing to restore checked state
// 2. Recreate HtmlCleanerEngineProfile[]
var tempProfiles = EngineProfiles?.Clone() as HtmlCleanerEngineProfile[];
EngineProfiles = new HtmlCleanerEngineProfile[engineTypes.Count];
for (var i = 0; i < engineTypes.Count; i++)
{
EngineProfiles[i] = new HtmlCleanerEngineProfile
{
EngineName = engineTypes[i].Name,
EngineDescription = ReflectionUtils.GetDescription(engineTypes[i]),
};
// Restore checked state
if (tempProfiles != null && i < tempProfiles.Length)
{
// Todo: if (EngineProfiles[i].EngineName == tempEngines[i].EngineName)
EngineProfiles[i].EngineChecked = tempProfiles[i].EngineChecked;
}
}
}
private static IList<Type> RetrieveEngineTypes()
{
return ReflectionUtils
.GetTypes("ContentManager.Core.Document.Cleaner")
.Where(x => typeof(IHtmlCleanerEngine).IsAssignableFrom(x) && x.Name != typeof(IHtmlCleanerEngine).Name)
.ToList();
}
}
And the HtmlCleanerEnglineProfile
[ProtoContract]
public sealed class HtmlCleanerEngineProfile
{
internal HtmlCleanerEngineProfile() { }
[ProtoMember(1)]
public string EngineName { get; set; }
[ProtoMember(2)]
public string EngineDescription { get; set; }
[ProtoMember(3)]
public bool EngineChecked { get; set; }
}
I've trying to create a new method (same as code inside constructor) and assign [ProtoBeforeDeserialization] attribute. But still i got same result. Did I do something wrong?
Solved by adding [ProtoAfterDeserialization] attribute and [ProtoContract(SkipConstructor = true)].
[ProtoContract(SkipConstructor = true)]
public class HtmlCleanerTemplate : ModelBase
New method
[ProtoAfterDeserialization]
protected void AfterDeserialization()
{
var engineTypes = RetrieveEngineTypes();
if (EngineProfiles != null && EngineProfiles.Length == engineTypes.Count) return;
.......
}
Ctor..
public HtmlCleanerTemplate()
{
AfterDeserialization();
}

Method To Set Nested Property Value

I'm writing a plugin for a piece of software that requires all code to be in a single class (or nested classes)...
What I'm wanting to do is create a method that can handle changes to the nested properties of my _data object and give me a central place to do things like setting a dirty flag so I know to save it later.
Below is some code illustrating my file's structure and at the bottom are two pseudo-methods that hopefully can give you an idea of what I'm trying to accomplish.
public class SomePlugin
{
private DataObject _data;
private bool _dataIsDirty = false;
private class DataObject
{
public GeneralSettings Settings { get; set; }
}
private class GeneralSettings
{
public string SettingOne { get; set; }
public string SettingTwo { get; set; }
}
protected override void Init()
{
_data = new DataObject
{
Settings = new GeneralSettings
{
SettingOne = "Example value one.",
SettingTwo = "Example value two."
}
}
}
// These are pseudo-methods illustrating what I'm trying to do.
private void SetData<t>(T ref instanceProperty, T newValue)
{
if (newValue == null) throw new ArgumentNullException("newValue");
if (instanceProperty == newValue) return;
instanceProperty = newValue;
_dataIsDirty = true;
}
private void SomeOtherMethod()
{
SetData(_data.Settings.SettingOne, "Updated value one.");
}
}
Consider an approach like:
public class SomePlugin
{
private DataObject _data;
private bool _dataIsDirty = false;
public bool IsDirty => _dataIsDirty || (_data?.IsDirty ?? false);
private class DataObject
{
private bool _dataIsDirty = false;
public bool IsDirty => _dataIsDirty || (Settings?.IsDirty ?? false);
public GeneralSettings Settings { get; set; }
}
private class GeneralSettings
{
public bool IsDirty { get; set; }
private string _settingOne;
public string SettingOne
{
get { return _settingOne; }
set
{
if (value != _settingOne)
{
IsDirty = true;
_settingOne = value;
}
}
}
public string SettingTwo { get; set; } // Won't mark as dirty
}
}
Note in particular that SettingOne has logic in its setter to determine whether to set IsDirty or not.

Implement many INotifyPropertyChanged

please tell me best way to implement many duplicate INotifyPropertyChanged.
I have a MainClass that has 10 children, every child has six field and every field must fired property change when own value changed.
this my code but not work:
public class BaseModel
{
public string S1 { get; set; }
public string S2 { get; set; }
public string S3 { get; set; }
public string S4 { get; set; }
public string S5 { get; set; }
public string S6 { get; set; }
}
and I use a class named ViewModelBase to implement INotifyPropertyChanged.
in second step use a class to implement duplicate INotifyPropertyChanged:
public class ImplementBaseModel : ViewModelBase
{
private readonly BaseModel _baseModel;
public ImplementBaseModel()
{
_baseModel = new BaseModel();
}
public string S1
{
get { return _baseModel.S1; }
set
{
if (_baseModel.S1 == value)
return;
_baseModel.S1 = value;
base.OnPropertyChanged("S1");
}
}
public string S2
{
get { return _baseModel.S2; }
set
{
if (_baseModel.S2 == value)
return;
_baseModel.S1 = value;
base.OnPropertyChanged("S2");
}
}
// other code...
}
then a model has 10 of this class:
public class MidClass
{
public ImplementBaseModel ImplementBaseModel1 { get; set; }
public ImplementBaseModel ImplementBaseModel2 { get; set; }
// other field
public ImplementBaseModel ImplementBaseModel10 { get; set; }
public MidClass()
{
ImplementBaseModel1 = new ImplementBaseModel();
ImplementBaseModel2 = new ImplementBaseModel();
// ....
ImplementBaseModel10 = new ImplementBaseModel();
}
}
OK finish code! now please tell me why some property not fired when value change? is a best way to implement this code?
In your setters, you never actually set the value. Use:
public string S1
{
get { return _baseModel.S1; }
set
{
if (_baseModel.S1 == value)
return;
baseModel.S1 = value;
OnPropertyChanged("S1");
}
}
Note that I removed the base from OnPropertyChanged. It isn't normal to invoke the PropertyChanged event in this way.
All NotifyPropertyChanged does is cause every binding to perform a "get" on their bound property. If the backing field is never updated, they will just get the same data.
as a shortcut, you could also create a local method like
bool UpdateAndRaiseIfNecessary( ref string baseValue, string newValue, [CallerMemberName] string propertyName = null)
{
if (baseValue != newValue)
{
baseValue = newValue;
OnPropertyChanged( propertyName );
return true;
}
return false;
}
and then all of the setters would be like this:
set
{
this.UpdateAndRaiseIfNecessary( ref _baseModel.S1, value );
}

Categories

Resources