I'm trying to mock the following method TryGetApns:
private readonly Func<string, ICommunicationClient> _communicationFactory;
public CommunicationApiFacade(Func<string, ICommunicationClient> communicationFactory)
{
_communicationFactory = communicationFactory;
}
public IList<ApnResponse> TryGetApns(string tenant)
{
GetApnsResponse response = null;
try
{
var communicationApiClient = _communicationFactory(tenant);
response = communicationApiClient.JctConfigurationService.GetApns();
}
catch (HttpException e)
{
...
}
return response?.Apns ?? new List<ApnResponse>();
}
with the following test:
private Mock<ICommunicationApiFacade> _communicationApiFacade;
[SetUp]
public void SetUp()
{
_fixture = new Fixture()
.Customize(new AutoMoqCustomization());
_communicationApiFacade = _fixture.Freeze<Mock<ICommunicationApiFacade>>();
}
[Test]
public void RunJctTests_WhenJctIsInAPrivateNetwork_ShouldReturnAPassedTest()
{
// Arrange
var jctApn = _fixture.Create<string>();
var message = _fixture.Build<JctLoggedIn>()
.With(x => x.ApnFromDevice, jctApn)
.Create();
var response = _fixture.Build<ApnResponse>()
.With(x => x.IsPrivateApn, true)
.With(x => x.ApnName, jctApn).Create();
_communicationApiFacade.Setup(x => x.TryGetApns(string.Empty))
.Returns(new List<ApnResponse> { response });
var subject = _fixture.Create<NetworkProviderTestRunner>();
// Act
var result = subject.Execute(message);
// Assert
Assert.That(result.Result, Is.True);
}
and this is the NetworkProviderTestRunner class:
private readonly ICommunicationApiFacade _communicationApi;
public NetworkProviderTestRunner(ICommunicationApiFacade communicationApi)
{
_communicationApi = communicationApi;
}
public JctTest Execute(JctLoggedIn message)
{
var apns = _communicationApi.TryGetApns(message.Tenant);
var jctApn = apns.FirstOrDefault(x => x.ApnName == message.ApnFromDevice);
if (jctApn != null)
{
var privateApn = apns.FirstOrDefault(x => x.PublicApnId.Equals(jctApn.Id));
if (privateApn != null || jctApn.IsPrivateApn)
return new JctTest { Result = true };
}
return new JctTest { Result = false };
}
JctLoggedIn class:
public class JctLoggedIn : Message
{
public string Imei { get; set; }
public string SimCardIdFromDevice { get; set; }
public string SimCardIdFromStorage { get; set; }
public string ApnFromDevice { get; set; }
public string ApnFromStorage { get; set; }
public string FirmwareFromDevice { get; set; }
public int DeviceTypeFromStorage { get; set; }
public int SerialNumber { get; set; }
}
but for some reason I always get back an empty list. I've tried to populate the list in the SetUp and also defining the output there but I's always the same. Any help?
While you could explicitly omit the Tenant property when building the message object, you could also change the Mock setup to this:
_communicationApiFacade.Setup(x => x.TryGetApns(message.Tenant))
.Returns(new List<ApnResponse> { response });
This line
_communicationApiFacade.Setup(x => x.TryGetApns(string.Empty))
.Returns(new List<ApnResponse> { response });
is telling the mock to return the list containing response when TryGetApns is called with an empty string for the tenant parameter.
What is the value of message.Tenant after message is created by this line:
var message = _fixture.Build<JctLoggedIn>()
.With(x => x.ApnFromDevice, jctApn)
.Create();
If it is not string.empty, your mock will not return the reponse list.
Replace
var response = _fixture.Build<ApnResponse>()
.With(x => x.IsPrivateApn, true)
.With(x => x.ApnName, jctApn).Create();
with
var response = _fixture.Build<ApnResponse>()
.With(x => x.Tenant, String.Empty)
.With(x => x.IsPrivateApn, true)
.With(x => x.ApnName, jctApn).Create();
if Tenant is writable.
If the property is writable, AutoFixture will create an object with non-empty value for Tenant without explicitly setting up what you want.
Related
In the current example I find it easy going HeaderPayload => Header since I just use the source.Data.Values directly.
What let's me construct the other way Header => HeaderPayload?
The current config is wrong and fails in the Map call.
[Test]
public void TestAutoMapper2()
{
// arrange
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Header, HeaderPayload>()
.ForMember(dest => dest.Data, map => map.MapFrom(src => src.Values))
;
});
var a = new Header { Values = new List<string> { "foo", "bar" } };
var mapper = new Mapper(config);
// act
var b = mapper.Map<Header>(a);
// assert
Assert.IsNotNull(b.Data);
Assert.IsNotEmpty(b.Data.Values);
Assert.AreEqual("foo", b.Data.Values.FirstOrDefault());
}
public class Data
{
public List<string> Values { get; set; }
}
public class Header
{
public List<string> Values { get; set; }
}
public class HeaderPayload
{
public Data Data { get; set; }
}
how to retrieve partial object?
{
Id:123,
Name:"david",
Languages:[{b:"en"},{b:"ru"}]
}
public async Task<myObj> Get(long id, string lang=null)
{
FilterDefinition<myObj> filter = Builders<myObj>.Filter.Eq(s => s.Id, id)
& Builders<myObj>.Filter.ElemMatch(l => l.Languages, s => s.b== lang);
ProjectionDefinition<myObj> projection = Builders<Symptom>.Projection
.Include(d => d.Id)
.Include(d => d.Name)
.Include(d => d.Languages[-1]);
FindOptions<myObj> options = new FindOptions<myObj> { Projection = projection };
using (IAsyncCursor<myObj> cursor = await db.Collection.FindAsync(filter, options))
{
return cursor.SingleOrDefault();
}
}
if i call function get(123,"cn") i expect to get:
{
Id:123,
Name:"david",
Languages:null
}
instead of null.
how to fix the query to achieve my demand?
i think this will get the job done:
public async Task<myObj> Get(long id, string lang = null)
{
var res = await db.Collection.AsQueryable()
.Where(m =>
m.Id == id &&
m.Languages.Any(l => l.b == lang))
.SingleOrDefaultAsync();
return res ?? new myObj { _Id = id, Languages = null };
}
If you want to display the languages only when they match (and null if none match up), then try the following
public async Task<myObj> Get(long id, string lang = null)
{
FilterDefinition<myObj> filter = Builders<myObj>.Filter.Eq(s => s.Id, id)
var result = await collection.Find(filter).SingleOrDefaultAsync();
if (result != null)
result.Languages = result.Languages?.Where(lng => lng.b.Equals(lang)).ToList();
return result;
}
You will get your object that you want based on the ID.. then further it will return only those languages that match up with language that you are passing (null or otherwise).
It's working. I don't know what you mean by "instead of null".
One minor thing, that you would like to not include Languges, instead you projected the Languages to an array range with the [-1]. So it's just return last element of the array. The final code is:
> db.ItemWithLanguages.find()
{ "_id" : ObjectId("5dfb57c9692d22eefa6e0cfe"), "Id" : 123, "Name" : "david", "Languages" : [ { "B" : "en" }, { "B" : "cn" } ] }
internal class MyObj
{
public long Id { get; set; }
[BsonId]
[BsonElement("_id")]
public ObjectId MyId { get; set; }
public string Name { get; set; }
public List<Language> Languages { get; set; }
}
internal class Language
{
public string B { get; set; }
}
public static async Task<MyObj> Get(IMongoCollection<MyObj> collection, long id, string lang = null)
{
FilterDefinition<MyObj> filter = Builders<MyObj>.Filter.Eq(s => s.Id, id)
& Builders<MyObj>.Filter.ElemMatch(l => l.Languages, s => s.B == lang);
// excluding d.Languages by not including it.
// it makes Languages = null.
ProjectionDefinition<MyObj> projection = Builders<MyObj>.Projection
.Include(d => d.Id)
.Include(d => d.Name);
FindOptions<MyObj> options = new FindOptions<MyObj> { Projection = projection };
using (IAsyncCursor<MyObj> cursor = await collection.FindAsync(filter, options))
{
return cursor.SingleOrDefault();
}
}
...
string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("test");
var myObjs = db.GetCollection<MyObj>("ItemWithLanguages");
MyObj ret;
Task.Run(async () => { ret = await Get(myObjs, 123, "cn"); }).ConfigureAwait(false).GetAwaiter()
.GetResult();
I would like to use AutoMapper and provide some values in runtime.
For example I have DTO & ViewModel.
One of the property doesn't exist in DTO and cannot be mapped directly by using Converters/Resolvers/Transformers;
namespace Lab.So.Sample
{
public class UserDto
{
public int UserId { get; set; }
public string UserCode { get; set; }
}
public class UserGroupDto
{
public List<UserDto> Users { get; set; }
}
public class UserViewModel
{
public int UserId { get; set; }
public string FullName { get; set; }
}
public class UserGroup
{
public List<UserViewModel> Users { get; set; }
}
class Program
{
static void Main(string[] args)
{
var src = new Fixture().Create<UserGroupDto>();
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.ValidateInlineMaps = false;
});
// How to hook mapping of nested object User->UserViewModel to provide value of FullName in runtime
var dst = Mapper.Map<UserGroupDto, UserGroup>(src);
}
}
}
I have some workaround how do this, but it's not human-friendly, as for me:
class Program
{
internal class UserViewModelFullNameResolver : IValueResolver<UserDto, UserViewModel, string>
{
public string Resolve(UserDto source, UserViewModel destination, string destMember, ResolutionContext context)
{
var names = context.Items["ctx.Names"] as IDictionary<int, string>;
if (names == null || !names.TryGetValue(source.UserId, out var fullName))
{
return null;
}
return fullName;
}
}
static void Main(string[] args)
{
var src = new Fixture().Create<UserGroupDto>();
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.ValidateInlineMaps = false;
cfg.CreateMap<UserDto, UserViewModel>()
.ForMember(d => d.FullName, opt => opt.MapFrom<UserViewModelFullNameResolver>());
});
var names = new Dictionary<int, string>
{
{ 10, "FullName-10" },
{ 20, "FullName-20" },
};
var dst = Mapper.Map<UserGroupDto, UserGroup>(src, opt=>opt.Items["ctx.Names"] = names);
}
}
In that workaround most inconvenient it's agreement of key names in opt.Items;
If something accidentally made typo it hard to investigate and fix;
I looking something like this:
var dst = Mapper.Map<UserGroupDto, UserGroup>(src, opt=>opt.Use(new UserViewModelFullNameResolver());
By other words, its defining an instance of resolver in runtime for each unique case;
Also I would accept, if I will have ability to define a hook to mapping particular type in graph of objects:
var dst = Mapper.Map<UserGroupDto, UserGroup>(src, opt=>opt.Hook<UserDto,UserViewModel>((s,d)=> { /* any logic to read external data */ });
An example of usage:
var srcA = readDataA();
var srcB = readDataB();
var dst = Mapper.Map<UserGroupDto, UserGroup>(
src,
opt=>opt.Hook<UserDto,UserViewModel>(
(s,d)=>
{
d.FullName = srcA + srcB;
});
Please suggest something, that would help to read data to accomplish mapping in cases when source doesn't have all required data for nested object in destination.
I was able to use existing API to archive my goal:
public static class InlineMappingExtensions
{
public static void EnableInlineMapping(this IMapperConfigurationExpression cfg)
{
cfg.ForAllMaps((t, i) =>
{
i.AfterMap((s, d, ctx) =>
{
ctx.ApplyInlineMap(s, d);
}
);
});
}
public static IMappingOperationOptions OnMap<TSrc, TDst>(this IMappingOperationOptions opts,
Action<TSrc, TDst> resolve)
{
var srcTypeName = typeof(TSrc);
var dstTypeName = typeof(TDst);
var ctxKey = $"OnMap_{srcTypeName}_{dstTypeName}";
opts.Items.Add(ctxKey, resolve);
return opts;
}
private static void ApplyInlineMap(this ResolutionContext opts, object src, object dst)
{
if (src == null)
{
return;
}
if (dst == null)
{
return;
}
var srcTypeName = src.GetType();
var dstTypeName = dst.GetType();
var ctxKey = $"OnMap_{srcTypeName}_{dstTypeName}";
if (!opts.Items.TryGetValue(ctxKey, out var inlineMap))
{
return;
}
var act = inlineMap as Delegate;
act?.DynamicInvoke(src, dst);
}
}
To enable it:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.ValidateInlineMaps = false;
cfg.EnableInlineMapping();
});
Sample of usage:
// read data from external sources
var names = new Dictionary<int, string>
{
{ 10, "FullName-10" },
{ 20, "FullName-20" },
};
Action<UserDto, UserViewModel> mapA = (s, d) =>
{
if (names.TryGetValue(s.UserId, out var name))
{
d.FullName = name;
}
};
Action<UserGroupDto, UserGroup> mapB = (s, d) =>
{
if (DateTime.Now.Ticks > 0)
{
d.Users = null;
}
};
var dst = Mapper.Map<UserGroupDto, UserGroup>(src, opt => opt.OnMap(mapA).OnMap(mapB));
I'm using AutoMapper to copy an entity framework object to another identical database. The problem is that it tries to copy the lookup tables.
I have tried to exclude them with the AddGlobalIgnore and the ShouldMapProperty but it doesn't work. AutoMapper still try to copy those properties.
Here's my code. I would like to ignore the properties that start with "LU"
dynamic newObject= new NewObject();
MapperConfiguration config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.AddGlobalIgnore("LU");
cfg.ShouldMapProperty = p => !p.GetType().ToString().StartsWith("LU");
cfg.ShouldMapField = p => !p.GetType().ToString().StartsWith("LU");
});
IMapper mapper = config.CreateMapper();
newObject = mapper.Map(objectToCopy, objectToCopy.GetType(), newObject.GetType());
I did also tried
MapperConfiguration config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.AddGlobalIgnore("LU");
cfg.ShouldMapProperty = p => !p.PropertyType.Name.StartsWith("LU");
cfg.ShouldMapField = p => !p.FieldType.Name.StartsWith("LU");
});
and
MapperConfiguration config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.AddGlobalIgnore("LU");
cfg.ShouldMapProperty = p => !p.Name.StartsWith("LU");
cfg.ShouldMapField = p => !p.Name.StartsWith("LU");
});
Create your configuration as a separate profile, then add that profile to the mapper configuration.
class Program
{
static void Main(string[] args)
{
dynamic newObject = new NewObject();
var objectToCopy = new ObjectToCopy();
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MyProfile>();
});
var mapper = config.CreateMapper();
mapper.Map(objectToCopy, newObject);
// newObject.LU_Ignore = "Original value"
// newObject.DoNotIgnore = "New value"
}
}
class MyProfile : Profile
{
protected override void Configure()
{
CreateMissingTypeMaps = true;
ShouldMapProperty = p => !p.Name.StartsWith("LU"); // this is the correct way to get the property name
}
}
class ObjectToCopy
{
public string LU_Ignore { get; set; } = "New value";
public string DoNotIgnore { get; set; } = "New value";
}
class NewObject
{
public string LU_Ignore { get; set; } = "Original value";
public string DoNotIgnore { get; set; } = "Original value";
}
Something seems goofy about how configurations are applied to the Mapper created form the mapper.CreateMapper call. I'm looking into it to see if I can find out more information and will update this answer if I find anything.
I wont say that this is the best (performant or design-wise) approach, but this works:
public static class AutoExtensions {
public static IMappingExpression Ignore(this IMappingExpression expression, Func<PropertyInfo, bool> filter) {
foreach (var propertyName in expression
.TypeMap
.SourceType
.GetProperties()
.Where(filter)
.Select(x=>x.Name))
{
expression.ForMember(propertyName, behaviour => behaviour.Ignore());
}
return expression;
}
}
You can configure your mapper like this (for these sample classes):
public class Client {
public string LUName { get; set; }
public string Dno { get; set; }
}
public class ClientDTO
{
public string LUName { get; set; }
public string Dno { get; set; }
}
and test it out like this:
private static void ConfigAndTestMapper() {
var config = new MapperConfiguration(cfg =>{
cfg.CreateMap(typeof (Client), typeof (ClientDTO))
.Ignore(x => x.Name.StartsWith("LU"));
});
var mapper = config.CreateMapper();
var result = mapper.Map<ClientDTO>(new Client() {LUName = "Name", Dno = "Dno"});
var isIgnored = result.LUName == null;
}
PS: this is also pretty "hackish" since it tries to map all kind of properties there (readonly/non-public, etc.) so take it with a grain of salt.
I have the code, which use the userClass converters. I want to do the same thing using automapper. How to rewrite the code?
public static ClaimIdentityView ConvertToClaimIdentityView(this ClaimsIdentity Identity)
{
ClaimIdentityView result = new ClaimIdentityView()
{
Name = Identity.Name,
NameClaimType = Identity.NameClaimType,
AuthenticationType = (AuthenticationTypeEnum)EnumStringValue.Parse(typeof(AuthenticationTypeEnum), Identity.AuthenticationType),
RoleClaimType = Identity.RoleClaimType
};
foreach (Claim item in Identity.Claims)
result.ClaimViewList.Add(item.ConvertToClaimView());
return result;
}
public static ClaimView ConvertToClaimView(this Claim Claim)
{
return new ClaimView()
{
Type = Claim.Type,
Value = Claim.Value,
ValueType = Claim.ValueType
};
}
And the second class (the first one is from System.Security.Claims; namespace):
public class ClaimIdentityView
{
public ClaimIdentityView()
{
ClaimViewList = new List<ClaimView>();
}
public Guid UserId { get; set; }
public AuthenticationTypeEnum AuthenticationType { get; set; }
public IList<ClaimView> ClaimViewList { get; set; }
public string Name { get; set; }
public string NameClaimType { get; set; }
public string RoleClaimType { get; set; }
}
Your mappings would look like this:
AutoMapper.Mapper.CreateMap<ClaimsIdentity, ClaimIdentityView>()
.ForMember(dest => dest.ClaimViewList, opt => opt.MapFrom(src => src.Claims))
.ForMember(dest => dest.AuthenticationType,
opt => opt.MapFrom(src => (AuthenticationTypeEnum)
EnumStringValue.Parse(typeof (AuthenticationTypeEnum), src.AuthenticationType)));
AutoMapper.Mapper.CreateMap<Claim, ClaimView>();
Example mapping code:
var claimIdentity = new ClaimsIdentity(WindowsIdentity.GetCurrent());
var view = AutoMapper.Mapper.Map<ClaimsIdentity, ClaimIdentityView>(claimIdentity);
This test would then pass:
var claimIdentity = new ClaimsIdentity(WindowsIdentity.GetCurrent());
// realistically the current account will have claims, but..
claimIdentity.AddClaim(new Claim("Type", "Value"));
var view = AutoMapper.Mapper.Map<ClaimsIdentity, ClaimIdentityView>(claimIdentity);
Assert.IsTrue(view.ClaimViewList.Count > 0);