I have a class like
public User class
{
public string Name{get;set;}
public string Age{get;set;
}
With a dictionary like
Dictionary<string,string> data= new Dictionary<string,string>();
data.Add("Name","Rusi");
data.Add("Age","23");
User user= new User();
Now i want to map User object to this dictionary using Automapper. Automapper maps properties of objects but in my case there is a dictionary and object.
How can this be mapped?
AutoMapper maps between properties of objects and is not supposed to operate in such scenarios. In this case you need Reflection magic. You could cheat by an intermediate serialization:
var data = new Dictionary<string, string>();
data.Add("Name", "Rusi");
data.Add("Age", "23");
var serializer = new JavaScriptSerializer();
var user = serializer.Deserialize<User>(serializer.Serialize(data));
And if you insist on using AutoMapper you could for example do something along the lines of:
Mapper
.CreateMap<Dictionary<string, string>, User>()
.ConvertUsing(x =>
{
var serializer = new JavaScriptSerializer();
return serializer.Deserialize<User>(serializer.Serialize(x));
});
and then:
var data = new Dictionary<string, string>();
data.Add("Name", "Rusi");
data.Add("Age", "23");
var user = Mapper.Map<Dictionary<string, string>, User>(data);
If you need to handle more complex object hierarchies with sub-objects you must ask yourself the following question: Is Dictionary<string, string> the correct data structure to use in this case?
This thread is a bit old, but nowadays there's how to do it on automapper without any configuration, as stated at official documentation:
AutoMapper can map to/from dynamic objects without any explicit configuration (...) Similarly you can map straight from Dictionary to objects, AutoMapper will line up the keys with property names.
Update:
The following code shows a working sample (with unit tests).
void Test()
{
var mapper = new MapperConfiguration(cfg => { }).CreateMapper();
var dictionary = new Dictionary<string, object>()
{
{ "Id", 1 },
{ "Description", "test" }
};
var product = mapper.Map<Product>(dictionary);
Assert.IsNotNull(product);
Assert.AreEqual(product.Id, 1);
Assert.AreEqual(product.Description, "test");
}
class Product
{
public int Id { get; set; }
public string Description { get; set; }
}
With the current version of AutoMapper:
public class MyConfig
{
public string Foo { get; set; }
public int Bar { get; set; }
}
var config = new MapperConfiguration(cfg => {});
var mapper = config.CreateMapper();
var source = new Dictionary<string, object>
{
["Foo"] = "Hello",
["Bar"] = 123
};
var obj = mapper.Map<MyConfig>(source);
obj.Foo == "Hello"; // true
AutoMapper is quite a flexible solution. You could probably achieve this using a custom mapping profile, e.g.:
public class UserMappingProfile : Profile
{
// Props
public override string ProfileName { get { return "UserMappingProfile"; } }
// Methods
public override void Configure()
{
CreateMap<User, Dictionary<string, string>>().ConvertUsing<DictionaryTypeConverter>();
base.Configure();
}
// Types
internal class DictionaryTypeConverter : ITypeConverter<User, Dictionary<string, string>>
{
public User Convert(ResolutionContext context)
{
var dict = context.SourceValue as Dictionary<string, string>;
if (dict == null)
return null;
return new User() { Name = dict["Name"], Age = dict["Age"] };
}
}
}
With this, I can create a custom instance of a mapper:
var config = new Configuration(new TypeMapFactory(), MapperRegistry.AllMappers());
config.AddProfile<UserMappingProfile>();
config.AssertConfigurationIsValid();
var mapper = new MappingEngine(config);
Which I could probably do:
var dict = new Dictionary<string, string> { { "Name", "Matt" }, { "Age", "27" } };
var user = mapper.Map<User, Dictionary<string, string>>(dict);
Much simpler solution. Just map your object from KeyValuePair. Example:
CreateMap<KeyValuePair<Guid, string>, User>()
.ForMember(u => u.Id, src => src.MapFrom(x => x.Key))
.ForMember(u => u.Name, src => src.MapFrom(x => x.Value));
This will work if your function type is "ExpandoObject".
public EmpClass
{
public string EmpName { get; set; }
public int EmpId { get; set; }
}
this.CreateMap<IDictionary<string, object>, EmpClass>()
.ForMember(dest => dest.EmpName, src => src.MapFrom(x => x["EmpName"]))
.ForMember(dest => dest.EmpId, src => src.MapFrom(x => x["EmpId"]));
Let me know if it helps.
Apparently recent AutoMapper doesn't work with Dictionary<string, string>, must be Dictionary<string, object> without map defined
must be Dictionary<string, object> without CreateMap
Related
I have a List which I would like to populate with different types of objects, trying to do it with object\dynamic, but it doesn't, even when casting.
using asp.net core.
See my code:
public Dictionary<string, Employee> getEmployees(); //This method returns a dictionary of string as a key and Employee as a value.
public Dictionary<string, customer>()> getCustomers(); //same principal
public List<Dictionary<string, object>> getDifferentItems()
{
List<Dictionary<string, object>> listOfItems = new List<Dictionary<string, object>>();
listOfItems.add(getEmployees()); //Error
listOfItems.add(getCustomers()); //Error
return listOfItems;
}
Depending on what you are trying to do, I can see two solutions:
Create a list of TWO different dictionaries
public Dictionary<string, Employee> getEmployees() {
return new Dictionary<string, Employee>();
}
public Dictionary<string, Customer> getCustomers() {
return new Dictionary<string, Customer>();
}
public List<Dictionary<string, object>> getDifferentItems()
{
List<Dictionary<string, object>> listOfItems = new List<Dictionary<string, object>>();
listOfItems.Add(this.getEmployees().ToDictionary(entry => (string)entry.Key,
entry => (object)entry.Value));
listOfItems.Add(this.getCustomers().ToDictionary(entry => (string)entry.Key,
entry => (object)entry.Value));
return listOfItems;
}
Create one dictionary with all the values
public Dictionary<string, Employee> getEmployees() {
return new Dictionary<string, Employee>();
}
public Dictionary<string, Customer> getCustomers() {
return new Dictionary<string, Customer>();
}
public Dictionary<string, object> getDifferentItems()
{
Dictionary<string, object> listOfItems = new Dictionary<string, object>();
foreach (var entry in getEmployees()) {
listOfItems.Add(entry.Key, entry.Value);
}
foreach (var entry in getCustomers()) {
listOfItems.Add(entry.Key, entry.Value);
}
return listOfItems;
}
There are a couple of issues here, both related to variance.
This won't work because List<T>, or any other class in .NET for that matter, does not support variance.
In other words T has to be a specific type, and does not respect inheritance / substitutability as with non-generic types.
Similarly, for Dictionary<TKey, TValue>, TValue is not variant, so you can't simply use object as the value.
IEnumerable<out T> on the other hand is covariant so you could do this:
public IEnumerable<IDictionary> getDifferentItems()
{
yield return getEmployees();
yield return getCustomers();
}
IDictionary is used, as it is the only common ancestor (other than object) to Dictionary<string, Employee> and Dictionary<string, Customer>.
This may satisfy your requirements, but you don't make clear whay you are trying to achive with your getDifferentItems method.
More information on variance can be found here.
I'd personally make an interface, such as IPerson that has all the properties of Employees and Customers, such as Name, Address, ID, etc.
Set up your Customer and employee classes to implement IPerson
Then use a IPerson in your dictionary and you can add to the objects to that.
Here's some code:
public class Employee : IPerson
{
public int ID { get; set; }
public string Name { get; set; }
}
public class Customer : IPerson
{
public int ID { get; set; }
public string Name { get; set; }
}
public interface IPerson
{
int ID { get; set; }
string Name { get; set; }
}
public class Test
{
public void MyTest()
{
List<Dictionary<string, IPerson>> listOfItems = new List<Dictionary<string, IPerson>>();
Dictionary<string, IPerson> myEmployees = new Dictionary<string, IPerson>();
string someString = "blah";
Employee e = new Employee();
e.Name = "Bob";
e.ID = 1;
myEmployees.Add(someString, e);
Dictionary<string, IPerson> myCustomers = new Dictionary<string, IPerson>();
string someOtherString = "blah";
Customer c = new Customer();
c.Name = "Robert";
c.ID = 2;
myCustomers.Add(someOtherString, c);
listOfItems.Add(myEmployees);
listOfItems.Add(myCustomers);
}
}
Here is another solution :
public class Test
{
Dictionary<string, object> listOfItems = new Dictionary<string, object>();
List<Employee> employees = new List<Employee>();
List<customer> customers = new List<customer>();
public Dictionary<string, object> getEmployees()
{
return employees.GroupBy(x => x.name, y => (object)y).ToDictionary(x => x.Key, y => y.FirstOrDefault());
}//This method returns a dictionary of string as a key and Employee as a value.
public Dictionary<string, object> getCustomers()
{
return customers.GroupBy(x => x.name, y => (object)y).ToDictionary(x => x.Key, y => y.FirstOrDefault());
} //same principal
public Dictionary<string, object> getDifferentItems()
{
listOfItems = getEmployees();
listOfItems.Concat(getCustomers());
return listOfItems;
}
}
public class Employee
{
public string name { get;set;}
}
public class customer
{
public string name { get;set;}
}
I am trying to figure out if there is a way to generalize a function that takes a Hashset of two unrelated objects with similar attributes. I have some sample code below:
private IList<IDictionary<string, string>> BuildDictionary(HashSet<ClassA> ClassA)
{
IList<IDictionary<string, string>> data = new List<IDictionary<string, string>>();
foreach (var a in ClassA)
{
Dictionary<string, string> aDictionary = new Dictionary<string, string>();
aDictionary.Add(a.Code, a.Code + "," + a.Name);
data.Add(aDictionary);
}
return data;
}
private IList<IDictionary<string, string>> BuildDictionary(HashSet<ClassB> ClassB)
{
IList<IDictionary<string, string>> data = new List<IDictionary<string, string>>();
foreach (var b in ClassB)
{
Dictionary<string, string> bDictionary = new Dictionary<string, string>();
bDictionary.Add(b.Code, b.Code + "," + b.Name);
data.Add(bDictionary);
}
return data;
}
Thus as evident from the code, the two classes are not related but they both are in a HashSet and contain similar attributes (code, name). I have tried using the generic T but that failed due to the fact that I don't have the generic class T created. Would there be anyway to get around this issue without creating a new class?
If your source classes are sealed or can't be modified to a common interface, you can use accessors for the parts that are needed, as one might do in most LINQ queries.
Here's an example implementation. Note that toKey() and toMemberValue() could be named more appropriately, but this is enough to replicate what you are doing for any class where you can specify a lambda to retrieve the relevant property, and isn't dependent upon the class necessarily having the same property names so long as the lambda is written accordingly. Main() shows what it would look like to use this method for both cases.
public IList<IDictionary<string, string>> BuildDictionary<T>(HashSet<T> sourceSet, Func<T, string> toKey, Func<T, string> toMemberValue)
{
IList<IDictionary<string, string>> data = new List<IDictionary<string, string>>();
foreach (var element in sourceSet)
{
Dictionary<string, string> newLookup = new Dictionary<string, string>();
newLookup.Add(toKey(element), $"{toKey(element)},{toMemberValue(element)}");
data.Add(newLookup);
}
return data;
}
void Main()
{
HashSet<ClassA> setOfAs = new HashSet<ClassA>(new[] { new ClassA { Code = "foo", Name = "bar" }, new ClassA { Code = "foo2", Name = "bar2" } });
HashSet<ClassB> setOfBs = new HashSet<ClassB>(new[] { new ClassB { Code = "foo", Name = "bar" }, new ClassB { Code = "foo2", Name = "bar2" } });
var lookupOfAs = BuildDictionary(setOfAs, x => x.Code, x => x.Name);
var lookupOfBs = BuildDictionary(setOfBs, x => x.Code, x => x.Name);
}
// Define other methods and classes here
public class ClassA
{
public string Code { get; set; }
public string Name { get; set; }
}
public class ClassB
{
public string Code { get; set; }
public string Name { get; set; }
}
If you own the source code to both types you can implement a common interface.
private IList<IDictionary<string, string>> BuildDictionary<T>(HashSet<T> someHashSetOfTs) where T : ICommon
{
IList<IDictionary<string, string>> data = new List<IDictionary<string, string>>();
foreach (var a in someHashSetOfTs)
{
Dictionary<string, string> aDictionary = new Dictionary<string, string>();
aDictionary.Add(a.Code, a.Code + "," + a.Name);
data.Add(aDictionary);
}
return data;
}
Interface definition
public interface ICommon {
string Code {get; }
string Name {get; }
}
And now apply ICommon to both types ClassA and ClassB.
I am new in the dictionary datatype in C#. I got this model item:
public Dictionary<int, string> language { get; set; }
public string languageChoice { get; set; }
Then in my controller I got this:
[Route("{id}/settings")]
[HttpGet]
public async Task<HttpResponseMessage> GetProjectSettings(Guid id)
{
var projectSettings = new ProjectSettings
{
Id = id,
language = new Dictionary<int, string>() {
{1,"English"},
{2,"Spanish"}},
languageChoice = //get language by ID
};
if (projectSettings != null)
{
return Request.CreateResponse<ProjectSettings>(HttpStatusCode.OK, projectSettings);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Project not found");
}
}
I want to give the json object the integer and not the String of the language. How can I do that?
Kind regards
If you don't want to change your existing Dictionary<int, string> then you can just create a new Dictionary<int, int> from it.
var alteredDictionary = languages.ToDictionary(x => x.Key, x => x.Key);
Then do
var projectSettings = new ProjectSettings
{
Id = id,
language = new Dictionary<int, string>() {
{1,"English"},
{2,"Spanish"}},
languageChoice = alteredDictionary[1]// optional .ToString() if you want string
};
Where 1 is the language Id you want.
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 a Dictionary which is of type,
Dictionary<string, string> newdictionary = new Dictionary<string, string>();
newdictionary.Add("12345", "chip1");
newdictionary.Add("23456", "chip2");
Now i have a List which is of type
internal class CustomSerial
{
public string SerialNo { get; set; }
public decimal ecoID { get; set; }
}
var customList = new List<CustomSerial>();
CustomSerial custObj1= new CustomSerial();
custObj1.ecoID =1;
custObj1.SerialNo = "12345";
customList.Add(custObj1);
CustomSerial custObj2 = new CustomSerial();
custObj2.ecoID = 2;
custObj2.SerialNo = "23456";
customList.Add(custObj2);
Now i need to update the Initial dictionary by Filtering the Keys with ther SerialNumber and Replacing the values with the ecoID.
When i try this, it gives
foreach (KeyValuePair<string, string> each in newdictionary)
{
each.Value = customList.Where(t => t.SerialNo == each.Key).Select(t => t.ecoID).ToString();
}
System.Collections.Generic.KeyValuePair.Value' cannot be assigned to -- it is read only
LIN(Q) is a tool to query something not to update it.
However, you can first query what you need to update. For example:
var toUpdate = customList
.Where(c => newdictionary.ContainsKey(c.SerialNo))
.Select(c => new KeyValuePair<string, string>(c.SerialNo, c.ecoID.ToString()));
foreach(var kv in toUpdate)
newdictionary[kv.Key] = kv.Value;
By the way, you get the "KeyValuePair.Value' cannot be assigned to it is read only" exception because aKeyValuePair<TKey, TValue> is a struct which cannot be modified.
You'd have the simplest in this form: though I don't see why you are assigning the same value but the method applies regardless
var dictionary = new Dictionary<string, string>() { { "12345", "chip1" }, { "23456", "chip2" } };
var customList = new List<CustomSerial>() { new CustomSerial() { ecoID = 1, SerialNo = "12345" }, new CustomSerial() { ecoID = 2, SerialNo = "23456" } };
dictionary.Keys.ToList().ForEach(key =>
{
dictionary[key] = customList.FirstOrDefault(c => c.SerialNo == key).SerialNo;
});