Would you help me understand why before exiting the Main function the repository.Courses is null, even though I had just added a course to the repository by calling repository.AddCourse(course); before exiting? How would I correct this? I think it may have to do with how the Courses property is defined (getter and setter). In teh getter I want to initialize by an empty list only if it's already null, otherwise I want to return the existing list. In the setter I want to assign a value - this should be correlated with the possibility of adding to list.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Repo
{
public IList<Course> _courses;
public IList<Course> Courses
{
get
{
if (_courses == null)
_courses = new List<Course>();
return new List<Course>(_courses);
}
set
{
_courses = value;
}
}
public void AddCourse(Course course)
{
course.Id = Courses.NextId();
Courses.Add(course);
}
static void Main(string[] args)
{
Repo repository = new Repo();
var path = "C:\\a1\\demos\\demo1-after\\ConsoleApplication1\\json1.json";
var reader = new StreamReader(path);
string text = reader.ReadToEnd();
var course = JsonConvert.DeserializeObject<Course>(text);
repository.AddCourse(course);
}
}
public class Course : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public Course(string n, string a)
{
Name = n;
Author = a;
}
}
public interface IEntity
{
int Id { get; set; }
}
public static class Extensions
{
public static int NextId<T>(this IList<T> list) where T : IEntity
{
return list.Any() ? list.Max(x => x.Id) + 1 : 0;
}
}
}
The problem here is that your get is always returning a new list, rather than the backing field.
Try this instead:
get
{
if (_courses == null) _courses = new List<Course>();
return _courses;
}
I noticed that the getter of Courses returns a new list rather than a reference to the field. Is that because you don't want anyone from outside the class to be able to add anything to the list except through AddCourse?
In this case the fix I'd recommend is to change AddCourse to address the field directly rather than the property, as currently it's adding the item to the new list created by the getter rather than to the list referenced by your field.
Related
Is it possible to map from a source-object to a specific implementation of a target object using automapper?
In the following code-sample, I want to map from SourceItem to TargetSampleItem and not to TargetBaseItem. Important: TargetBaseItem can't be abstract. But if I use the following mapper-configuration, always TargetBaseItem is used.
To summarize things up, the following collection should contain items of type TargetSampleItem, which is derriving from TargetBaseItem:
public IList<TargetItemBase> Items { get; set; } = new List<TargetItemBase>();
Complete code
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MapperTest
{
class Program
{
static void Main(string[] args)
{
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SourceRoot, TargetRoot>();
cfg.CreateMap<SourceItem, TargetItemBase>()
.IncludeAllDerived();
});
configuration.AssertConfigurationIsValid();
var mapper = configuration.CreateMapper();
var source = new SourceRoot
{
Id = 1,
Items = new List<SourceItem>
{
new SourceItem { A = "a1", B = "b1" },
new SourceItem { A = "a2", B = "b2" }
}
};
var result = mapper.Map<TargetRoot>(source);
// Should retur true
Console.WriteLine(result.Items.First() is TargetSampleItem);
Console.ReadLine();
}
/// <summary>
/// Source model
/// </summary>
public class SourceRoot
{
public int Id { get; set; }
public IList<SourceItem> Items { get; set; } = new List<SourceItem>();
}
public class SourceItem
{
public string A { get; set; }
public string B { get; set; }
}
/// <summary>
/// Target model
/// </summary>
public class TargetRoot
{
public int Id { get; set; }
public IList<TargetItemBase> Items { get; set; } = new List<TargetItemBase>();
}
public class TargetItemBase
{
public string A { get; set; }
}
public class TargetSampleItem : TargetItemBase
{
public string B { get; set; }
}
}
}
EDIT
using As<> is not working, because than AutoMapper is not mapping to the type, rather than just casting it:
cfg.CreateMap<SourceItem, TargetItemBase>()
.As<TargetSampleItem>();
EDIT 2/Solution
Using As<> is working, if a map between SourceItem and TargetSampleItem is added too:
cfg.CreateMap<SourceItem, TargetItemBase>().As<TargetSampleItem>();
cfg.CreateMap<SourceItem, TargetSampleItem>();
As does work for me:
cfg.CreateMap<SourceItem, TargetItemBase>().As<TargetSampleItem>();
cfg.CreateMap<SourceItem, TargetSampleItem>();
If As<> doesn't work for you, then a possible solution might be using AfterMap like -
CreateMap<SourceRoot, TargetRoot>()
.AfterMap((s, d) =>
{
s.Items.ToList().ForEach(p => d.TargetItems.Add(new TargetSampleItem { A = p.A, B = p.B }));
});
(Its not an elegant solution, but since TargetSampleItem is not the target of any of your maps, I don't see any reason why AutoMapper would create an instance of it).
You have to rename either of the Items properties so that AutoMapper doesn't try to map them automatically (I renamed the one in TargetRoot class to TargetItems). And of course you don't need the mapping between SourceItem and TargetItemBase.
I need to fill a list with specific data. This list is a property of another object. The elements of that List have the following rules:
They contain a int-Property named "Id" which is writable
They have a constructor without parameters
This is the code so far: (I did not add any plausiblity checks or error handling to keep this example simple)
private void SetListProperty(string propertyName, object target, int[] ids) {
PropertyInfo property=target.GetProperty(propertyName);
Type propertyType=property.PropertyType();
Type elementType=propertyType.GetElementType();
PropertyInfo elementId=elementType.GetProperty("Id");
var targetList=new List<>(elementType); // PseudoCode. Does not work
foreach (int id in ids) {
var element=Activator.CreateInstance(elementType);
elementId.SetValue(element, id);
targetList.Add(element); // PseudoCode. Does not work
}
property.SetValue(target, targetList);
}
Example for calling that method:
public class User {
public int Id {get;set;}
public string Name {get;set;}
}
public class House {
public int Id {get;set;}
public string HouseName {get;set;]
}
public class Group {
public string Name {get;set;}
public List<User> Users {get;set;}
public List<House> Houses {get;set;}
}
var group=new Group { Name="nerds"};
SetListProperty("Users"), group, new int[] {1,2,3,4,5});
SetListProperty("Houses"), group, new int[] {1,2,3,4,5});
So after calling that method, group should contain a property Users that has 5 elements each with the Id set.
I've seen similar questions here about creating a List of an unknown Type, but not how to actually add single items of an unknown type to that list.
(Update)
I assume my problem was not clear enough in the beginning. Inside that method I do NOT know the property Type. Not even if it is a list.
I only have the property name as string.
I scrapped the original answer (below) to truly address doing this without knowing the type of property OTHER than that it is either an item that has an id...or it is an enumerable of objects that have an Id.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace StackOverflowTests
{
[TestClass]
public class StackOverflow_49181925Tests
{
public interface IHasId
{
int Id { get; set; }
}
public class Foo : IHasId
{
public int Id { get; set; }
}
public class HasAFoo
{
public Foo Foo { get; set; }
}
public class HasManyFoos
{
public IEnumerable<Foo> Foos { get; set; }
}
public void SetPropertyIds(object target, string propertyName, IEnumerable<int> ids)
{
var property = target.GetType().GetProperty(propertyName);
var propertyType = property.PropertyType;
//is it enumerable?
if (typeof(IEnumerable).IsAssignableFrom(propertyType))
{
var objectType = propertyType.GetGenericArguments().First();
var list = Activator.CreateInstance(typeof(List<>).MakeGenericType(objectType)) as IList;
foreach(var id in ids)
{
var obj = Activator.CreateInstance(objectType) as IHasId;
((IHasId)obj).Id = id;
list.Add(obj);
}
property.SetValue(target, list);
}
else
{
if(ids.Count() != 1) throw new ApplicationException("You're trying to set multiple Ids to a single property.");
var objectType = propertyType;
var obj = Activator.CreateInstance(objectType);
((IHasId)obj).Id = ids.First();
property.SetValue(target, obj);
}
}
[TestMethod]
public void TestHasAFoo()
{
var target = new HasAFoo();
this.SetPropertyIds(target, "Foo", new[] { 1 });
Assert.AreEqual(target.Foo.Id, 1);
}
[TestMethod]
public void TestHasManyFoos()
{
var target = new HasManyFoos();
this.SetPropertyIds(target, "Foos", new[] { 1, 2 });
Assert.AreEqual(target.Foos.ElementAt(0).Id, 1);
Assert.AreEqual(target.Foos.ElementAt(1).Id, 2);
}
}
}
ORIGINAL ANSWER BELOW
Lots of different ways of accomplishing this. The third way which implements an interface can give you a lot of help since most of your business models probably have some sort of Id. The Linq version though is short and sweet. These are all just variations on a theme.
Using Linq:
group.Users = new[]{1,2,3}.Select(s=>new User{Id = s}).ToList();
Using Generics:
private IList<T> IntsToModels<T>(IEnumerable<int> ints, Action<T,int> setId) where T : new(){
return ints.Select(s=>{
var t = new T();
setId(t,s);
return t;
}).ToList();
}
Using Generics and Interfaces
public interface IHasID{
int Id{get;set;}
}
//implement the IHasID interface
public class User : IHasID...
private IEnumerable<T> ToModels(IEnumerable<int> ints) where T : IHasID, new(){
foreach(var i in ints){
yield return new T{Id = i};
}
}
I'm an intern with very basic knowledge of ASP and C#.
I'm trying to display a list of projects > maps > thememaps in an application I'm working on in ASP.NET MVC. In my third foreach I get an error saying: "Does not contain a definition for "ThemeMaps" and no extension method "ThemeMaps" accepting a first argument of type could be found".
I'm confused as to why vmProject.Maps does not contain the property ThemeMaps. I instantiated that list just like maps. What am I doing wrong?
LayersController.cs
// Create viewmodel object
var viewModel = new AddLayerToThemeMapViewModel();
// Create Project list
viewModel.Projects = new List<AddProject>();
// Loop over all maps
List<Project> projects = this.applicationDb.Projects.OrderBy(e => e.Title).ToList();
foreach (var project in projects)
{
// Create map
var vmProject = new AddProject()
{
ProjectId = project.ProjectID,
ProjectTitle = project.Title,
Maps = new List<AddLayerMap>(),
};
foreach (var map in project.Maps.OrderBy(e => e.Title))
{
// Create map
vmProject.Maps.Add(new AddLayerMap()
{
MapId = map.MapId,
MapTitle = map.Title,
ThemeMaps = new List<AddLayerMapThemeMap>(),
});
// Loop over all thememaps in map
foreach (var thememap in map.ThemeMaps.OrderBy(e => e.Order))
{
vmProject.Maps.ThemeMaps.Add(new AddLayerMapThemeMap()
{
ThemeMapId = thememap.ThemeMapId,
ThemeMapTitle = thememap.Title,
});
}
}
// Add map to list
viewModel.Projects.Add(vmProject);
}
My viewmodel class
using Mapgear.MapViewer.Entities;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Mapgear.MapViewer.ViewModels
{
public class AddLayerToThemeMapViewModel
{
public Guid LayerId { get; set; }
public List<AddProject> Projects { get; set; }
}
public class AddProject
{
public Guid ProjectId { get; set; }
public string ProjectTitle { get; set; }
public List<AddLayerMap> Maps { get; set; }
}
public class AddLayerMap
{
public Guid MapId { get; set; }
public string MapTitle { get; set; }
public List<AddLayerMapThemeMap> ThemeMaps { get; set; }
}
public class AddLayerMapThemeMap
{
public Guid ThemeMapId { get; set; }
public string ThemeMapTitle { get; set; }
}
}
I made a scetch before I started on paper, which looks like the following:
LayerId
List project
ProjectId
ProjectTitle
List Map
MapId
MapTitle
List ThemeMap
ThemeMapId
ThemeMapTitle
I know my class names are a bit out of wack, however I din't write them myself. Gonna optimize them after.
PS: This is my first question on StackOverflow!
I modified the code in the second for-each loop a bit. I added a variable for the newly created map and add the thememaps to this created map. Check if it's correct.
var newMap = new AddLayerMap()
{
MapId = map.MapId,
MapTitle = map.Title,
ThemeMaps = new List<AddLayerMapThemeMap>(),
};
// Create map
vmProject.Maps.Add(newMap);
// Loop over all thememaps in map
foreach (var thememap in map.ThemeMaps.OrderBy(e => e.Order))
{
newMap.ThemeMaps.Add(new AddLayerMapThemeMap()
{
ThemeMapId = thememap.ThemeMapId,
ThemeMapTitle = thememap.Title,
});
}
I have code that looks like this:
List<Meme> myList = new List<Meme>();
myList.Add(DogThugLife);
myList.Add(AintNobodyGotTimeForThat);
myList.Add(WashingTheDishes);
I was wondering how I can add a search feature so people can search for the items in this list. I have also made a Meme class:
class Meme
{
public string Name { get; set; }
public string Topic { get; set; }
public bool Popular { get; set; }
public string Identifier { get; set; }
}
and some info on the items in my list:
Meme DogThugLife = new Meme();
DogThugLife.Name = "Dog Thug Life";
DogThugLife.Topic = "Thug Life";
DogThugLife.Popular = false;
So basically I want users to be able to search for these properties. Thanks!
The List class in C# has a Contains method. That contains method will iterate over the collection and search for an identical object. It does so by invoking the Equals method in System.Object for each object in the collection.
Basically, the Contains method receives an object as a parameter. It will pass this object to the System.Equals method for each object in that collection until it finds an identical object or until it has iterated over all of the objects in the collection (didn't find what it was searching for).
This means that you need to override the Equals method in your Meme class, this is an example.
A basic implementation for your Meme class can be like this:
class Meme
{
public string Name { get; set; }
public string Topic { get; set; }
public bool Popular { get; set; }
public string Identifier { get; set; }
public bool Equals(object other)
{
var casted = other as Meme;
if(null == casted)
{
return false;
}
return casted.Name == Name && Casted.Topic == Topic and Casted.Popular == Popular && Casted.Identifier == Identifier;
}
//IMPORTANT: The Equals and the GetHashCode methods are intertwined. Thus, when you override one of them, you should override the other.
//The rule is: The GetHashCode method should return the same value for two objects that are equals (according to their Equals method)
//More about the GetHashCode method: https://msdn.microsoft.com/en-us/library/system.object.gethashcode%28v=vs.110%29.aspx
public int GetHashCode()
{
//Naive implementation
return Name.GetHashCode();
}
}
And then:
var myList = new List<Meme>();
// add items to list.
var dogThugLife = new Meme();
// initialize dogThugLife object's properties (though you should use a constructor for that)
var result = myList.Contains(dogTugLife);
It is important to note that there are better ways in terms of simplicity and performance to search for objects in a collection. If you know from advance that you will need to search for an object and you know that you won't have only a few number of items in that collection, then you should consider using a Dictionary or a HashSet instead of a list.
A simple example for using a HashSet:
var mySet = new HashSet<Meme>();
// add items to set (example: mySet.Add(someMeme)).
var dogThugLife = new Meme();
// initialize dogThugLife object's properties (though you should use a constructor for that)
var result = mySet.Contains(dogTugLife); // This will search the set via the GetHashCode value of dogTugLife
A sample program showing how to peek a meme object by its identifier from a generic list
using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Forms;
namespace SOFAcrobatics
{
class Meme
{
public String Name
{
get;
set;
}
public String Topic
{
get;
set;
}
public Boolean Popular
{
get;
set;
}
public String Identifier
{
get;
set;
}
public Meme (String name, String topic, Boolean popular, String identifier)
{
this.Name = name;
this.Topic = topic;
this.Popular = popular;
this.Identifier = identifier;
}
public override string ToString()
{
return String.Format("(Identifier: {0}, Topic: {1}, Popular: {2}, Name: {3})",
this.Identifier,
this.Topic,
this.Popular,
this.Name);
}
}
public static class Launcher
{
public static void Main ()
{
List<Meme> memes = new List<Meme>() {
new Meme ("name1", "topic1", false, "id1"),
new Meme ("name3", "topic2", true, "id2"),
new Meme ("name2", "topic3", false, "id3")
};
MessageBox.Show(Launcher.SearchByIdentifier(memes, "id2").ToString(),
"Search result for Meme name3",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
private static Meme SearchByIdentifier (List<Meme> memes, String identifier)
{
return memes.Find(
m => (m.Identifier == identifier)
);
}
}
}
I have this class:
public class ExtraDisplayItems
{
public int ItemId { get; set; }
public string ItemCode { get; set; }
public string ItemDescription { get; set; }
public double? ItemSellingPrice { get; set; }
}
I then add data to the class with the method below:
using (TruckServiceClient TSC = new TruckServiceClient())
{
var item = cmbAddExtras.SelectedItem as ExtraDisplayItems;
if (item != null)
{
var displayItem = new List<ExtraDisplayItems>
{
new ExtraDisplayItems
{
ItemId = item.ItemId,
ItemCode = item.ItemCode,
ItemDescription = item.ItemDescription,
ItemSellingPrice = item.ItemSellingPrice
}
};
dgAddExtras.Items.Add(item);
}
}
Now what I want to do is create a read-only property where I would get the total Sum of the ItemSellingPrice, but in this property I cannot reach the Sum when trying to calculate the double?
This is how I wan't the coding to work:
public double? SubTotalExtras
{
get { return ExtraDisplayItems.Sum(x => x.ItemSellingPrice); }
}
But... it's giving me the error:
ExtraDisplayItems does not contain a definition for 'Sum'
How would I go about fixing this?
EDIT
I have changed the class to:
public class ExtraDisplayItems
{
private List<ExtraDisplayItems> displayItems;
public int ItemId { get; set; }
...
}
And...
if (item != null)
{
this.displayItems = new List<ExtraDisplayItems> //Error
{
new ExtraDisplayItems
{
...
}
};
dgAddExtras.Items.Add(item);
}
But it now throws the error: does not contain definition for 'displayItems'
It seems dat ExtraDisplayItems is an instance of the ExtraDisplayItems class, which doesn't implement IEnumerable<T> (that is: 'it is a collection of something'). That means that it indeed doesn't have a Sum method, nor does the extension method apply.
My best guess at the moment is that you should save the displayItem (which is a List<ExtraDisplayItems>, so it implements IEnumerable<ExtraDisplayItems>) somewhere in your view model and call Sum on that.
Also, use naming conventions: you are making plurals singular and the other way around... Very confusing...
You are probably missing Linq in your using definitions.
using System.Linq;
EDIT:
I was assuming ExtraDisplayItems was a collection, then it is common to have the error 'x does not contain a definition for 'Sum'' even if you see it in code samples.
In order to do the sum various objects you actually need a collection of them not a single object.