C# Get Class Object List from ViewModel - c#

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.

Related

How to map a property in C# that is a list?

These are my classes:
public class Registration
{
public bool? IsRegistered { get; set; }
public List<RegistrationProcess> RegistrationProcess { get; set; }
}
public class RegistrationProcess
{
public bool? PaidInFull { get; set; }
public double PaymentAmount { get; set; }
public bool IdentityVerified { get; set; }
}
I have a method that is doing the object mapping like this:
public Registration Translate(Services.Registration source)
{
return new Registration
{
IsRegistered = source.IsRegistered,
RegistrationProcess = new List<RegistrationProcess>
{
new RegistrationProcess()
{
PaidInFull = source.RegistrationProcess.Select(o => o.HasPaid),
}
}
};
}
I am not sure how to set up the mapping for the RegistrationProcess.
I want to map PaidInFull within RegistrationProcess to the property HasPaid. They are both bools.
I am getting an error: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<bool?>' to 'bool?'
I feel like I need to add something to the end of the Select statement but I am not sure what. I did FirstOrDefault() and that made the error go away but I only got one value back and that is not what I want.
The problem with your approach is that you are only creating one instance of RegistrationProcess inside the list constructor. So by calling source.RegistrationProcess.Select(o => o.HasPaid) and assign it to your newly created RegistrationProcess you are creating a Collection of all bool values of your service registration process and try to assign it to a single registration process.
The Solution is to create multiple RegistrationProcess instances. In fact, one for each element in source.RegistrationProcess. To do this you can use the Select method on source.RegistrationProcess directly:
source.RegistrationProcesses.Select(x => new RegistrationProcess() { PaidInFull = x.HasPaid }).ToList()
As you can see, for every element in source.RegistrationProcesses a new RegistrationProcess is created. Or in other words: you select the elements of source.RegistrationProcesses as new RegistrationProcess() { PaidInFull = x.HasPaid } if that makes more sense to you.
The .ToList() converts the IEnumerable to a list.

List has invalid arguments

I have following classes:
public class Selections
{
public List<Selection> selection { get; set; }
}
public class Selection
{
public Promotion promotion { get; set; }
public Products products { get; set; }
}
public class Products
{
public List<int> productId { get; set; }
}
I am creating List and assigning property values but when I am adding the list I'm getting error:
The best overloaded method match for
'System.Collections.Generic.List.Add(Selection)' has some
invalid arguments
C# code:
Selections productSelections = new Selections();
List<Selection> listOfProductSelections = new List<Selection>();
Selection dataSelection = new Selection()
{
promotion = new ProviderModels.Common.Promotion()
{
promotionID = Convert.ToInt32(applicablePromotion.PromotionId),
lineOfBusiness = applicablePromotion.LineOfBusiness
},
products = new ProviderModels.Common.Products()
{
productId = GetIdsOfSelectedProducts(context, selectedOffer)
}
};
productSelections.selection.Add(listOfProductSelections);
Am I missing something?
You are adding a list to another list. You want to add the list items.
Instead of
productSelections.selection.Add(listOfProductSelections);
write
productSelections.selection.AddRange(listOfProductSelections);
But you have to be sure you have initialized the selection property at that point, otherwise you'll run into a NullReferenceException.
By the way, check all your error messages. You will see a second message telling you which type is excpected and what you were using.
you should use AddRange as listOfProductSelections is a list.
productSelections.selection.AddRange(listOfProductSelections)
productSelections.selection is a reference to a List, consquently when you try to add an item to it (last line of your example) the Add method expects a parameter of type of Selection - you're passing listOfProductSelections which is a reference to another list.
Maybe you wanted to add dataSelection which is of the required type? If not, you can use AddRange as the other respondents have suggested.

How to ignore a property based on a runtime condition?

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"));

c# add object array to ICollection - convert dto -> model

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!

How to Cast List<T> To List<ClassName>

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();

Categories

Resources