I have classes something like this:
public class foo{
public string FooProp1 {get; set;}
public Bar Bar{get; set;}
}
public class Bar{
public string BarProp1 {get; set;}
public string BarProp2 {get; set;}
}
I have some audit setup where If I Update Foo then I can get that property Name and value for all the property apart from the 'Bar'. Is there a way to get property name and value of 'BarProp1'.
private void ProcessModifiedEntries(Guid transactionId) {
foreach (DbEntityEntry entry in ChangeTracker.Entries().Where(t => t.State == EntityState.Modified).ToList()) {
Track audit = CreateAudit(entry, transactionId, "U");
foreach (var propertyName in entry.CurrentValues.PropertyNames) {
string newValue = entry.CurrentValues[propertyName]?.ToString();
string originalValue = entry.OriginalValues[propertyName]?.ToString();
SetAuditProperty(entry, propertyName, originalValue, audit, newValue);
}
}
}
I want to audit BarProp1 when Foo got changed.
You want classes to report additional information to your auditing system. I think the best place to do that is in your CreateAudit method. The question is how.
You could have code in there that does something special for each incoming entry:
var foo = entry.Entity as Foo;
if (foo != null)
{
// do something with foo.Bar
}
var boo = entry.Entity as Boo;
if (boo != null)
{
// do something with boo.Far
}
etc.
Of course that isn't very pretty.
If you have multiple classes that need to report additional info to the auditor I would define an interface and tack that to each of these classes:
public interface IAuditable
{
string AuditInfo { get; }
}
public class Foo : IAuditable
{
public string FooProp1 { get; set; }
public Bar Bar { get; set; }
[NotMapped]
public string AuditInfo
{
get { return Bar?.BarProp1; }
}
}
And then in CreateAudit:
var auditable = entry.Entity as IAuditable;
if (auditable != null)
{
// do something with auditable.AuditInfo
}
And even if there's only one class that needs this behavior I would still use the interface because it makes your code self-explanatory.
Related
I have a class
[BsonIgnoreExtraElements]
public class CustomerModel
{
public string BranchID { get; set; }
public string BranchName { get; set; }
public string ReceiverID { get; set; }
public string ReceiverName{ get; set; }
}
I am writing a filter activity which can validate any field with specific value configured in MongoDB
"Exclude":[{"SourceCol":"Receiver Mode ID","Values":{"Value":["G","8","J"]}}
and written the comparing logic as
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
public static bool CheckPropertyCompare(CustomerModel customer, Exclude item)
{
var propertyValue = GetPropValue(customer, item.SourceCol);
return item.Values.Value.ToList().Contains(propertyValue);
}
In this, the Receiver Mode ID from MongoDB is actually looking for ReceiverID and I am stuck as to how can I resolve this issue. The only option I can think of is Key-Value pair collection to fetch the field name. but would like to know if there is any options like Attributes which can ease this process.
TIA
I think you can achieve that with Attributes as you say.
You can create a custom attribute, like this:
internal class MongoDBFieldAttribute : Attribute
{
public string Field{ get; private set; }
public MongoDBFieldAttribute(string field)
{
this.Field= field;
}
}
Then in your class:
public class CustomerModel
{
...
[MongoDBField("ReceiverModeID")]
public string ReceiverID { get; set; }
}
I think it could be better without spaces, it could be a problem, maybe yo can use a Trim() or similar... or yoy can try [MongoDBField("Receiver Mode ID")], never tried.
Then you can create a method than can relation both, property name and attribute name, for example:
private Dictionary<string, string> getRelationPropertyAttribute(Type type)
{
var dicRelation = new Dictionary<string, string>();
var properties = type.GetProperties();
foreach (var property in properties)
{
var attributes = property.GetCustomAttributes(inherit: false);
var customAttributes = attributes
.AsEnumerable()
.Where(a => a.GetType() == typeof(MongoDBFieldAttribute));
if (customAttributes.Count() <= 0)
continue;
foreach (var attribute in customAttributes)
{
if (attribute is MongoDBFieldAttribute attr)
dicRelation[attr.Field] = property.Name;
}
}
return dicRelation;
}
Finally, you can play with that dictionary and in your method you can do something like that:
public static bool CheckPropertyCompare(CustomerModel customer, Exclude item)
{
var dicRelation = getRelationPropertyAttribute(typeof(CustomerModel));
var propertyName = dicRelation[item.SourceCol];
var propertyValue = GetPropValue(customer, propertyName);
return item.Values.Value.ToList().Contains(propertyValue);
}
It´s an idea...
Hope it helps.
I need to create a mapping (using AutoMapper) from n classes all being derived from one abstract class to a contract class
So for example:
public abstract class bar
{
public string Field1 {get; set;}
public someClass Field2 {get; set;}
}
public class foo1bar: bar
{
// members
}
public class foo2bar: bar
{
// members
}
public class barContract
{
public string Field1 {get; set;}
// this will use existing someClass.Description field
public string Field2Description {get; set;}
}
implementations of bar class are multiple, and also are likely to change (more will be added). As Automapper cannot map to abstract class (so the constructor mapperConfiguration.CreateMap<bar, barContract>() is incorrect), I was wondering will it be possible to use reflection to find all classes 'implementing' bar class and map them 'automatically'
var type = typeof(bar);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
I've got the Types, and I'm trying to invoke CreateMap.
As the type is now a variable, I'm creating a generic method once again using reflection:
foreach (Type t in types)
{
mapperConfiguration.GetType().GetMethod("CreateMap")
.MakeGenericMethod(t, typeof(barContract))
.Invoke(mapperConfiguration, null);
}
The problem is that CreateMap is not a member of type that is extracted from mapperConfiguration instance - when I'm trying to extract the method by name I get null. I see it's defined in IProfileExpression, so I'm trying to extract the method from the interface:
typeof(IProfileExpression).GetMethod("CreateMap") and I get System.Reflection.AmbiguousMatchException - what is kind of OK, but using System.Reflection.BindingFlags in GetMethod to be more specific I'm again getting nulls.
What am I doing wrong, or how to get around that mapping problem ?
You can create map from one type to another type CreateMap(SouceType, DestinationType));
public abstract class Bar
{
public string Field1 { get; set; }
}
public class Foo1bar : Bar
{
// members
}
public class Foo2bar : Bar
{
// members
}
public class BarContract
{
public string Field1 { get; set; }
// this will use existing someClass.Description field
public string Field2Description { get; set; }
}
class Program
{
static void Main(string[] args)
{
AutoMapperConfiguration.Init();
var foo1 = new Foo1bar {Field1 = "One"};
var barContract1=AutoMapperConfiguration.Mapper.Map<Foo1bar, BarContract>(foo1);
Console.WriteLine("barContract1.Field1: " + barContract1.Field1);
var foo2 = new Foo2bar {Field1 = "Two"};
var barContract2=AutoMapperConfiguration.Mapper.Map<Foo2bar, BarContract>(foo2);
Console.WriteLine("barContract2.Field1: " + barContract2.Field1);
Console.ReadLine();
}
public static class AutoMapperConfiguration
{
public static void Init()
{
MapperConfiguration = new MapperConfiguration(cfg =>
{
var types = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !string.IsNullOrEmpty(type.Namespace) &&
type.BaseType != null &&
type.BaseType == typeof(Bar));
foreach (Type type in types)
{
cfg.CreateMap(type, typeof(BarContract));
}
});
Mapper = MapperConfiguration.CreateMapper();
}
public static IMapper Mapper { get; private set; }
public static MapperConfiguration MapperConfiguration { get; private set; }
}
}
Output
I have the following classes:
interface IEntityWithId
{
int id { get; set; }
}
public class MyEntityMetaData
{
[Display(Name="Name of my property")]
public string MyProp { get; set; }
}
public partial class MyEntity : MyEntityMetaData, IEntityWithId
{
}
/* Autogenerated code somewhere else */
public partial class MyEntity
{
public int Id { get; set; }
public string MyProp { get; set; }
}
Now I have a IQueryable<IEntityWithId> in which the objects are actually of type MyEntity.
I used this code to extract display names:
private static List<string> GetColumnNames<T>(IQueryable<T> query) where T : class
{
Type elementType = query.ElementType;
var defaultColumns = new List<WebGridColumn>();
var columnNames =
(
from
p in elementType.GetProperties()
where
IsBindableType(p.PropertyType) &&
(p.GetIndexParameters().Length == 0)
select
new
{
DisplayName = (DisplayNameAttribute)p.GetCustomAttributes(typeof(DisplayNameAttribute), true).FirstOrDefault(),
Display = (DisplayAttribute)p.GetCustomAttributes(typeof(DisplayAttribute), true).FirstOrDefault(),
Name = p.Name
}
).Select
(
name =>
name.DisplayName != null ?
name.DisplayName.DisplayName :
name.Display != null ?
name.Display.Name :
name.Name
).ToList();
return columnNames;
}
But it doesn't actually work (display names are null). I do get correct type and properties within the method, but the attributes are missing. I'm guessing it has something to do with the fact that those are attributes of a parent class and/or my class being a partial one.
Is there something I can do to actually get those attributes inside my method (I can't really change my input type)?
Edit: Having run into the problem yet again, I believe that the answer to this question is in the part of MVC code responsible for bringing ViewData.ModelMetadata to life (because ViewData.ModelMetadata has correct those display names, even when they are as oddly inherited as in this question). This post makes me believe that this part of MVC that I'm looking for is a ModelMetadataProvider. One of those days I'll have to analyze the code...
The autogenerated code is hiding MyEntityData.MyProp with MyEntity.Prop. MyEntity.Prop is to all efects a completely new method and does not inherit any attributes that might be defined in MyEntityData.MyProp.
If possible, you should refactor MyEntityData to:
public class MyEntityMetaData
{
[Display(Name="Name of my property")]
public virtual string MyProp { get; set; }
}
And make the code generation tool override said method:
/* Autogenerated code somewhere else */
public partial class MyEntity
{
public int Id { get; set; }
public override string MyProp { get; set; }
}
I have an abstract class that looks like so:
public abstract class PageObjectsBase
{
public abstract string FriendlyName { get; }
public abstract string PageObjectKeyPrefix { get; }
public abstract string CollectionProperty { get; }
}
And a class that derives from PageObjectsBase:
public class PageRatingList : PageObjectsBase
{
public IList<PageRating> PageRatings { get; set; }
public PageRatingList()
{
this.PageRatings = new List<PageRating>();
}
public override string CollectionProperty
{
get
{
var collectionProperty = typeof(PageRatingList).GetProperties().FirstOrDefault(p => p.Name == "PageRatings");
return (collectionProperty != null) ? collectionProperty.Name : string.Empty;
}
}
public override string FriendlyName
{
get
{
return "Page feedback/rating";
}
}
public override string PageObjectKeyPrefix
{
get
{
return "pagerating-";
}
}
}
And a PageRating class which PageRatingList.PageRatings is holding a collection of:
public class PageRating : PageObjectBase
{
public int Score { get; set; }
public string Comment { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
The PageRatingList is being stored in a database (EPiServer's Dynamic Data Store, more specifically using the Page Object Manager). I need to create some reporting functionality and am essentially loading all reports that derive from PageObjectBase. When it comes to returning the data, the code will never know at compile time what type of data it is to load, so I am using Reflection. In my reporting class I have:
//this gives me the right type
var type = Type.GetType("MyNameSpace.PageRatingList", true);
var startPageData = this._contentRepository.Get<PageData>(startPage);
PageObjectManager pageObjectManager = new PageObjectManager(startPageData);
//this loads the instances from the DB
var props = pageObjectManager.LoadAllMetaObjects()
.FirstOrDefault(o => o.StoreName == "Sigma.CitizensAdvice.Web.Business.CustomEntity.PageRatingList");
//this gives me 4 PropertyInfo objects (IList: PageRatings, string : CollectionProperty, string :FriendlyName, string : PageObjectKeyPrefix)
var properties = props.Value.GetType().GetProperties();
I can then iterate through the PropertyInfo objects using:
foreach (var property in properties)
{
//extract property value here
}
The issue I am having is that I cannot figure out how to get the value of each of the propertyinfo objects. In addition, one of those properties is type List and again we wont know the type of T until runtime. So I also need some logic that checks if one of the PropertyInfo objects is of type List and then provides access to each of the properties in the List - the List being of type PageRating.
Can anyone help here? I've not really used reflection in the past so I am winging my way through it, rightly or wrongly!
Many thanks
Al
I may be missunderstanding the problem, but i think you may use something like this:
var props = new PageRatingList(); /*actual instanse of the object, in your case, i think "props.Value" */
var properties = typeof(PageRatingList).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(IList<PageRating>))
{
IList<PageRating> list = (IList<PageRating>)property.GetValue(props);
/* do */
}
else
{
object val = property.GetValue(props);
}
}
Hope this helps to find your solution.
I am developing a WPF Application using MVVM Architecture. I am an amateur in WPF so bear with me..
I have two model classes. Parent class has an object of another (child) class as its property. (i mean nested objects and not inherited objects)
For instance, consider the following scenario.
public class Company
{
public string CompanyName {get; set;}
public Employee EmployeeObj {get; set;}
}
public class Employee
{
public string FirstName {get; set;}
public string LastName {get; set;}
}
I want to validate the properties of Employee entity using Enterprise Library Validation Block.
I was able to do it by implementing IDataErroInfo interface in employee class as shown below
public class Employee : IDataErrorInfo
{
[NotNullValidator(MessageTemplate="First Name is mandatory"]
public string FirstName {get; set;}
[StringLengthValidator(0,20,MessageTemplate="Invalid")]
public string LastName {get; set;}
public string Error
{
get
{
StringBuilder error = new StringBuilder();
ValidationResults results = Validation.ValidateFromAttributes<Employee>(this);
foreach (ValidationResult result in results)
{
error.AppendLine(result.Message);
}
return error.ToString();
}
}
public string this[string propertyName]
{
get
{
ValidationResults results = Validation.ValidateFromAttributes<Employee>(this);
foreach (ValidationResult result in results)
{
if (result.Key == propertyName)
{
return result.Message;
}
}
return string.Empty;
}
}
}
I dont want to implement IDataErroInfo for every child model i create.
Is there any way to validate the Employee object by implementing IDataErrorInfo on the parent (Company) class ?
And also is there any triggers to start validation of objects. I would like to validate the objects only when i want to and not all the time.
You can absolutely implement IDataErrorInfo on a base class using Validation Application Block. Here is an article that describes how to. The code basically comes down to this:
public abstract class DataErrorInfo : IDataErrorInfo
{
string IDataErrorInfo.Error
{
get { return string.Empty; }
}
string IDataErrorInfo.this[string columnName]
{
get
{
var prop = this.GetType().GetProperty(columnName);
return this.GetErrorInfo(prop);
}
}
private string GetErrorInfo(PropertyInfo prop)
{
var validator = this.GetPropertyValidator(prop);
if (validator != null)
{
var results = validator.Validate(this);
if (!results.IsValid)
{
return string.Join(" ",
results.Select(r => r.Message).ToArray());
}
}
return string.Empty;
}
private Validator GetPropertyValidator(PropertyInfo prop)
{
string ruleset = string.Empty;
var source = ValidationSpecificationSource.All;
var builder = new ReflectionMemberValueAccessBuilder();
return PropertyValidationFactory.GetPropertyValidator(
this.GetType(), prop, ruleset, source, builder);
}
}
You can use this abstract class add validation behavior to your entities by inheriting from it:
public partial class Customer : DataErrorInfo
{
}