If I have the following class
public class Customer
{
public string Name;
}
and then have the following log command in Serilog
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.Seq("http://localhost:5341")
.CreateLogger();
var item = new Customer();
item.Name = "John";
Serilog.Log.Information("Customer {#item}", item);
The log just displays in Seq as
Customer {}
If I change the Name field to a property it works but I would prefer not to do that at this stage. Is there any way around it?
To do this just for the one type (recommended), you can use:
.Destructure.ByTransforming<Customer>(c => new { c.Name })
If you want to include public fields for all types, or those matching some kind of condition, you can plug in a policy to do it:
class IncludePublicFieldsPolicy : IDestructuringPolicy
{
public bool TryDestructure(
object value,
ILogEventPropertyValueFactory propertyValueFactory,
out LogEventPropertyValue result)
{
if (!(value is SomeBaseType))
{
result = null;
return false;
}
var fieldsWithValues = value.GetType().GetTypeInfo().DeclaredFields
.Where(f => f.IsPublic)
.Select(f => new LogEventProperty(f.Name,
propertyValueFactory.CreatePropertyValue(f.GetValue(value))));
result = new StructureValue(fieldsWithValues);
return true;
}
}
The example scopes this down to look at objects derived from SomeBaseType only.
You can plug it in with:
.Destructure.With<IncludePublicFieldsPolicy>()
(I think it's likely to require some tweaking, but should be a good starting point.)
Thanks to Nicholas Blumhardt for a good starting point. I just have a small tweak.
my class:
public class Dummy
{
public string Field = "the field";
public string Property { get; set; } = "the property";
}
log call:
Log.Information("Dummy = {#Dummy}", new Dummy());
IDestructuringPolicy implementation includes both fields and properties:
internal class IncludePublicFieldsPolicy : IDestructuringPolicy
{
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
{
var typeInfo = value.GetType().GetTypeInfo();
var fieldsWithValues = typeInfo
.DeclaredFields
.Where(f => f.IsPublic)
.Select(f =>
{
var val = f.GetValue(value);
var propval = propertyValueFactory.CreatePropertyValue(val);
var ret = new LogEventProperty(f.Name, propval);
return ret;
})
;
var propertiesWithValues = typeInfo
.DeclaredProperties
.Where(f => f.CanRead)
.Select(f =>
{
var val = f.GetValue(value);
var propval = propertyValueFactory.CreatePropertyValue(val);
var ret = new LogEventProperty(f.Name, propval);
return ret;
})
;
result = new StructureValue(fieldsWithValues.Union(propertiesWithValues));
return true;
}
}
Related
I have this viewModel where I like to check the accessGroupList has any value of True and set baccess base on that value. If they are both false it then baccess would be false but if one of them is true baccess would be true.
MemberViewModel result = new MemberViewModel();
result.IsPractices = true;
result.IsUser = false;
var accessGroupList = new List<string>();
accessGroupList.Add("IsUser");
accessGroupList.Add("IsBestPractices");
var baccess = result.GetType().GetProperties().First(o => o.Name == accessGroupList).GetValue(result, null);
bool? baccess = Property as bool?;
I create this simple console project. You can do this, remove comment from where for using in your project
class Program
{
static void Main(string[] args)
{
var cl = new MyClass();
cl._item1 = false;
cl._item2 = false;
var a = cl.GetType().GetProperties()
//.Where(x => accessGroupList.Contains(x.Name))
.Select(x => new
{
name = x.Name,
value = (bool)x.GetValue(cl, null)
})
.Any(x => x.value);
Console.WriteLine(a);
}
}
public class MyClass
{
public bool _item1 { get; set; }
public bool _item2 { get; set; }
}
First of all note that accessGroupList is list and you need to use Contains or Any to compare it with property name. Then you can select the value of those property that appeared in accessGroupList
var baccess = result.GetType().GetProperties()
.Where(o => accessGroupList.Contains(o.Name))
.Select(t=>(bool)t.GetValue(result, null));
var baccess = result.GetType().GetProperties()
.Where(o => accessGroupList.Any(propName => Equals(propName, o.Name))
.Select(x => (bool)x.GetValue(result, null))
.Any(val => val);
Your problem is that you were using .First (which will only return one item) but then in there, you're also comparing the property name to the list itself. You need to do another linq operation to get the appropriate properties out, then you can check if any of those properties have a value of true
Thanks for taking a moment. I have a url . I'm trying to build the querystring parameters from these nullable input parameters you see below. I'm thinking what I need to do is store the nullable parameters into some type of generic list and then if the given value of the parameter is not null, append it to the url. So for example, If they supply the string? state a value of 'OH' my url would look like (http://www.thiscoolsiteJas.com/api/v1/moratorium?state=OH). How do I do this ? Any advice or direction would be greatly appreciated. - Jason
public GetMoratoriums(string? state, int? system, DateTime? inEffectOn, bool? expired, int? reason) {
//How do I build this with parameters using the input parameters??
string url = http://www.thiscoolsiteJas.com/api/v1/moratorium?";
}
Something like this?
var queryStrings = new List<string>();
if (state.HasValue) { queryStrings.Add($"state={state.Value}"); }
if (system.HasValue) { queryStrings.Add($"system={system.Value}"); }
if (inEffectOn.HasValue) { queryStrings.Add($"inEffectOn={inEffectOn.Value}"); }
if (expired.HasValue) { queryStrings.Add($"expired={expired.Value}"); }
if (reason.HasValue) { queryStrings.Add($"reason={reason.Value}"); }
var result = #"http://www.thiscoolsiteJas.com/api/v1/moratorium";
if (queryStrings.Any()) { result += $"?{String.Join("&", queryStrings)}"; }
return result;
I'm sure there is a better way to do it but this is how I would handle it.
string url = http://www.thiscoolsiteJas.com/api/v1/moratorium?";
var queryStringParams = new List<KeyValuePair<string, string>>();
if (!string.IsNullOrEmpty(state))
{
queryStringParams.Add("state",state);
}
etc...
var newUrl = new Uri(QueryHelpers.AddQueryString(url, queryStringParams));
Object oriented approach ;).
public class Parameter
{
private readonly string _name;
private readonly object _value;
public Parameter(string name, object value)
{
_name = name;
_value = value;
}
public string ForQuery()
{
return _value == null ? null : $"{_name}={_value}";
}
}
Usage
string? state = "new_state";
int? system = 1;
DateTime? inEffectOn = default;
bool? expired = default;
var parameters = new[]
{
new QueryParameter("state", state),
new QueryParameter("system", system),
new QueryParameter("inEffectOn", inEffectOn),
new QueryParameter("expired", expired),
};
var queryParameters = parameters.Select(p => p.ForQuery()).Where(p => p != null);
var query = string.Join("&", queryParameters);
For example instead of passing long list of parameters you can pass a collection of value of type QueryParameter and build query.
With extension method
public static string AppendTo(this IEnumerable<QueryParameter> parameters, string baseUrl)
{
return parameters
.Select(p => p.ForQuery())
.Where(p => p != null)
.Aggregate(
new StringBuilder(baseUrl),
(builder, p) => builder.Append(p).Append("&"),
(builder) =>
{
if (builder.Length > 0)
{
builder.Insert(0, "?");
builder.Length -= 1; // remove last "&"
}
})
.ToString();
}
Usage
var parameters = new[]
{
new QueryParameter("state", "new"),
new QueryParameter("system", null),
new QueryParameter("inEffectOn", null),
new QueryParameter("expired", false),
};
var url = parameters.AppendTo("http://www.thiscoolsiteJas.com/api/v1/moratorium");
I get an exception when I try to set a nested member Property using FastMember. For example when having these classes
public class A
{
public B First { get; set; }
}
public class B
{
public string Second { get; set; }
}
and I want to set First.Second of an instance to "hello".
var b = new B{ Second = "some value here" };
var a = new A{ First = b };
var accessor = ObjectAccessor.Create(a);
accessor["First.Second"] = value; // this does not work and gives ArgumentOutOfRangeException
I can't split it up into ["First"]["Second"] because I don't know the depth at this point. Is there a magical access for nested properties or do I have to split the hierarchy myself?
I solved the problem recursively using an Extension Method this way:
public static class FastMemberExtensions
{
public static void AssignValueToProperty(this ObjectAccessor accessor, string propertyName, object value)
{
var index = propertyName.IndexOf('.');
if (index == -1)
{
accessor[propertyName] = value;
}
else
{
accessor = ObjectAccessor.Create(accessor[propertyName.Substring(0, index)]);
AssignValueToProperty(accessor, propertyName.Substring(index + 1), value);
}
}
}
... and this is started as follows:
ObjectAccessor.Create(a).AssignValueToProperty("First.Second", "hello")
You need to traverse the object graph using multiple ObjectAccessor instances.
public static void UseFastMember()
{
var b = new B { Second = "some value here" };
var a = new A { First = b };
var value = "hello";
var a_accessor = ObjectAccessor.Create(a);
var first = a_accessor["First"];
var b_accessor = ObjectAccessor.Create(first);
b_accessor["Second"] = value;
}
Hats off to #Beachwalker for the inspiration. But should you be using TypeAccessor as opposed to ObjectAccessor this is an extension method I've had much success with:
public static class TypeAccessorExtensions
{
public static void AssignValue<T>(this TypeAccessor accessor, T t, MemberSet members, string fieldName, object fieldValue)
{
var index = fieldName.IndexOf('.');
if (index == -1)
{
if (members.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))
accessor[t, fieldName] = fieldValue;
}
else
{
string fieldNameNested = fieldName.Substring(0, index);
var member = members.FirstOrDefault(m => string.Equals(m.Name, fieldNameNested, StringComparison.OrdinalIgnoreCase));
if (member != null)
{
var nestedAccesor = TypeAccessor.Create(member.Type);
var tNested = accessor[t, fieldNameNested];
if (tNested == null)
{
tNested = Activator.CreateInstance(member.Type);
accessor[t, fieldNameNested] = tNested;
}
nestedAccesor.AssignValue(tNested, nestedAccesor.GetMembers(), fieldName.Substring(index + 1), fieldValue);
}
}
}
}
The function want to convert is using automapper is below
public ActionResult Index()
{
Func<int,string> _roleName=t=>{return new RolesDAO().getROLEsByID(t).ROLE_NAME; };
Func<int,string> _STATENAME=t=>{return new StateDAO().getStatesByID(t).STATENAME; };
Func<int,string> _COUNTRYNAME=t=>{return new CountryDAO().getCountrieByID(t).COUNTRYNAME; };
//Func<int,string> _TYPE_NAME=t=>{return new UsersTypeDAO().getUsersTypeByID(t).TYPE_NAME; };
string usertypeName = string.Empty;
string RoleName = string.Empty;
string StateName = string.Empty;
string CountryName = string.Empty;
var value = objDAO.getUsers().Select(t => new UsersVM
{
ACTIVE=t.ACTIVE,
ROLENAME=_roleName(t.ROLEID),
PROFILE_IMAGE=t.PROFILE_IMAGE,
STATEName = _STATENAME(t.STATE),
COUNTRYName = _COUNTRYNAME(t.COUNTRY),
MOBILE=t.MOBILE,
FNAME=t.FNAME,
LNAME=t.LNAME,
EMAIL=t.EMAIL,
CITY=t.CITY,
USERTYPENAME = _TypeName(t.USER_TYPE_ID),
ADDRESS=t.ADDRESS,
}).ToList();
return View("list", value);
}
I used automapper function call for member but no success, I can't even debug using breakpoint. Please suggest how to convert this object to another object. I looked into many blog post but I am not able to relate that to my problem.
All I want to use automapper for this conversion instead of Creating Anonymous type and using functions in it.
I tried to use .forMember function but it is not working returning null for converted properties. Here is my profile class:
public class UsersDM_TO_VM : Profile
{
public UsersDM_TO_VM()
{
Func<int,string> _roleName=t=>{return new RolesDAO().getROLEsByID(t).ROLE_NAME; };
Func<int,string> _STATENAME=t=>{return new StateDAO().getStatesByID(t).STATENAME; };
Func<int,string> _COUNTRYNAME=t=>{return new CountryDAO().getCountrieByID(t).COUNTRYNAME; };
Func<int,string> _TYPE_NAME=t=>{return new UsersTypeDAO().getUsersTypeByID(t).TYPE_NAME; };
CreateMap<STBL_ADMINUSERS, UsersVM>()
.ForMember(
a => a.STATEName,
b => b.MapFrom(c => _STATENAME(c.STATE)))
.ForMember(
d => d.ROLENAME,
e => e.MapFrom(f => _roleName(f.STATE)))
.ForMember(
g => g.COUNTRYName,
h => h.MapFrom(i => _COUNTRYNAME(i.STATE)))
.ForMember(
j => j.USERTYPENAME,
k => k.MapFrom(l => _TYPE_NAME(l.STATE)));
}
}
However it is not giving any error but unexpected result. No success with custom type converters also.
public class CustomUserConverter : ITypeConverter<STBL_ADMINUSERS, UsersVM>
{
Func<int, string> _roleName = t => { return new RolesDAO().getROLEsByID(t).ROLE_NAME; };
Func<int, string> _STATENAME = t => { return new StateDAO().getStatesByID(t).STATENAME; };
Func<int, string> _COUNTRYNAME = t => { return new CountryDAO().getCountrieByID(t).COUNTRYNAME; };
Func<int, string> _TYPE_NAME = t => { return new UsersTypeDAO().getUsersTypeByID(t).TYPE_NAME; };
public UsersVM Convert(STBL_ADMINUSERS s, ResolutionContext c)
{
UsersVM t = new UsersVM();
t.ROLENAME = _roleName(s.ROLEID);
t.COUNTRYName = _COUNTRYNAME(s.COUNTRY);
t.STATEName = _STATENAME(s.STATE);
t.USERTYPENAME = _TYPE_NAME(s.USER_TYPE_ID);
return t;
}
}
You want to use a custom type converter. A type converter will allow you to completely control how AutoMapper maps from the source type to the destination type.
Custom Type Converters
This is a learning exercise in expression trees.
I have this working code:
class Foo
{
public int A { get; set; }
public string B { get; set; }
}
class Bar
{
public int C { get; set;}
public string D { get; set; }
}
class FieldMap
{
public PropertyInfo From { get; set; }
public PropertyInfo To { get; set; }
}
class Program
{
static Action<TFrom, TTo> CreateMapper<TFrom, TTo>(IEnumerable<FieldMap> fields)
{
ParameterExpression fromParm = Expression.Parameter(typeof(TFrom), "from");
ParameterExpression toParm = Expression.Parameter(typeof(TTo), "to");
//var test = new Func<string, string>(x => x);
//var conversionExpression = Expression.Call(null, test.Method);
var assignments = from fm in fields
let fromProp = Expression.Property(fromParm, fm.From)
let toProp = Expression.Property(toParm, fm.To)
select Expression.Assign(toProp, fromProp);
var lambda = Expression.Lambda<Action<TFrom, TTo>>(
Expression.Block(assignments),
new ParameterExpression[] { fromParm, toParm });
return lambda.Compile();
}
static void Main(string[] args)
{
var pa = typeof(Foo).GetProperty("A");
var pb = typeof(Foo).GetProperty("B");
var pc = typeof(Bar).GetProperty("C");
var pd = typeof(Bar).GetProperty("D");
var mapper = CreateMapper<Foo, Bar>(new FieldMap[]
{
new FieldMap() { From = pa, To = pc },
new FieldMap() { From = pb, To = pd }
});
Foo f = new Foo();
Bar b = new Bar();
f.A = 20;
f.B = "jason";
b.C = 25;
b.D = "matt";
mapper(f, b); // copies properties from f into b
}
}
Works nicely. As noted it copies the corresponding properties from f to b. Now, supposing I wanted to add some conversion or formatting method that takes the "from property", does some magic, and then sets the "to property" equal to the result. Note the two commented out lines in the middle of CreateMapper.
How do I accomplish this? I got this far, but I'm sort of lost now.
Your code sample is almost there; you can use Expression.Call to do the transformation as you are clearly trying to do. Instead of assigning toProp to the fromProp MemberExpression, you can assign to a MethodCallExpression representing the value of the transformation.
The tricky part here is to figure out how to do the transformation, which I assume will vary for different properties.
You can replace the LINQ expression with:
var assignments = from fm in fields
let fromProp = Expression.Property(fromParm, fm.From)
let fromPropType = fm.From.PropertyType
let fromTransformed
= Expression.Call(GetTransform(fromPropType), fromProp)
let toProp = Expression.Property(toParm, fm.To)
select Expression.Assign(toProp, fromTransformed);
(Notice that the right-hand side of the assignment is now fromTransformed rather than fromProp.)
where GetTransform looks something like (I've assumed here that the nature of the transformation depends only on the type of the property):
private static MethodInfo GetTransform(Type type)
{
return typeof(Program).GetMethod(GetTransformName(type));
}
private static string GetTransformName(Type type)
{
if (type == typeof(int))
return "MapInt";
if (type == typeof(string))
return "MapString";
throw new ArgumentException("Unknown type");
}
Then the only thing left to do is filling in the transformations themselves; for example:
public static int MapInt(int x) { return x * 2; }
public static string MapString(string x) { return x + x; }
Then, your usage-test method would produce:
b.c == 40
b.d == "jasonjason"
I had a bit of a play with your code and I think I can give you a nice fluent-style field map builder. Given your classes Foo & Bar you could run this code:
var foo = new Foo() { A = 20, B = "jason", };
var bar = new Bar() { C = 25, D = "matt", };
var fm = new FieldMapBuilder<Foo, Bar>()
.AddMap(f => f.A, b => b.C)
.AddMap(f => f.B, b => b.D)
.AddMap(f => f.A, b => b.D, x => String.Format("!{0}!", x))
.Compile();
fm(foo, bar);
The result is that bar now looks as if it were declared like so:
var bar = new Bar() { C = 20, D = "!20!", };
The nice thing about this code is you don't need to do any reflection in the calling code, property types are inferred, and it neatly handles mapping properties of different types.
Here's the code that does it:
public class FieldMapBuilder<TFrom, TTo>
{
private Expression<Action<TFrom, TTo>>[] _fieldMaps = null;
public FieldMapBuilder()
{
_fieldMaps = new Expression<Action<TFrom, TTo>>[] { };
}
public FieldMapBuilder(Expression<Action<TFrom, TTo>>[] fieldMaps)
{
_fieldMaps = fieldMaps;
}
public FieldMapBuilder<TFrom, TTo> AddMap<P>(
Expression<Func<TFrom, P>> source,
Expression<Func<TTo, P>> destination)
{
return this.AddMap<P, P>(source, destination, x => x);
}
public FieldMapBuilder<TFrom, TTo> AddMap<PFrom, PTo>(
Expression<Func<TFrom, PFrom>> source,
Expression<Func<TTo, PTo>> destination,
Expression<Func<PFrom, PTo>> map)
{
var paramFrom = Expression.Parameter(typeof(TFrom), "from");
var paramTo = Expression.Parameter(typeof(TTo), "to");
var invokeExpressionFrom =
Expression.Invoke(map, Expression.Invoke(source, paramFrom));
var propertyExpressionTo =
Expression.Property(paramTo,
(destination.Body as MemberExpression).Member as PropertyInfo);
var assignmentExpression =
Expression.Assign(propertyExpressionTo, invokeExpressionFrom);
return new FieldMapBuilder<TFrom, TTo>(
_fieldMaps.Concat(new Expression<Action<TFrom, TTo>>[]
{
Expression.Lambda<Action<TFrom, TTo>>(
assignmentExpression,
paramFrom,
paramTo)
}).ToArray());
}
public Action<TFrom, TTo> Compile()
{
var paramFrom = Expression.Parameter(typeof(TFrom), "from");
var paramTo = Expression.Parameter(typeof(TTo), "to");
var expressionBlock =
Expression.Block(_fieldMaps
.Select(fm => Expression.Invoke(fm, paramFrom, paramTo))
.ToArray());
var lambda = Expression.Lambda<Action<TFrom, TTo>>(
expressionBlock,
paramFrom,
paramTo);
return lambda.Compile();
}
}