How do I change the state of List which is part of view model in extension method so the view model reflects this change without having to re assign the value
Code:
//ViewModel:
//This line should modify the change internally wihtout having to reassign like
// ..Selected =model.CheckBoxList.Selected.RemoveWhere(c => c.SelectedValue == null)
model.CheckBoxList.Selected.RemoveWhere(c => c.SelectedValue == null)
Extension Method :
public static IEnumerable<T> RemoveWhere<T>(this IEnumerable<T> source, Predicate<T> predicate)
{
var x = source.ToList();
x.RemoveAt(1);
source = x;
return source;
}
Update:
public class CheckBoxList
{
public CheckBoxList()
{
//Selected = new List<CheckBoxListViewModel>();
}
[XmlElement("SelectedItem")]
public List<CheckBoxListViewModel> Selected { get; set; }
}
[XmlRoot(ElementName = "MyClass")]
public class CheckBoxListViewModel
{
public string SelectedValue { get; set; }
public string SelectedDescription { get; set; }
}
Assign a parameter has no effect on the expression you pass to the method.
You instead want to mutate the existing instance. To do that, you need to accept a mutable type; namely, List<T>.
List<T> already has a RemoveAll() function which does exactly that, so you don't need to do anything at all.
Related
Sometimes, when handling data transfer objects (for instance retrieved from the database or a csv file), it's nice to write some helper functions to move the data.
For instance:
class MyDto
{
public string Name { get; set; }
}
class MyBusinessObject
{
public string Name { get; set;}
}
I'd like to write something like:
MyDto source;
MyBusinessObject target;
var hasChanged = target.Set(source, source => source.Name, target => target.Name); // lamdba expressions, or whatever it takes to make it work
with the extension method:
public static bool Set<TS, TT, TValue>(this TS source, IGetProperty<TS, TValue> sourceGetProperty, IGetOrSetProperty<TT, TValue> targetGetOrSetProperty)
{
var sourceValue = sourceGetProperty.Invoke(source);
var actualValue = targetGetOrSetProperty.Invoke(target);
if(sourceValue != actualValue)
{
targetGetOrSetPropery.Invoke(target, sourceValue);
return true;
}
return false;
}
I made up the IGetProperty and IGetOrSetProperty. Is it possible to implement them some way without using reflection (so that it's compile-time checked)?
Or is there an elegant way to handle this kind of situation?
EDIT: the example was misleading because the goal wasn't to use an Automapper, but to represent somehow properties as objects. I realize that it's pretty close in fact to the idea of using properties as "ref" for instance, so it's more a language-related question that has always been answered here: Passing properties by reference in C#
It's not exactly possible without reflection, but the expression lambda gives you compile time checking:
public static bool Set<TTarget, TValue>(
this TTarget target,
Expression<Func<TTarget, TValue>> targetProperty,
TValue sourceValue)
{
var actualValue = targetProperty.Compile().Invoke(target);
if (actualValue.Equals(sourceValue))
{
return false;
}
var property = (PropertyInfo)((MemberExpression)targetProperty.Body).Member;
property.SetValue(target, sourceValue);
return true;
}
Usage looks like so:
var hasChanged = target.Set(t => t.Name, source.Name);
Working example: https://dotnetfiddle.net/CJVxIS
Why you should not do this:
targetProperty.Compile() is slow,
Automapper does such mappings for you.
You can consider serialising/deserialising. It may be less efficient (but then again, rudimentary implementation of reflection is expensive too) but it will be syntactically more readable and elegant. The other benefit is, Json.Net is very flexible so you can customise the copy behaviours (e.g map Name property to a property of another name)
class MyDto
{
public string Name { get; set; }
public string Other { get; set; }
public string Remap { get; set; }
}
class MyBusinessObject
{
[JsonIgnore]
public string Other { get; set; }
public string Name { get; set; }
[JsonProperty(PropertyName = "Remap")]
public string RemmapedField { get; set; }
}
public T DeepCopy<T>(object o)
{
string json=JsonConvert.SerializeObject(o);
T newO=JsonConvert.DeserializeObject<T>(json);
return newO;
}
Usage
MyDto source = new MyDto() { Name = "JP", Other = "Something",Remap="R" };
var target = DeepCopy<MyBusinessObject>(source);
Result:
Name: "JP"
Other: null
RemmapedField: "R"
Suppose we have a NodeData class:
public class NodeData<T>
{
public string Name;
public T Value;
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
And a base Node class and child classes that have several properties with type NodaData:
public class Node
{
public List<NodeData<T>> listOutputs<T>()
{
var fieldInfos = GetType().GetFields();
var list = new List<NodeData<T>>();
foreach (var item in fieldInfos)
{
Type t = item.FieldType;
string name = item.Name;
if (t == typeof(NodeData<T>))
{
var output = new NodeData<T>(name, default(T));
list.Add(output);
}
}
return list;
}
}
public class TestNode : Node {
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode ()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
As you can see there is a method which lists all outputs with type T in the Node class So I can find what are the output fields of the child class in runtime:
TestNode node = new TestNode ();
var list = node.listOutputs<int>(); // this returns data
But I need to know how to use this method to list all NodeOutputs of any type T. In this example int and double. Do I need to add a method with this signature public List<NodeData<T>> listOutputs() // should return all properties data, data2, data3. Is it possible to have method like this? return type is generic but there is no type argument for method.
Even after your edit(s) it is not entirely clear what you are trying to achieve but here are my assumptions:
-You want to have some kind of Node object that acts as a container for different types of NodeData elements.
-You want to be able to return one list from this Node object that contains all NodeData elements stored in the Node container, regardless of the NodeData objects' type.
Instead of returning a List> object from the listOutputs methods, just return the non-generic version of the List object. Then you don't have to deal with T in the method call.
The logic that loops through the objects in the non-generic list can then examine the type to process the contained NodeData objects correctly.
Important note: My proposed solution is by no means pretty but I think it answers the question. In my opinion something is already seriously flawed from an OO point of view in the presented code (e.g. use of reflection) and a better solution would have to start by changing the underlying data structures. But that can only be done if we have more information how this is to be used, e.g. what kind of logic consumes the returned list.
You can create a base interface that will be used to return the generic data.
public interface INodeData
{
string Name { get; }
}
public class NodeData<T> : INodeData
{
public string Name { get; private set; }
public T Value { get; private set; }
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
I modified the function to return a list of the interface. Doing this you won't depend on T.
public class Node
{
public List<INodeData> listOutputs()
{
var fieldInfos = GetType().GetFields();
var list = new List<INodeData>();
foreach (var item in fieldInfos)
{
INodeData data = GetType().GetField(item.Name).GetValue(this) as INodeData;
list.Add(data);
}
return list;
}
}
If you test the method, it should return the fields in a list. To work with a specific type, you can make use of is before using the type you search for.
public class TestNode : Node
{
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
private static void Main(string[] args)
{
TestNode node = new TestNode();
var list = node.listOutputs(); // this returns data
}
This may well be an XY problem, in that you probably want to rethink how you are designing your classes because using reflection in this way doesn't seem right. But give the problem you've presented, I'd tackle it like this:
public abstract class NodeDataBase
{
public string Name { get; set; }
public NodeData(string name)
{
this.Name = name;
}
// this isn't actually needed, but might be helpful
public abstract object GetValue();
}
public class NodeData<T> : NodeDataBase
{
public T Value { get; set; }
public NodeData(string name, T value) : base(name)
{
this.Value = value;
}
public override object GetValue()
{
return Value;
}
}
And now your method signature would be:
public List<NodeDataBase> listOutputs()
And with the list returned, you can use the GetValue method to get the actual values without needing to cast to the right generic type to be able to get at the Value property.
You could also just have a return type of List<object>, but then you'll have to cast each member of that list to the right generic type before you can access it's properties.
You can also avoid that nasty reflection code, instead of having data, data1, and data2, you could simply do this in your Node class:
public class Node
{
public List<NodeDataBase> Data { get; protected set; }
public Node()
{
Data = new List<NodeDataBase>();
}
}
And now you don't even need your listOutputs method because you can just get the list from the node (unless you actually wanted a copy, but that's fairly trivial to implement).
And you TestNode would be just:
public class TestNode : Node {
public TestNode ()
{
Data.Add(new NodeData<int>("test", 111));
Data.Add(new NodeData<double>("test", 113));
}
}
I have a list of object and I want to replace one of the objects in the list with the new object:
public Parent AppendParentChildren(Request request)
{
var Children = request.Parent.Children.ToList();
if (Children.Any(x => x.TrackingNumber == request.Child.TrackingNumber))
{
//Here I want to replace any Children that have the same tracking number in the list with the new Child passed in
}
else
{
Children.Add(request.Child);
}
request.Parent.Children = Children;
return request.Parent;
}
public class Request
{
public Parent Parent { get; set; }
public Child Child { get; set; }
}
public class Parent
{
public IEnumerable<Child> Children {get;set;}
}
If I try and use it in a loop:
public static class Extension
{
public static void Update<T>(this List<T> items, T newItem)
{
foreach (var item in items)
{
//this
item = newItem;
}
}
}
item is read only, so I cannot replace the object in the list.
Any suggestions?
You can't change the member of a foreach iteration because foreach implements the IEnumerable type which is read-only.
A solution would be to cast the list of items inside the extension method as a List (which is read-writable). Then you'd want to identify which item(s) in the list you are replacing and update them. Below is what the Update extension method would look like (assuming you're in a situation where you can use LINQ)
public static class Extension
{
public static void Update<T>(this List<T> items, T newItem)
{
var childList = items as List<Child>;
var newChildItem = newItem as Child;
var matches = childList.Where(x => x.TrackingNumber == newChildItem.TrackingNumber).ToList();
matches.ForEach(x => childList[childList.IndexOf(x)] = newChildItem);
}
}
I've put a working example (albeit slightly bloated) on dotnetfiddle
https://dotnetfiddle.net/MJ5svP
It's also worth noting that although it looks like you're altering childList, this is actually referenced back to the original list not creating a copy (more info on this here)
private List<T> newList;
public List<T> NewList
{
get{return newList;}
set{newList = value;}
}
I want to create something like this, but this is won't work. it's just an example to demonstrate my goal as it's pretty common creating proprties for string and int and even T but I've never seen a List property
Is it even possible do such a thing, creating a property for type List ?
EDIT
I have a normal class that has normal properties (string properties, int properties, etc) but I have this property that stores user options, So on the presentation layer I had to convert them into a string so I could be able to store them in the Object. Now is it possible to have a property of type List to store the multivalues in a better and clean way, instead of converting information into one string and then split it and again join it! Thanks Again =D
EDIT2
private List<KeyValuePair<string, string>> _settings;
public List<KeyValuePair<string, string>> MySettings
{
get { return _settings; }
set { _settings = value; }
}
I used the exact code you posted but the property still won't appear in the object's instance, so I tried adding code in the get and set (I wonder why you left them empty or does it means something?) and also added a private variable in the class but still it doesn't appear in the properties of the object's instance!
I hope you could provide the exact code to implement this property and a simple code that assigns or retrieves from/to an instance of this class object
It's the first time to even hear about this KeyValuePair and all the tutorials are pretty simple and not for my case, sorry!
The Last Edit: After a lot of researching and the help of Mark Avenius I found the perfect answer. hope everyone can benefit from this.
NOW! HOW TO CREATE A PROPERTY FOR A LIST :
The Options Class
Public Class Options
{
private string id;
private int option;
public int ID
{
get { return id; }
set { id= value; }
}
public string Option
{
get { return option; }
set { option = value; }
}
}
The Users Class
public class Users
{
private int userId;
private string pass;
private List<Options> userOptions = new List<Options>();
public int ID
{
get { return userId; }
set { user = userId; }
}
public string Pass
{
get { return pass; }
set { pass = value; }
}
public List<Options> OptionsList
{
get { return userOptions; }
set { userOptions = value; }
}
}
The Presentation Layer
Users newUser = new Users ();
Options userOption = new Options ();
userOption.ID = int.Parse(txtBxID.Text);
userOption.Option = txtBxOption.Text;
Item.Options.Add(userOption);
T must be defined within the scope in which you are working. Therefore, what you have posted will work if your class is generic on T:
public class MyClass<T>
{
private List<T> newList;
public List<T> NewList
{
get{return newList;}
set{newList = value;}
}
}
Otherwise, you have to use a defined type.
EDIT: Per #lKashef's request, following is how to have a List property:
private List<int> newList;
public List<int> NewList
{
get{return newList;}
set{newList = value;}
}
This can go within a non-generic class.
Edit 2:
In response to your second question (in your edit), I would not recommend using a list for this type of data handling (if I am understanding you correctly). I would put the user settings in their own class (or struct, if you wish) and have a property of this type on your original class:
public class UserSettings
{
string FirstName { get; set; }
string LastName { get; set; }
// etc.
}
public class MyClass
{
string MyClassProperty1 { get; set; }
// etc.
UserSettings MySettings { get; set; }
}
This way, you have named properties that you can reference instead of an arbitrary index in a list. For example, you can reference MySettings.FirstName as opposed to MySettingsList[0].
Let me know if you have any further questions.
EDIT 3:
For the question in the comments, your property would be like this:
public class MyClass
{
public List<KeyValuePair<string, string>> MySettings { get; set; }
}
EDIT 4: Based on the question's edit 2, following is how I would use this:
public class MyClass
{
// note that this type of property declaration is called an "Automatic Property" and
// it means the same thing as you had written (the private backing variable is used behind the scenes, but you don't see it)
public List<KeyValuePair<string, string> MySettings { get; set; }
}
public class MyConsumingClass
{
public void MyMethod
{
MyClass myClass = new MyClass();
myClass.MySettings = new List<KeyValuePair<string, string>>();
myClass.MySettings.Add(new KeyValuePair<string, string>("SomeKeyValue", "SomeValue"));
// etc.
}
}
You mentioned that "the property still won't appear in the object's instance," and I am not sure what you mean. Does this property not appear in IntelliSense? Are you sure that you have created an instance of MyClass (like myClass.MySettings above), or are you trying to access it like a static property (like MyClass.MySettings)?
Simple and effective alternative:
public class ClassName
{
public List<dynamic> MyProperty { get; set; }
}
or
public class ClassName
{
public List<object> MyProperty { get; set; }
}
For differences see this post: List<Object> vs List<dynamic>
public class MyClass<T>
{
private List<T> list;
public List<T> MyList { get { return list; } set { list = value; } }
}
Then you can do something like
MyClass<int> instance1 = new MyClass<int>();
List<int> integers = instance1.MyList;
MyClass<Person> instance2 = new MyClass<Person>();
IEnumerable<Person> persons = instance2.MyList;
You could do this but the T generic parameter needs to be declared at the containing class:
public class Foo<T>
{
public List<T> NewList { get; set; }
}
It's possible to have a property of type List<T> but your class needs to be passed the T too.
public class ClassName<T>
{
public List<T> MyProperty { get; set; }
}
Either specify the type of T, or if you want to make it generic, you'll need to make the parent class generic.
public class MyClass<T>
{
etc
I'd like to map a paged list of business objects to a paged list of view model objects using something like this:
var listViewModel = _mappingEngine.Map<IPagedList<RequestForQuote>, IPagedList<RequestForQuoteViewModel>>(requestForQuotes);
The paged list implementation is similar to Rob Conery's implementation here:
http://blog.wekeroad.com/2007/12/10/aspnet-mvc-pagedlistt/
How can you setup Automapper to do this?
Using jrummell's answer, I created an extension method that works with Troy Goode's PagedList. It keeps you from having to put so much code everywhere...
public static IPagedList<TDestination> ToMappedPagedList<TSource, TDestination>(this IPagedList<TSource> list)
{
IEnumerable<TDestination> sourceList = Mapper.Map<IEnumerable<TSource>, IEnumerable<TDestination>>(list);
IPagedList<TDestination> pagedResult = new StaticPagedList<TDestination>(sourceList, list.GetMetaData());
return pagedResult;
}
Usage is:
var pagedDepartments = database.Departments.OrderBy(orderBy).ToPagedList(pageNumber, pageSize).ToMappedPagedList<Department, DepartmentViewModel>();
AutoMapper does not support this out of the box, as it doesn't know about any implementation of IPagedList<>. You do however have a couple of options:
Write a custom IObjectMapper, using the existing Array/EnumerableMappers as a guide. This is the way I would go personally.
Write a custom TypeConverter, using:
Mapper
.CreateMap<IPagedList<Foo>, IPagedList<Bar>>()
.ConvertUsing<MyCustomTypeConverter>();
and inside use Mapper.Map to map each element of the list.
If you're using Troy Goode's PageList, there's a StaticPagedList class that can help you map.
// get your original paged list
IPagedList<Foo> pagedFoos = _repository.GetFoos(pageNumber, pageSize);
// map to IEnumerable
IEnumerable<Bar> bars = Mapper.Map<IEnumerable<Bar>>(pagedFoos);
// create an instance of StaticPagedList with the mapped IEnumerable and original IPagedList metadata
IPagedList<Bar> pagedBars = new StaticPagedList<Bar>(bars, pagedFoos.GetMetaData());
I needed to return a serializable version of IPagedList<> with AutoMapper version 6.0.2 that supports the IMapper interface for ASP.NET Web API. So, if the question was how do I support the following:
//Mapping from an enumerable of "foo" to a different enumerable of "bar"...
var listViewModel = _mappingEngine.Map<IPagedList<RequestForQuote>, PagedViewModel<RequestForQuoteViewModel>>(requestForQuotes);
Then one could do this:
Define PagedViewModel<T>
Source: AutoMapper Custom Type Converter not working
public class PagedViewModel<T>
{
public int FirstItemOnPage { get; set; }
public bool HasNextPage { get; set; }
public bool HasPreviousPage { get; set; }
public bool IsFirstPage { get; set; }
public bool IsLastPage { get; set; }
public int LastItemOnPage { get; set; }
public int PageCount { get; set; }
public int PageNumber { get; set; }
public int PageSize { get; set; }
public int TotalItemCount { get; set; }
public IEnumerable<T> Subset { get; set; }
}
Write open generic converter from IPagedList<T> to PagedViewModel<T>
Source: https://github.com/AutoMapper/AutoMapper/wiki/Open-Generics
public class Converter<TSource, TDestination> : ITypeConverter<IPagedList<TSource>, PagedViewModel<TDestination>>
{
public PagedViewModel<TDestination> Convert(IPagedList<TSource> source, PagedViewModel<TDestination> destination, ResolutionContext context)
{
return new PagedViewModel<TDestination>()
{
FirstItemOnPage = source.FirstItemOnPage,
HasNextPage = source.HasNextPage,
HasPreviousPage = source.HasPreviousPage,
IsFirstPage = source.IsFirstPage,
IsLastPage = source.IsLastPage,
LastItemOnPage = source.LastItemOnPage,
PageCount = source.PageCount,
PageNumber = source.PageNumber,
PageSize = source.PageSize,
TotalItemCount = source.TotalItemCount,
Subset = context.Mapper.Map<IEnumerable<TSource>, IEnumerable<TDestination>>(source) //User mapper to go from "foo" to "bar"
};
}
}
Configure mapper
new MapperConfiguration(cfg =>
{
cfg.CreateMap<RequestForQuote, RequestForQuoteViewModel>();//Define each object you need to map
cfg.CreateMap(typeof(IPagedList<>), typeof(PagedViewModel<>)).ConvertUsing(typeof(Converter<,>)); //Define open generic mapping
});
AutoMapper automatically handles conversions between several types of lists and arrays:
http://automapper.codeplex.com/wikipage?title=Lists%20and%20Arrays
It doesn't appear to automatically convert custom types of lists inherited from IList, but a work around could be:
var pagedListOfRequestForQuote = new PagedList<RequestForQuoteViewModel>(
AutoMapper.Mapper.Map<List<RequestForQuote>, List<RequestForQuoteViewModel>>(((List<RequestForQuote>)requestForQuotes),
page ?? 1,
pageSize
I created a little wrapper around AutoMapper to map PagedList<DomainModel> to PagedList<ViewModel>.
public class MappingService : IMappingService
{
public static Func<object, Type, Type, object> AutoMap = (a, b, c) =>
{
throw new InvalidOperationException(
"The Mapping function must be set on the MappingService class");
};
public PagedList<TDestinationElement> MapToViewModelPagedList<TSourceElement, TDestinationElement>(PagedList<TSourceElement> model)
{
var mappedList = MapPagedListElements<TSourceElement, TDestinationElement>(model);
var index = model.PagerInfo.PageIndex;
var pageSize = model.PagerInfo.PageSize;
var totalCount = model.PagerInfo.TotalCount;
return new PagedList<TDestinationElement>(mappedList, index, pageSize, totalCount);
}
public object Map<TSource, TDestination>(TSource model)
{
return AutoMap(model, typeof(TSource), typeof(TDestination));
}
public object Map(object source, Type sourceType, Type destinationType)
{
if (source is IPagedList)
{
throw new NotSupportedException(
"Parameter source of type IPagedList is not supported. Please use MapToViewModelPagedList instead");
}
if (source is IEnumerable)
{
IEnumerable<object> input = ((IEnumerable)source).OfType<object>();
Array a = Array.CreateInstance(destinationType.GetElementType(), input.Count());
int index = 0;
foreach (object data in input)
{
a.SetValue(AutoMap(data, data.GetType(), destinationType.GetElementType()), index);
index++;
}
return a;
}
return AutoMap(source, sourceType, destinationType);
}
private static IEnumerable<TDestinationElement> MapPagedListElements<TSourceElement, TDestinationElement>(IEnumerable<TSourceElement> model)
{
return model.Select(element => AutoMap(element, typeof(TSourceElement), typeof(TDestinationElement))).OfType<TDestinationElement>();
}
}
Usage:
PagedList<Article> pagedlist = repository.GetPagedList(page, pageSize);
mappingService.MapToViewModelPagedList<Article, ArticleViewModel>(pagedList);
It is important that you would have to use the element types!
If you have any question or suggestions, please feel free to comment :)
It is easy with Automapper .net core 8.1.1
You just need to add type map to your mapperProfile and mapping the object inside pagedList
CreateMap(typeof(IPagedList<>), typeof(IPagedList<>));
CreateMap<RequestForQuote, RequestForQuoteViewModel>().ReverseMap();
It also can be and abstract class like PagedList. It is not related with class/ınterface type
And you can use it directly in mapper.Map - Initialize mapper from IMapper in the class constructor
RequestForQuote result
_mapper.Map<IPagedList<RequestForQuoteViewModel>>(result);
If you are using X.PageList then you can simply use this code:
PagedList<exampleDTO> result = new PagedList<exampleDTO>(item, _mapper.Map<List<exampleDTO>>(item.ToList()));
The PageList lets you to create new PageList with modified items.
more information