I'am using ValueInjector(3.x) over AutoMapper but I have some questions.
First, I don't understand the difference between UnflatLoopInjection and FlatLoopInjection.
Also i want to set values in complex types.
Class Product {
public string Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
Class ProductDTO {
public string Name { get; set; }
public Category Category { get; set; }
}
var product = repository.Get(id);
product.InjectFrom(dto);
The problem is my product.Category already have some properties with values and using InjectFrom the value injector replace the product.Category to dto.Category replacing the entire category even replacing to null.
Thanks
flattening is when you go from
Foo1.Foo2.Foo1.Name to Foo1Foo2Foo1Name
unflattening the other way around
I understand that you want to avoid injecting when the source property is Null
for this you can create an injections like this:
public class AvoidNullProps : LoopInjection
{
protected override void SetValue(object source, object target, PropertyInfo sp, PropertyInfo tp)
{
var val = sp.GetValue(source);
if(val != null)
tp.SetValue(target, val);
}
}
and use it
res.InjectFrom<AvoidNullProps>(src);
you could also use the Mapper:
Mapper.AddMap<ProductDTO, Product>(dto =>
{
var res = new Product();
res.Id = dto.Id;
res.Name = dto.Name;
if(dto.Category != null && dto.Category.Id != null)
res.Category = Mapper.Map<Category>(dto.Category);
return res;
});
var product = Mapper.Map<Product>(dto);
Related
I would like to build a Function where user could search if certain property from list contains value
Let say we will have List, and Company will be defined as a class with properties like :
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public string CompanyAddress1 { get; set; }
public string CompanyPostCode { get; set; }
public string CompanyCity { get; set; }
public string CompanyCounty { get; set; }
}
Now - Ideally I would like to have function with this parameters
List<Company> FilterCompanies(List<Company> unfilteredList, string fieldToQueryOn, string query)
{
// linq version what ideally would like to archeve
return unfilteredList.Where(x => x."fieldToQueryOn".ToString().ToLower().Contains(query.ToLower())).ToList();
}
and call :
var variable = FilterCompanies(NotNullFilledUnfilteredList, "CompanyCity", "New York")
I tried to follow the tutorial at learn.microsoft.com and it's easy, but I don't have clue how to extend that solution with reflection on Type and use it in an expression tree.
You can use Type.GetProperty to find a property by name using reflection, and then use GetValue to retrieve the value:
List<Company> FilterCompanies(List<Company> list, string propertyName, string query)
{
var pi = typeof(Company).GetProperty(propertyName);
query = query.ToLower();
return list
.Where(x => pi.GetValue(x).ToString().ToLower().Contains(query))
.ToList();
}
You should probably add some error handling though in case someone uses a property that is invalid. For example, you could do (pi?.GetValue(x) ?? string.Empty).ToString().ToLower()… to be on the safe side.
I’ve also moved the query.ToLower() out of the lambda expression to make sure it only runs once. You can also try other case-insensitive ways to check whether query is a substring of the value to avoid having to convert any string. Check out the question “Case insensitive 'Contains(string)'” for more information.
Btw. if you are generally interested in running dynamic queries, you should take a look at dynamic LINQ.
Generics and lambda:
namespace WhereTest
{
class Program
{
static void Main(string[] args)
{
var companies = new[] { new Company { Id = 1, Name = "abc" }, new Company { Id = 2, CompanyAddress1 = "abc" } };
foreach (var company in FilterCompanies(companies, "abc", x => x.Name, x => x.CompanyCity))
{
Console.WriteLine(company.Id);
}
}
static List<Company> FilterCompanies(IEnumerable<Company> unfilteredList, string query, params Func<Company, string>[] properties)
{
return unfilteredList.Where(x => properties.Any(c => c.Invoke(x) == query)).ToList();
}
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public string CompanyAddress1 { get; set; }
public string CompanyPostCode { get; set; }
public string CompanyCity { get; set; }
public string CompanyCounty { get; set; }
}
}
Advantages: no reflection, strongly typed code.
You can use GetProperty combined with GetValue
List<Company> FilterCompanies(List<Company> unfilteredList, string fieldToQueryOn, string query)
{
return unfilteredList
.Where(x => x.GetType.GetProperty(fieldToQueryOn).GetValue(x)
.ToString().ToLower().Contains(query.ToLower())).ToList();
}
OR: property accessors using string (same as javascript obj[property])
You can modify your class:
public class Company
{
// just add this code block to all your classes that would need to access
// your function
public object this[string propertyName]
{
get{
Type myType = typeof(Company);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set{
Type myType = typeof(Company);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
public int Id { get; set; }
public string Name { get; set; }
public string CompanyAddress1 { get; set; }
public string CompanyPostCode { get; set; }
public string CompanyCity { get; set; }
public string CompanyCounty { get; set; }
}
and then you can change your function like this:
List<Company> FilterCompanies(List<Company> unfilteredList, string key, string query)
{
// linq version what ideally would like to archeve
return unfilteredList.Where(x => x[key].ToString().ToLower().Contains(query.ToLower())).ToList();
}
Check this Demo
NOTE:
In order for your function to work, you need to add this code to your classes:
public object this[string propertyName]
{
get{
Type myType = typeof(<YOUR CLASS HERE>);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set{
Type myType = typeof(<YOUR CLASS HERE>);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
Bonus: you can now retrieve values using myObject["someproperty"] and you can even set their values!
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I have a case there I want to compare two objects in c#.Also I would have option to exclude specific properties when comparing. Can anyone suggest a better approach. Class will look like as below
public class Address
{
public string AddressID { get; set; }
public int AddressStagingID { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
public string Country { get; set; }
public bool PreferredAddress { get; set; }
public int? DBID { get; set; }
public Enum AddressStatus Status { get; set; }
}
I need to have a function like
private bool IsAddressModified(Address currentAddress,Address existingAddress)
{
}
Compare NET Objects have everything you need!
Ignore By Expression
CompareLogic compare = new CompareLogic();
compare.Config.IgnoreProperty<Person>(x => x.Name);
Ignore By the ClassName.MemberName
CompareLogic compare = new CompareLogic();
compare.Config.MembersToIgnore.Add("Person.Name");
Ignore All Members with a Name
CompareLogic compare = new CompareLogic();
compare.Config.MembersToIgnore.Add("UpdateDate");
Ignore with a Wildcard
CompareLogic compare = new CompareLogic();
compare.Config.MembersToIgnore.Add("*Id");
Ignore with an Attribute
[AttributeUsage(AttributeTargets.Property)]
public sealed class CompareIgnoreAttribute : Attribute
{
}
public class Shipment
{
public long IdentCode { get; set; }
public String Customer { get; set; }
[CompareIgnore]
public DateTime InsertDate { get; set; }
}
CompareLogic compare = new CompareLogic();
compare.Config.AttributesToIgnore.Add(typeof(CompareIgnoreAttribute));
Compare Only Properties with an Attribute
public class Movie
{
[Compare]
public string Name { get; set; }
public decimal PaymentForTomCruise { get; set; }
}
CompareLogic compare = new CompareLogic();
compare.Config.RequiredAttributesToCompare.Add(typeof(CompareAttribute));
I tried to develop a different solution using Expression Trees which is, in my opinion, more flexible
public class Test
{
public static void Main()
{
Address a1 = new Address();
a1.AddressID = "100";
Address a2 = new Address();
a2.AddressID = "200";
Console.WriteLine(IsAddressModified(a1,a2,a=>a.AddressID));
}
public static bool IsAddressModified(Address a1,Address a2,params Expression<Func<Address,Object>>[] props)
{
if(props == null)
return a1.Equals(a2);
foreach(Expression<Func<Address,object>> memberExpression in props)
{
MemberExpression property = memberExpression.Body as MemberExpression;
if(property != null)
{
foreach(PropertyInfo pi in typeof(Address).GetProperties())
{
// exclude all properties we passed in
if(!pi.Name.Equals(property.Member.Name))
{
var valueA1 = pi.GetValue(a1);
var valueA2 = pi.GetValue(a2);
if(valueA1 != null && valueA2 != null)
if(!valueA1.Equals(valueA2))
return true;
}
}
}
}
return false;
}
}
So what does the code?
You can pass an array of 'properties' to the method IsAddressModified. These properties will be excluded while comparing.
From the Expression I extract a MemberExpression to get the Name of each Property.
I iterate through all Properties the type Address has and check if it is one property to exclude.
Last but not least, I compare the property values.
Why so 'complicated' ?
With this solution you can pass as much properties into the function as you like AND you are totally type-safe during compilation.
In the Main you can see how I call this function. Even due to the fact that AddressID of a1 and a2 differ, the function returns false, because you excluded the AddressID.
A full compilable example can be found here
How about Reflection:
private bool IsAddressModified(Address currentAddress, Address existingAddress)
{
foreach (PropertyInfo pi in currentAddress.GetType().GetProperties())
{
//To exclude properties use condition
if (pi.Name != "City") {
object currentElement = typeof(Address).GetProperty(pi.Name).GetValue(currentAddress,null);
object existingElement = typeof(Address).GetProperty(pi.Name).GetValue(existingAddress,null);
if (!currentElement.Equals(existingElement))
{ return false; }
}
return true;
}
}
If you are looking for something very simple, use reflection. But if you need something advanced, use CompareObjects. Here is the Nuget. This library can give detailed reports on changes as well. Which means you can use it for logging etc.
This is a sample code from the site.
//This is the comparison class
CompareLogic compareLogic = new CompareLogic();
//Create a couple objects to compare
Person person1 = new Person();
person1.DateCreated = DateTime.Now;
person1.Name = "Greg";
Person person2 = new Person();
person2.Name = "John";
person2.DateCreated = person1.DateCreated;
ComparisonResult result = compareLogic.Compare(person1, person2);
//These will be different, write out the differences
if (!result.AreEqual)
Console.WriteLine(result.DifferencesString);
Brute force?
private bool IsAddressModified(Address a, Address b)
{
return a.Address1 != b.Address1 || a.Address2 != b.Address2
|| a.City != b.City || a.PostCode != b.PostCode;
// etc. for all the properties that are considered as modified
}
I have a hard time explaining what I'm exactly trying to do. There's probably a name for it, but I don't know what it is.
First, I have a model such as:
public class Customer
{
public int Id { get; set; }
public int ProductId { get; set; }
...more properties...
public virtual Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
...more properties...
}
Second, I have a string of HTML text with placeholders in {}. I'd like to have something like {Id} and have it replace the Html text with the model properties.
<div><span>Name</span><span>{Id}-{Product.Name}</span></div>
My thought was to use a NameValueCollection to get the Model properties as strings. Using reflection, I can do that for the base properties, but not for something like Product.Name.
Am I going about this the wrong way? What could I use to get a NameValueCollection that I could loop through and do a replace of the Html?
Here is the current code I have (skips virtual properties):
public virtual NameValueCollection GetNameValueCollection(Object obj)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
var coll = new NameValueCollection();
foreach (PropertyInfo property in properties)
{
if(!property.GetGetMethod().IsVirtual)
{
if (property.PropertyType == typeof(DateTime))
{
var date = (DateTime)property.GetValue(obj, null);
coll.Add(property.Name, date.ToLongDateString());
}
else if (property.PropertyType == typeof(DateTime?))
{
var date = (DateTime?)property.GetValue(obj, null);
if (date.HasValue)
{
coll.Add(property.Name, date.Value.ToLongDateString());
}
else
{
coll.Add(property.Name, string.Empty);
}
}
else
{
var value = property.GetValue(obj, null);
if (value != null)
{
coll.Add(property.Name, value.ToString());
}
}
}
}
return coll;
}
This should be recursive, but it seems like there should be a better way. By the way, I don't need a NameValueCollection specifically (could be Dictionary<string,string> for example). Thoughts? Is there a nuget package that already does this?
I ended up just using what I had and added a sub section for handling child objects. I didn't want to do full recursion since I only wanted the direct child objects, not all the way through the chain.
I have the model class like the following,
public class Station
{
[DataMember (Name="stationName")]
public string StationName;
[DataMember (Name="stationId")]
public string StationId;
}
I would like to get the Name of DataMember with Property Name, i.e If I have the property name "StationName", How can I get the stationName?
A slight modification to your class
[DataContract]
public class Station
{
[DataMember(Name = "stationName")]
public string StationName { get; set; }
[DataMember(Name = "stationId")]
public string StationId { get; set; }
}
and then this is how you can get it
var properties = typeof(Station).GetProperties();
foreach (var property in properties)
{
var attributes = property.GetCustomAttributes(typeof(DataMemberAttribute), true);
foreach (DataMemberAttribute dma in attributes)
{
Console.WriteLine(dma.Name);
}
}
I created an extension method:
public static string GetDataMemberName(this MyClass theClass, string thePropertyName)
{
var pi = typeof(MyClass).GetProperty(thePropertyName);
if (pi == null) throw new ApplicationException($"{nameof(MyClass)}.{thePropertyName} does not exist");
var ca = pi.GetCustomAttribute(typeof(DataMemberAttribute), true) as DataMemberAttribute;
if (ca == null) throw new ApplicationException($"{nameof(MyClass)}.{thePropertyName} does not have DataMember Attribute"); // or return thePropertyName?
return ca.Name;
}
with usage
myInstance.GetDataMemberName(nameof(MyClass.MyPropertyName)))
You can do it simply by using the Reflection, cast that return attribute to DataMemberAttribute class and read the Name property value.
Here is a complete 3rd party example.
I'm currently working on a project where the requirement is to log changes to each individual property of an entity.
Many of the entity's have 1 to many relationship with a lookup value type and when this changes I can so far track that i.e. '1' was changed to '2'.
What I'm trying to accomplish is to translate the number to a more readable value.
I have the below bit of code which I thought was going to work but it doesn't :(
namespace DomainClasses
{
public interface IAuditDescribable
{
String GetAuditDescription();
}
public abstract class BaseEntity
{
public Int32 Id { get; set; }
}
public class Car : BaseEntity
{
public String Vrn { get; set; }
public Int32? TransmissionId { get; set; }
public virtual Transmission Transmission { get; set; }
}
public class Transmission : BaseEntity, IAuditDescribable
{
public String Name { get; set; }
public String GetAuditDescription()
{
return Name;
}
}
}
namespace AuditTrailTwo
{
public class AuditContext : DbContext
{
public DbSet<Car> Cars { get; set; }
public DbSet<Transmission> Transmissions { get; set; }
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
var octx = ((IObjectContextAdapter)this).ObjectContext;
var changes = octx.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var change in changes)
{
var changedProperties = change.GetModifiedProperties();
var associationSets = change.EntitySet.EntityContainer.AssociationSets;
foreach (var changedProperty in changedProperties)
{
CurrentValueRecord current = change.CurrentValues;
var newValue = current.GetValue(
current.GetOrdinal(changedProperty));
DbDataRecord original = change.OriginalValues;
var oldValue = original.GetValue(
original.GetOrdinal(changedProperty));
var aSet = associationSets
.Where(x => x.ElementType.IsForeignKey)
.FirstOrDefault(x => x.ElementType.Constraint.ToProperties[0].Name == changedProperty);
if (aSet != null)
{
var targetEnd = aSet.AssociationSetEnds.GetValue(aSet.Name+"_Target", true);
//This type is always null because the 'FullName' returns: 'AuditTrailTwo.Transmission'
//and not 'DomainClasses.Transmission' as I was expecting.
var type = Type.GetType(targetEnd.EntitySet.ElementType.FullName);
if (type != null)
{
var typeSet = Set(type);
var newEntry = typeSet.Find(newValue) as IAuditDescribable;
var newEntryValue = newEntry.GetAuditDescription();
var oldEntry = typeSet.Find(oldValue) as IAuditDescribable;
var oldEntryValue = oldEntry.GetAuditDescription();
}
}
}
}
return base.SaveChanges();
}
}
}
I have looked at many posts discussing auditing with entity framework but I have so far not found any way to achieve what I'm trying to do. I'm hoping it's simply because I'm overlooking something.
Any help is appreciated.
You could use aspect oriented programming to handle when a property has changed.
http://www.postsharp.net/