I need to convert this DTO
public class MyDTO
{
[JsonProperty("studentInfo")]
public StudentInfo studentInfo {get; set; }
public class StudentInfo
{
[JsonProperty("others")]
public ICollection<AnotherDTO[]> Others { get; set; }
}
public class AnotherDTO
{
[JsonProperty("name")]
public string name { get; set; }
}
}
to this model
public class MyModel
{
public StudentInfo studentInfo {get; set; }
public class StudentInfo
{
public ICollection<Another[]> Others { get; set; }
}
public class Another
{
public string name { get; set; }
}
}
I am getting hung up on the ICollection. Here is the bit where I am trying to populate ICollection Others.
private static ICollection<MyModel.Another[]> getAnothers(MyDTO myDTO, MyModel myModel)
{
List<MyModel.Another> myList = new List<MyModel.Another>();
foreach(var x in myDTO.studentInfo.Others)
{
foreach(var y in x)
{
myList.Add(new MyModel.Another
{
name = y.name
});
}
}
}
MyModel.Another[] newList;
newList = myList.ToArray();
ICollection<MyDTO.AnotherDTO[]> ic = (ICollection<MyModel.Another[]>newList.Cast<MyModel.Another[]>().ToList();
This last line is giving me the following error:
Unable to cast object of type MyModel to type MyModel[].
I would appreciate any help.
Instead of manually mapping the object, I would suggest to use AutoMapper. AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another. You can also get Nuget package for AutoMapper
Just define the mapping for the objects -
Mapper.CreateMap<MyDTO, MyModel>();
..and simply map the object like this -
var myModelObject = Mapper.Map<MyModel>(myDtoObject);
Please refer this getting started link.
You can use a select statement to convert the collection.
var myDto = new MyDto
{
studentInfo = new MyModel.StudentInfo
{
Others = new List<AnotherDTO[]>
{
new[]
{
new AnotherDTO { Name = "a" },
new AnotherDTO { Name = "b" }
},
new[]
{
new AnotherDTO { Name = "x" },
new AnotherDTO { Name = "y" }
}
}
}
var model = new MyModel
{
studentInfo = new MyModel.StudentInfo
{
Others = dto.StudentInfo.Others
.Select(otherDtoArray =>
otherDtoArray
.Select(otherDto => new MyModel.Other {Name = otherDto.Name})).ToList()
}
Thanks for your comments everyone. My main problem was trying to convert the ICollection.
I ended up changing ICollection in my model to simply List. And it is working out nicely. I am not able to Use AutoMapper as was suggested, but I do see the value of it for case like mine.
There's all kinds of wrong in your code.
foreach(var x in myDTO.studentInfo.Others)
{
foreach(var y in x)
{
myList.Add(new MyModel.Another
{
name = y.name
});
}
Why do you need the 2nd foreach loop ? Your first foreach loop is already looping through your Others (type: ICollection). Each "x" is an instance of AnotherDTO.
Secondly, the following is completely wrong:
MyModel.Another[] newList;
newList = myList.ToArray();
ICollection<MyDTO.AnotherDTO[]> ic = (ICollection<MyModel.Another[]>newList.Cast<MyModel.Another[]>().ToList();
MyList is already a list. You're then trying to cast it as an Array and then back to a list. There's no point in doing this.
Also, you might as well just not use ICollection at all. It's heavy for nothing. Just use List.
Also don't name variables with simple letters 'a', 'b', 'x', 'y', etc... You're going to get flamed by your colleagues once you're on the job market.
Here's the corrected version of your code:
public class SomeClass
{
public static List<Another> MapAnotherDtoToAnother(List<AnotherDTO> others)
{
List<Another> anotherList = new List<Another>();
foreach(var anotherDto in others)
{
Another another = new Another();
/* Populate fields of `another` whatever they are from `anotherDto` */
another.FirstName = anotherDto.FirstName;
anotherList.Add(another);
}
return anotherList;
}
}
To convert an ICollection to List just do:
/* Convert your ICollection to a List */
List<AnotherDTO> AnotherDTOList = myDto.StudentInfo.Others.Cast<AnotherDTO>().ToList()
/* Use your static function get your non-dto counterpart */
List<Another> anothers = SomeClass.MapAnotherDtoToAnother(AnotherDTOList);
Hope it helps.
Good luck!
Related
Dictionary shoppingList = new Dictionary <string, int>()
{
{"Eggs",200 },
{"Milk",200},
{"Fish",400},
{"Apples",150}
};
Dictionary does not have a concept of "first", and indeed is free to re-order things. Now you can get a .First() item, but it's only guaranteed for that enumeration, and the order might not be the order you expect or want. That said, I'm not aware of a specific case .Net will actually do this; just be aware it's not part of the "contract" for the type.
Really, it seems like what you have here is more of a List<ShoppingItem> (it's even in the name of the variable!), where ShoppingItem is a class with properties for Name and Price.
To be more predictable about retrieving the first item, it might be helpful to create a class called ShoppingItem and then use a List instead of a Dictionary:
public class ShoppingItem
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Create a List using your new ShoppingItem type.
List<ShoppingItem> shoppingList = new List<ShoppingItem>();
Populate your list:
shoppingList.Add(
new ShoppingItem
{
Id = 1,
Name = "Eggs",
Price = 200
});
shoppingList.Add(
new ShoppingItem
{
Id = 2,
Name = "Applies",
Price = 150
});
Then, finding the first in the sequence can be achieved in a number of ways:
var firstUsingFirst = shoppingList.First();
var firstUsingIndex = shoppingList[0];
var firstLowestId = shoppingList.Find(item => item.Id == (shoppingList.Min(e => e.Id)));
Each method returns a ShoppingItem object, so get the values of the class properties like:
int firstId = firstUsingFirst.Id
Lots of examples in the documentation here.
I have data viewModel with two class object.
public class StudentViewModel
{
public PeopleEntity People { get; set; }
public PeopleUnitEntity PeopleUnit { get; set; }
}
In following code I get values for both PeopleEntity and PeopleUnitEntity
IList<StudentViewModel> _stv = _StudentServicesObject.GetStudentByPersonCode(PersonCode);
I want to store only People List in new PeopleEntity Object, how can I do that???
I tried as following but it doesn't work for me
IList<PeopleEntity> _People = _StudentServicesObject.GetStudentByPersonCode(PersonCode).Select(pl => new PeopleEntity { People = pl });
Error
Wouldn't it just be:
IList<PeopleEntity> _People = _StudentServicesObject.GetStudentByPersonCode(PersonCode).Select(pl => pl.People).ToList();
???
Where returns an IEnumerable, not an IList so you need to either make _stv an IEnumerable or convert the result into a List.
I have a simple pair of classes which for I've set up a mapping at initialization time.
public class Order {
public int ID { get; set; }
public string Foo { get; set; }
}
public class OrderDTO {
public int ID { get; set; }
public string Foo { get; set; }
}
...
Mapper.CreateMap<Order, OrderDTO>();
Now at a certain point I need to map an Order to an OrderDTO. BUT depending on some circumstances, I might need to ignore Foo during mapping. Let's also assume that I cannot "store" the condition in the source or destination object.
I know how I can configure the ignored properties at initialization time, but I have no idea how I could achieve such a dynamic runtime behavior.
Any help would be appreciated.
UPDATE
My use case for this behaviour is something like this. I have an ASP.NET MVC web grid view which displays a list of OrderDTOs. The users can edit the cell values individually. The grid view sends the edited data back to the server like a collection of OrderDTOs, BUT only the edited field values are set, the others are left as default. It also sends data about which fields are edited for each primary key. Now from this special scenario I need to map these "half-empty" objects to Orders, but of course, skip those properties which were not edited for each object.
The other way would be to do the manual mapping, or use Reflection somehow, but I was just thinking about if I could use AutoMapper in some way.
I've digged into the AutoMapper source code and samples, and found that there is a way to pass runtime parameters at mapping time.
A quick example setup and usage looks like this.
public class Order {
public int ID { get; set; }
public string Foo { get; set; }
}
public class OrderDTO {
public int ID { get; set; }
public string Foo { get; set; }
}
...
Mapper.CreateMap<Order, OrderDTO>()
.ForMember(e => e.Foo, o => o.Condition((ResolutionContext c) => !c.Options.Items.ContainsKey("IWantToSkipFoo")));
...
var target = new Order();
target.ID = 2;
target.Foo = "This should not change";
var source = new OrderDTO();
source.ID = 10;
source.Foo = "This won't be mapped";
Mapper.Map(source, target, opts => { opts.Items["IWantToSkipFoo"] = true; });
Assert.AreEqual(target.ID, 10);
Assert.AreEqual(target.Foo, "This should not change");
In fact this looks quite "technical", but I still think there are quite many use cases when this is really helpful. If this logic is generalized according to application needs, and wrapped into some extension methods for example, then it could be much cleaner.
Expanding on BlackjacketMack's comment for others:
In your MappingProfile, add a ForAllMaps(...) call to your constructor.
using AutoMapper;
using System.Collections.Generic;
using System.Linq;
public class MappingProfile : Profile
{
public MappingProfile()
{
ForAllMaps((typeMap, mappingExpression) =>
{
mappingExpression.ForAllMembers(memberOptions =>
{
memberOptions.Condition((o1, o2, o3, o4, resolutionContext) =>
{
var name = memberOptions.DestinationMember.Name;
if (resolutionContext.Items.TryGetValue(MemberExclusionKey, out object exclusions))
{
if (((IEnumerable<string>)exclusions).Contains(name))
{
return false;
}
}
return true;
});
});
});
}
public static string MemberExclusionKey { get; } = "exclude";
}
Then, for ease of use, add the following class to create an extension method for yourself.
public static class IMappingOperationOptionsExtensions
{
public static void ExcludeMembers(this AutoMapper.IMappingOperationOptions options, params string[] members)
{
options.Items[MappingProfile.MemberExclusionKey] = members;
}
}
Finally, tie it all together: var target = mapper.Map<Order>(source, opts => opts.ExcludeMembers("Foo"));
I am using generic method to fill my dropdown for all types
below is my code.
the entity type are as follow
public class Role
{
public string Id { get; set; }
public string Name { get; set; }
}
public class DropDown
{
public string Id { get; set; }
public string Name { get; set; }
}
i am able to fetch data successfully at
var data = DataFetcher.FetchData<T>();
private static void Main( string[] args )
{
List<DropDown> cities = BLL.GetDataList<City>();
List<DropDown> states = BLL.GetDataList<State>();
List<DropDown> roles = BLL.GetDataList<Role>();
}
public static class BLL
{
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data as List<DropDown>;
}
}
I knew this cast data as List<DropDown> will fail,thats why its returning null back to calling method,
How can i cast Generic list to List of Known Type?
You have to ask yourself: how do I want to convert T to DropDown? If you can't answer this, the answer is: you can't.
I guess your DropDown class has an object Value property, that holds the dropdown value, and you wish to assign the data entity to that property.
Then you can project the list of data entities to DropDowns as such:
var data = DataFetcher.FetchData<T>();
return data.Select(d => new DropDown { Value = d }).ToList();
As for your edit: so you have at least one type, the displayed Role, that has an Id and Name property. But type T doesn't guarantee this, so you'd need to introduce an interface:
public interface INamedIdentifyableEntity
{
string Id { get; set; }
string Name { get; set; }
}
And apply this to your entities. Then introduce it as a generic constraint and do the mapping:
return data.Select(d => new DropDown
{
Id = d.Id,
Name = d.Name,
}).ToList();
But you don't want this, as here you are tying these two properties to dropdowns. Tomorrow you'll want an entity with Code instead of Id and Text instead of Name, so you'll have to add more interfaces, more overloads, and so on.
Instead you might want to use reflection, where you can specify the member names in the call:
List<DropDown> cities = BLL.GetDataList<City>(valueMember: c => c.CityCode, displayMember: c => c.FullCityname);
And use these member expressions to look up data's values and fill those into the DropDown.
However, you're then reinventing the wheel. Leave out your DropDown class entirely, and leave the dropdown generation to the front end, in this case MVC:
var cities = DataFetcher.FetchData<City>();
var selectList = new SelectList(cities.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCityId),
Text = c.FullCityName,
Value = c.CityCode,
});
Or:
var selectList = new SelectList(cities, "CityCode" , "FullCityName", selectedCityId);
One solution is to use AutoMapper.
First create a map between your models like this:
AutoMapper.Mapper.CreateMap<Role, DropDown>();
Do the same thing for City and State classes if you need to.
Then you can use AutpMapper to convert your objects to DropDown like this:
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data.Select(x => AutoMapper.Mapper.Map<DropDown>(x)).ToList();
}
If I understood the question correctly, you could use Linq as follows.
return data.Cast<DropDown>().ToList();
I'm brand new to MongoDB in C#.
I have created a very simple class which I would like to automatically insert into a collection.
How do I do that, if I don't want to map everything manually?
public class DummyClass
{
[BsonId]
public int Id { set; get; }
[BsonElement("first")]
public string First { set { _name = value; } }
[BsonConstructor]
public DummyClass()
{
Id = 2;
First = "1";
}
}
I had hoped I could do something like this:
_dbClient = new MongoClient();
_database = _dbClient.GetDatabase("testDB");
_collection = _database.GetCollection<BsonDocument>("Collection");
var doc = BsonDocument.Create(dummy);
_collection.InsertOneAsync(doc);
But it's no good. I get the exception:
System.ArgumentException : .NET type DummyClass cannot be mapped to BsonType.Document.
Parameter name: value
Any suggestions?
And I really don't want to do:
{
{"Id", "2"},
{"First", "1"},
}
EDIT:
I forgot this small line:
BsonClassMap.RegisterClassMap<DummyClass>();
It does wonders.
It makes sense to use BsonDocument when fields in collections don't match properties in your model class. Otherwise you should create collection mapped to your class.
_collection = _database.GetCollection<DummyClass>("Collection");
await _collection.InsertOneAsync(doc);
And don't forget about async/await methods in MongoDB.Driver.
You can use ToBsonDocument()
_collection.InsertOneAsync(doc.ToBsonDocument())