In previous versions of AutoMapper I created a general purpose PreCondition to conditionally map/not map certain members. I achieved this by adding an array of member names to the Map options and checking this list against the member currently being mapped as demonstrated in the code below.
Unfortunately MemberName is no longer exposed in ResolutionContext so I have no idea what member is currently being mapped. I could not find anything in the new slimmed down ResolutionContext that would lead me to this information.
I know I can write lots of specific PreCondition cases but I'm using this for zillions of types. Does anyone know of some other means of obtaining the current MemberName?
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
public class AutoMapperTest
{
public class SomeSourceType
{
public string String1;
public string String2;
public string String3;
}
public class SomeDestinationType
{
public string String1;
public string String2;
public string String3;
}
public void Test()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SomeSourceType, SomeDestinationType>();
cfg.ForAllMaps((Action<TypeMap, IMappingExpression>)((typeMap, map) =>
{
map.ForAllMembers(opt =>
{
opt.PreCondition(context =>
{
var optionsItems = context.Options.Items;
var propertiesToMap = (IEnumerable<string>)(optionsItems.Keys.Contains("PropertiesToMap") ? optionsItems["PropertiesToMap"] : null);
// The following line no longer works because MemberName is no longer exposed!
return (propertiesToMap == null) || propertiesToMap.Contains(context.MemberName);
});
});
}));
});
var source = new SomeSourceType() { String1 = "Hello", String2 = "World" };
// This will map ALL properties.
var dest = Mapper.Map<SomeSourceType, SomeDestinationType>(source);
// This will only map String1 and String3.
dest = Mapper.Map<SomeSourceType, SomeDestinationType>(source, opts => opts.Items.Add("PropertiesToMap", new string[] { "String1", "String3" }));
}
}
You were almost there. The "opt" parameter is type IMemberConfigurationExpression, which includes the destination member as a property. Here's your updated example:
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
public class AutoMapperTest
{
public class SomeSourceType
{
public string String1;
public string String2;
public string String3;
}
public class SomeDestinationType
{
public string String1;
public string String2;
public string String3;
}
public void Test()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SomeSourceType, SomeDestinationType>();
cfg.ForAllMaps((Action<TypeMap, IMappingExpression>)((typeMap, map) =>
{
map.ForAllMembers(opt =>
{
opt.PreCondition(context =>
{
var optionsItems = context.Options.Items;
var propertiesToMap = (IEnumerable<string>)(optionsItems.Keys.Contains("PropertiesToMap") ? optionsItems["PropertiesToMap"] : null);
return (propertiesToMap == null) || propertiesToMap.Contains(opt.DestinationMember.Name);
});
});
}));
});
var source = new SomeSourceType() { String1 = "Hello", String2 = "World" };
var dest = Mapper.Map<SomeSourceType, SomeDestinationType>(source);
dest = Mapper.Map<SomeSourceType, SomeDestinationType>(source, opts => opts.Items.Add("PropertiesToMap", new string[] { "String1", "String3" }));
}
}
Related
I tried to use AutoMapper NullSubstitute feature with source member and destination member of type string.
It doesn't seem to work with Projection.
As an example, I adapted src/UnitTests/Projection/NullSubstitutes.cs.
namespace AutoMapper.UnitTests.Projection
{
using System.Collections.Generic;
using System.Linq;
using QueryableExtensions;
using Xunit;
public class NullSubstitutesWithMapFrom
{
private List<Dest> _dests;
public class Source
{
public string Value { get; set; }
}
public class Dest
{
public string ValuePropertyNotMatching { get; set; }
}
[Fact]
public void Can_substitute_null_values()
{
MapperConfiguration Configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, Dest>().ForMember(m => m.ValuePropertyNotMatching, opt =>
{
opt.MapFrom(src => src.Value);
opt.NullSubstitute("5");
});
});
var source = new[] { new Source { Value = null } }.AsQueryable();
_dests = source.ProjectTo<Dest>(Configuration).ToList();
Assert.Equal("5", _dests[0].ValuePropertyNotMatching);
}
}
}
Expected : "5" equals "5"
Actual : "5" equals null
With Map everything is ok
namespace AutoMapper.UnitTests.Projection
{
using System.Collections.Generic;
using System.Linq;
using Xunit;
public class NullSubstitutesWithMapFrom
{
private List<Dest> _dests;
public class Source
{
public string Value { get; set; }
}
public class Dest
{
public string ValuePropertyNotMatching { get; set; }
}
[Fact]
public void Can_substitute_null_values()
{
MapperConfiguration Configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, Dest>().ForMember(m => m.ValuePropertyNotMatching, opt =>
{
opt.MapFrom(src => src.Value);
opt.NullSubstitute("5");
});
});
var source = new[] { new Source { Value = null } }.ToList();
_dests = Configuration.CreateMapper().Map<List<Dest>>(source);
Assert.Equal("5", _dests[0].ValuePropertyNotMatching);
}
}
}
It looks a bit like what was described in https://github.com/AutoMapper/AutoMapper/issues/642
I need authorization attribute for action which allows all but specific role.
something like
[!Authorize(Roles = "SuperUser")]
public ActionResult PaySuperUser.....
Anything built in?
Or any suggestion for custom attribute?
I think a custom attribute is a way to go.
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace YourFancyNamespace
{
public class AuthorizeExtended : AuthorizeAttribute
{
private string _notInRoles;
private List<string> _notInRolesList;
public string NotInRoles
{
get
{
return _notInRoles ?? string.Empty;
}
set
{
_notInRoles = value;
if (!string.IsNullOrWhiteSpace(_notInRoles))
{
_notInRolesList = _notInRoles
.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim()).ToList();
}
}
}
public override void OnAuthorization(HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
if (_notInRolesList != null && _notInRolesList.Count > 0)
{
foreach (var role in _notInRolesList)
{
if (actionContext.RequestContext.Principal.IsInRole(role))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
}
}
}
}
}
And here is how you can use it:
// A AuthorizeExtended equals Authorize(with Role filter) + exclude all pesky users
[AuthorizeExtended(Roles = "User", NotInRoles="PeskyUser")]
[HttpPost]
[Route("api/Important/DoNotForgetToUpvote")]
public async Task<IHttpActionResult> DoNotForgetToUpvote()
{
return Ok("I did it!");
}
// Б AuthorizeExtended equals plain Authorize + exclude all pesky users
[AuthorizeExtended(NotInRoles="PeskyUser")]
[HttpPost]
[Route("api/Important/DoNotForgetToUpvote")]
public async Task<IHttpActionResult> DoNotForgetToUpvote()
{
return Ok("I did it!");
}
// В AuthorizeExtended equals Authorize
[AuthorizeExtended]
[HttpPost]
[Route("api/Important/DoNotForgetToUpvote")]
public async Task<IHttpActionResult> DoNotForgetToUpvote()
{
return Ok("I did it!");
}
I have some problem with cascade all (orphan) and delete the old objcet from the database.
Example:
I have an class A which contains an object of class B. Now, when I create an object of class A and save it, everything works fine. When I call the method SetValueOfB(int i) and save the object A again, the old object B is still in the database.
Must the association between the classes always be directional (for every HasMany/Reference/HasOne...)? (But object b has nothing to know about object a)
Is there a way to solve the problem with unidirectional association?
Do I need a one-to-one mapping? Because the object B can only belong to object A (A is a parameter and B is a value).
Here is a failing test:
using System.Collections.Generic;
using System.Data;
using System.IO;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Mapping;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
namespace ReferenceCascade.Test
{
public class CascadeTest
{
private const string DbFile = "firstProject.db";
[Test]
public void checkCascadeAll()
{
var sessionFactory = CreateSessionFactory();
A testee = new A(new B(1));
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(testee);
transaction.Commit();
}
}
testee.SetValueOfB(2);
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(testee);
transaction.Commit();
}
}
using (var session = sessionFactory.OpenSession())
{
using (session.BeginTransaction())
{
IList<B> stores = session.CreateCriteria(typeof(B))
.List<B>();
Assert.That(stores.Count, Is.EqualTo(1));
}
}
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard.UsingFile(DbFile).IsolationLevel(IsolationLevel.ReadCommitted))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<CascadeTest>())
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private static void BuildSchema(Configuration config)
{
// delete the existing db on each run
if (File.Exists(DbFile))
{
File.Delete(DbFile);
}
// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config)
.Create(false, true);
}
}
public abstract class Entity
{
public const long InitialId = 0;
private readonly long _id;
protected Entity()
{
_id = InitialId;
}
public virtual long Id
{
get { return _id; }
}
}
public class A : Entity
{
private B _b;
public A()
{
}
public A(B b)
{
_b = b;
}
public virtual void SetValueOfB(int i)
{
_b = new B(i);
}
public virtual B B
{
get { return _b; }
}
}
public class B : Entity
{
private readonly int _i;
public B()
{
}
public B(int i)
{
_i = i;
}
public virtual int I
{
get { return _i; }
}
}
public class EntityMap<T> : ClassMap<T> where T : Entity
{
public EntityMap()
{
Id(x => x.Id).GeneratedBy.HiLo("33878").Access.CamelCaseField(Prefix.Underscore);
}
}
public class AMap : EntityMap<A>
{
public AMap()
{
Table("A");
References(x => x.B).Not.LazyLoad().Cascade.All().Access.CamelCaseField(Prefix.Underscore);
}
}
public class BMap : EntityMap<B>
{
public BMap()
{
Table("B");
Map(x => x.I).Not.LazyLoad().Access.CamelCaseField(Prefix.Underscore);
}
}
}
Or here is the project: vs project
We haven't found a way to solve the problem. In NHibernate version 4.1, the problem will be fixed and it is possible to use cascade=all-delete-orphan with many-to-one. See here: https://nhibernate.jira.com/browse/NH-1262
Hi I have to different classes with Same properties and I want to access the peoperties of my classes Dynamically.
public Class1
{
public const prop1="Some";
}
public Class2
{
public const prop1="Some";
}
And in my code I am getting my class name like this
string classname="Session["myclass"].ToString();";//Say I have Class1 now.
And I want to get the prop1 value .
Something like
string mypropvalue=classname+".prop1";//my expected result is Some
///
Type typ=Type.GetType(classname);
Please help me in getting this
Reflection
var nameOfProperty = "prop1";
var propertyInfo = Class1Object.GetType().GetProperty(nameOfProperty);
var value = propertyInfo.GetValue(myObject, null);
for static:
var nameOfProperty = "prop1";
var propertyInfo = typeof(Class1).GetProperty("prop1", BindingFlags.Static);
var value = propertyInfo.GetValue(myObject, null);
Class reference from string
EDIT (I made example):
class Program
{
static void Main(string[] args)
{
var list = Assembly.Load("ConsoleApplication4").GetTypes().ToList();
Type ty = Type.GetType(list.FirstOrDefault(t => t.Name == "Foo").ToString());
//This works too: Type ty = Type.GetType("ConsoleApplication4.Foo");
var prop1
= ty.GetProperty("Temp", BindingFlags.Static | BindingFlags.Public);
Console.WriteLine(prop1.GetValue(ty.Name, null));
Console.ReadLine();
}
}
public static class Foo
{
private static string a = "hello world";
public static string Temp
{
get
{
return a;
}
}
}
Msdn
you can use following function to get a property value fron an object dynamically:
just pass object to scan & property name
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
I would like map array of objects to one single object.
For example:
public class ArrayData
{
//name of property in class MyObject in upper under score casing
public string PropName{get;set;}
//value of property in class MyObject
public string PropValue{get;set;}
}
Source data:
ArrayData [] sourceData = new ArrayData[]{new ArrayData{PropName="MY_ID",PropValue="1"}}
Destination object:
public class MyObject
{
public int MyId{get;set;}
}
My aim is set MyObject.MyId to 1.
Convention is:
if(ArrayData.PropName == MyObject.Property.Name)
{
MyObject.PropName = ArrayData.PropValue;
}
EDITED: I tried this way:
public class UpperUnderscoreNamingConvention : INamingConvention
{
#region Implementation of INamingConvention
public Regex SplittingExpression
{
get { throw new System.NotImplementedException(); }
}
public string SeparatorCharacter
{
get { return string.Format("_"); }
}
#endregion
}
public class TestProfile: Profile
{
public override string ProfileName { get { return "TestProfile"; } }
protected override void Configure()
{
SourceMemberNamingConvention = new UpperUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
CreateMap<ArrayData, MyObject>()
.ForMember(dest => dest.MyId, opt =>
{
opt.Condition(src => src.ColumnName == "MY_ID");
opt.MapFrom(src => src.Value);
});
}
}
Conversion:
Mapper.Initialize((obj) => obj.AddProfile(new TestProfile()));
var myClass = Mapper.Map<ArrayData[], MyClass>(sourceData);
It doesnt work, I get this exception:
AutoMapper.AutoMapperMappingException: Missing type map configuration
or unsupported mapping.
Also I think it is not good solution map all properties manualy:
.ForMember(dest => dest.MyId, opt => opt.Condition(src => src.ColumnName =="MY_ID"))