This question already has answers here:
How do I clone a generic list in C#?
(29 answers)
Closed 4 years ago.
I have studied deep copies over the day but I still don't quite get it.
Here's what I want.
static class MyList
List<MenuData> Menu1 = MenuList.Menus.ToList();
List<MenuData> Menu2 = MenuList.Menus.ToList();
I use Menu2 on remove method.
However, Menu1 was also deleted.
I realize that Menu1 and Menu2 were deleted together because of Swallow Copy.
They also referred to samples of other people, but failed.
static class MenuList
{
public class MenuData
{
public string ID { get; set; }
public string Text { get; set; }
public string Image { get; set; }
public Boolean Expanded { get; set; }
}
public static List<MenuData> Menus = new List<MenuData>()
{
new MenuData {
ID = "1",
Text = "Service",
Image = "file_path.png",
Expanded = false
},
};
public static T Clone<T>(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
public static List<MenuData> CopyMenus = MenuList.Clone(MenuList.Menus);
}
ErrMessage : The format 'Models.MenuData' is not marked serializable.
It was asked to reduce further waste of time.
I don't know why did you want to clone a static class
but if you want to Serialize object you need to use a attribute Serializable to tag your Serialize Class.
[Serializable]
public class MenuData
{
public string ID { get; set; }
public string Text { get; set; }
public string Image { get; set; }
public Boolean Expanded { get; set; }
}
serialization
EDIT
If you want to clone object, your clone class can implement ICloneable interface.
Here is a sample.
public class MenuList
{
public List<MenuData> Menus { get; private set; }
public MenuList()
{
Menus = new List<MenuData>()
{
new MenuData {
ID = "1",
Text = "Service",
Image = "file_path.png",
Expanded = false
},
};
}
public class MenuData : ICloneable
{
public string ID { get; set; }
public string Text { get; set; }
public string Image { get; set; }
public Boolean Expanded { get; set; }
public object Clone()
{
return new MenuData()
{
ID = this.ID,
Text = this.Text,
Expanded = this.Expanded,
Image = this.Image
};
}
}
public List<MenuData> CloneMenus()
{
return Menus.Select(o => (MenuData)o.Clone()).ToList();
}
}
You can get Clone List on outside.
MenuList list = new MenuList();
List<MenuData> CloneList = list.CloneMenus();
Related
All - I've stumbled into a scenario that's causing me quite a bit of grief. I have a json structure (produced by gateio api) which on the face of it, looks super simple to deserialize into an object. the jsonTickerString looks like the below:
{
"method":"ticker.update",
"params":[
"BTC_USDT",
{
"period":86400,
"open":"46721.06",
"close":"48130.43",
"high":"48758.59",
"low":"46330.3",
"last":"48130.43",
"change":"2.95",
"quoteVolume":"2246.8399550054",
"baseVolume":"106183751.468785134437"
}
],
"id":null
}
However, this is proving to be deceptively funky when trying to push it into an object model. I derived the object model below and thought we were all set to go:
public partial class GateIoTicker
{
[JsonProperty("method")]
public string Method { get; set; }
[JsonProperty("params")]
public List<ParamElement> Params { get; set; }
[JsonProperty("id")]
public object Id { get; set; }
}
public class ParamClass
{
[JsonProperty("period")]
public long Period { get; set; }
[JsonProperty("open")]
public string Open { get; set; }
[JsonProperty("close")]
public string Close { get; set; }
[JsonProperty("high")]
public string High { get; set; }
[JsonProperty("low")]
public string Low { get; set; }
[JsonProperty("last")]
public string Last { get; set; }
[JsonProperty("change")]
public string Change { get; set; }
[JsonProperty("quoteVolume")]
public string QuoteVolume { get; set; }
[JsonProperty("baseVolume")]
public string BaseVolume { get; set; }
}
public partial struct ParamElement
{
public string coinName;
public ParamClass quoteParams;
public static implicit operator ParamElement(ParamClass quoteparams)
{
return new ParamElement { quoteParams = quoteparams };
}
public static implicit operator ParamElement(string coinname)
{
return new ParamElement { coinName = coinname };
}
}
I then set about populating the object using the standard Json.Net approach:
var gateIoTicker = JsonConvert.DeserializeObject<GateIoTicker>(jsonTickerString);
However, although this correctly deserializes the string element in the "params" object, no amount of coersion will bring about a deserialization of the ParamClass object.
Am I missing something very obvious here?? I've spent an inordinate amount of time trying to figure this out and think it's now time to solicit some superior brain power.
Hope this scans as expected...
[Edit] - further to Serge's suggestion, i took his code and added it as a method on my GatIoTicker object. Would have preferred an option that desrializes using attributes, but this works perfectly. Refactored code looks like:
public partial class GateIoTicker
{
[JsonProperty("method")]
public string Method { get; set; }
[JsonProperty("params")]
public List<ParamElement> Params { get; set; }
[JsonProperty("id")]
public object Id { get; set; }
public GateIoTicker FromJson(string json)
{
var jsonObject = JObject.Parse(json);
var pars = jsonObject["params"] as JArray;
var paramElement = new ParamElement();
foreach (var jObject in pars)
{
if (jObject.GetType().Name.ToString() == "JValue") paramElement.ParamName = ((JValue)jObject).ToString();
else
{
paramElement.ParamBody = jObject.ToObject<ParamClass>();
}
}
GateIoTicker gateIoTicker = new GateIoTicker { Params = new List<ParamElement>() };
gateIoTicker.Id = (string)jsonObject["Id"];
gateIoTicker.Method = (string)jsonObject["method"];
gateIoTicker.Params.Add(paramElement);
return gateIoTicker;
}
}
public partial class ParamElement
{
public string ParamName { get; set; }
public ParamClass ParamBody {get; set;}
}
thanks again for the suggestions and nudges. seasons greetings
Try this, it was tested in Visual studio
var jsonObject = JObject.Parse(json);
var pars = jsonObject["params"] as JArray;
var paramElement = new ParamElement();
foreach (var jObject in pars)
{
if (jObject.GetType().Name.ToString() == "JValue") paramElement.ParamName = ((JValue)jObject).ToString();
else
{
paramElement.ParamBody=jObject.ToObject<ParamClass>();
}
}
GateIoTicker gateIoTicker = new GateIoTicker {Params= new List<ParamElement>()};
gateIoTicker.Id= (string) jsonObject["Id"];
gateIoTicker.Method= (string) jsonObject["method"];
gateIoTicker.Params.Add(paramElement);
ParamElement class
public partial class ParamElement
{
public string ParamName { get; set; }
public ParamClass ParamBody {get; set;}
}
I have the following class which is populated after de-serializing a JSON string:
public class Doors
{
public List<Door> doors { get; set; }
}
public class Door
{
public int id { get; set; }
public string name { get; set; }
public bool elevator { get; set; }
}
JSON string:
var result = JsonConvert.DeserializeObject<Doors>(response.Content);
// "{\"doors\":[{\"id\":1,\"name\":\"Main Door\",\"elevator\":false},{\"id\":2,\"name\":\"Back Door\",\"elevator\":false}]}"
The data maps to my class fine, I'm then trying to pass the class data to another class:
public class WS4APIResult
{
public List<Door> doors { get; set; } = new List<Door>();
}
public class Door
{
public int id { get; set; }
public string name { get; set; }
public bool elevator { get; set; }
}
return new WS4APIResult() {
doors = result.doors
}
With the following error: any ideas please?
Cannot implicitly convert type 'System.Collections.Generic.List<WS4PortalApi.Models.Door>' to 'System.Collections.Generic.List<WS4PortalApi.Domain.Door>'
The two c#-Files refer to different classes if you type Door. You need to implement a conversion between WS4PortalApi.Models.Door and WS4PortalApi.Domain.Door.
Like:
public static WS4PortalApi.Domain.Door DoorConvert(WS4PortalApi.Models.Door door)
then you can use linq to generate a new List
doors = result.doors
.Select(d => DoorConvert(d))
.ToList();
You have to map the properties of your domain object to those of the model.
I normally create a method for this like:
var doors = new List<Model.Door>();
foreach(door in result.doors)
{
var doorModel = new Model.Door
{
id = door.id,
name = door.name,
elevator = door.elevator
};
doors.Add(doorModel);
}
return doors;
Or you can use a library like automapper.
I'm having some trouble mapping an object with multiple layers of properties to an object with a single layer of properties. Here's an example:
My destination class
public class Part
{
public string Name { get; set; }
public string PartNumber { get; set; }
public string Position { get; set; }
public IList<Part> ReplacedBy { get; set; } = new List<Part>();
}
My source classes
public class PartType
{
public string PartNumber { get; set; }
public PartInformationType Part { get; set; }
}
public class PartInformationType
{
public string Position { get; set; }
public string Name { get; set; }
public IList<PartType> ReplacedBy { get; set; } = new List<PartType>();
}
Note that the real objects have ALOT more properties in each layer so it would be a hassle to do a ForMember() on each affected property. Is there any automated way to do this for me?
Expected result:
This is giving me the expected result but only for one generation of parts, say that each replacedBy part is replaced by another part for 10 generations, that quickly becomes unmanageable
var part = Mapper.DynamicMap<Part>(result);
part.ReplacedBy = new List<ReplacementPart>();
foreach (var partType in result.ReplacedBy)
{
var replacementPart = Mapper.DynamicMap<ReplacementPart(partType.Part);
replacementPart.Name= partType.Name;
replacementPart.Position= partType.Position;
part.ReplacedBy.Add(replacementPart);
}
This is an interesting problem, and I happen to have solved a very similar one recently in one of my projects so hopefully my answer will suit your needs as well or at least get you going on the right track. I've adjusted the sample code somewhat to your case.
My destination model class (pretty much same as yours):
public class PartModel
{
public string Name { get; set; }
public string PartNumber { get; set; }
public string Position { get; set; }
public List<PartModel> ReplacedBy { get; set; }
}
My two source classes (also pretty much same as yours)
public class PartEntity
{
public string PartNumber { get; set; }
public PartEntityInformation PartEntityInformation { get; set; }
}
public class PartEntityInformation
{
public string Position { get; set; }
public string Name { get; set; }
public List<PartEntity> ReplacedBy { get; set; }
}
I have defined a static EntityMap with the configurations for my two mappings. As I have mentioned in the comments of your question - I am not specifying any particular member mapping config as Automapper will just map by convention because the property names match between my source and destination objects.
public static class EntityMap
{
public static IMapper EntityMapper { get; set; }
static EntityMap()
{
EntityMapper = new MapperConfiguration(config =>
{
config.CreateMap<PartEntity, PartModel>();
config.CreateMap<PartEntityInformation, PartModel>();
}).CreateMapper();
}
}
You can use EntityMap like below. Nothing special here, it will just yield me the base model where only PartNumber property is mapped.
var rootPart = GetPart();
var rootPartModel = EntityMap.EntityMapper.Map<PartModel>(rootPart);
In order to get the nested mappings for your ReplacedBy parts, you can use recursion (this is how I solved this nested mapping requirement in my project, there may be better solutions). In this method, I recursively map the nested child objects to their destination objects because the initial mapping will give me the number of items that there is in the nested ReplacedBy list.
public static void MapRecursively(PartModel partModel, PartEntity partEntity)
{
EntityMap.EntityMapper.Map(partEntity.PartEntityInformation, partModel);
if (partEntity.PartEntityInformation.ReplacedBy == null
|| partEntity.PartEntityInformation.ReplacedBy.Count == 0) return;
for (var i = 0; i < partModel.ReplacedBy.Count; i++)
{
MapRecursively(partModel.ReplacedBy[i], partEntity.PartEntityInformation.ReplacedBy[i]);
}
}
So you can now just use the rootPart and rootPartModel with this recursive method to map out of the rest of your nested objects.
MapRecursively(rootPartModel, rootPart);
This should work out of the box, I have provided the GetRootPart() method specification below as it is just the sample data I have used.
private static PartEntity GetPart()
{
var partEntityInfo = new PartEntityInformation
{
Name = "SomeName",
Position = "2",
ReplacedBy = new List<PartEntity>
{
new PartEntity
{
PartNumber = "22",
PartEntityInformation = new PartEntityInformation
{
Name = "SomeSubName"
}
},
new PartEntity
{
PartNumber = "33",
PartEntityInformation = new PartEntityInformation
{
Name = "33SubName",
Position = "33SubPosition",
ReplacedBy = new List<PartEntity>
{
new PartEntity
{
PartNumber = "444",
PartEntityInformation = new PartEntityInformation
{
Name = "444SubSubname"
}
}
}
}
}
}
};
var part = new PartEntity
{
PartNumber = "1",
PartEntityInformation = partEntityInfo
};
return part;
}
I use the mongoDB C# driver 2.1.1 to store and retrieve documents in a mongoDb collection. This worked well so far until one document was CORRECTLY stored but could not be read back by the driver.
I got to that conclusion using robomongo (a user interface to monitor what's inside my collection) and manipulating the object stored.
It contains a collection of elements (around 4000 sub elements) and if I remove enough of it, the document is finally retrievable again.
The line of code used is:
public Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default(CancellationToken))
{
return MongoQueryable.FirstOrDefaultAsync(_collection, predicate, cancellationToken);
}
Before you ask, what I first thought is that one specific element of my sub elements set had encountered some issues while being serialized/deserialized and was creating the issue. I tested by removing randomly elements of the collection, and just the number seems to be the root of the problem.
The text file size of the Json document represents around 11 Mb - I will try now to get the actual size of the object which would be more relevant.
Something else I can add is that in the mondoDb log file, there is one line that only appear when I try to select an "oversized" document :
2016-06-29T19:30:45.982+0200 I COMMAND [conn12] command MYCOLLECTIONDb.$cmd command: aggregate
{ aggregate: "XXXX", pipeline: [ { $match: { _id: "5773e0e9a1152d259c7d2e50" } }, { $limit: 1 } ]
, cursor: {} } keyUpdates:0 writeConflicts:0 numYields:0 reslen:4188200 locks:{ Global:
{ acquireCount: { r: 6 } }, MMAPV1Journal: { acquireCount:
{ r: 3 } }, Database: { acquireCount: { r: 3 } }, Collection: { acquireCount: { R: 3 } } } 109ms
(XXXX being my custom object)
So I'd like to know if you have any idea of what's causing this issue, any hint of where I should try to look, because I am running out of ideas :)
EDIT_1: That is basically the structure of my object :
[DataContract]
public class MyClass
{
[DataMember]
public string _id { get; set; }
[DataMember]
public string XmlPreference { get; set; }
[DataMember]
public string XmlMarketDates { get; set; }
[DataMember]
public IEnumerable<ClassB> Lines { get; set; }
}
[DataContract]
public class ClassB
{
[DataMember]
public string UniqueId { get; set; }
[DataMember]
public string ParentId { get; set; }
[DataMember]
public Dictionary<string, ClassC> DataMapping {get; set;}
}
and ClassC is just a container of 7 ints and 1 object properties.
Still looking for something :)
EDIT 2: I reproduced the bug only using the mongoDb C# drivers 2.1.1 release version. Here is the code :
using MongoDB.Bson;
using MongoDB.Driver.Linq;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace TestingMongoDbCsharpDriver
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Debugging starting...");
try
{
MongoDB.Driver.MongoClient myClient = new MongoDB.Driver.MongoClient("mongodb://localhost:27010");
var db = myClient.GetDatabase("DocumentStoreDb");
var collection = db.GetCollection<DocumentModel>("TestOverload");
Console.WriteLine("Collection TestOverload found");
Random rand = new Random(999999999);
DocumentModel toInsert = null;
for (int lineNb = 1; lineNb < 50000; lineNb += 1000)
{
string id = rand.Next().ToString();
Console.WriteLine(string.Format("\nCreating document with id '{0}' and {1} lines...", id, lineNb));
toInsert = Funcs.Create(id, lineNb);
Console.WriteLine("Created.");
// Saving and waiting for it to finish
collection.InsertOneAsync(toInsert).Wait();
Console.WriteLine("Inserted.");
// retrieving it
var filter = new BsonDocument("_id", new BsonDocument("$eq", id));
var cursor = collection.FindAsync<DocumentModel>(filter).Result;
cursor.MoveNextAsync().Wait();
string messFilterMethod = cursor.Current.Count() == 1 ? "Retrieved" : "Not retrieved. Bug confirmed";
Console.WriteLine("With Filter: " + messFilterMethod);
var model = MongoQueryable.FirstOrDefaultAsync(collection.AsQueryable(), e => e._id == id).Result;
string mess = model != null ? "Retrieved" : "Not retrieved. Bug confirmed";
Console.WriteLine("With AsQueryable: " + mess);
// Removing it
collection.DeleteOneAsync(filter).Wait();
Console.WriteLine("Deleted.\n");
Console.ReadKey();
}
}
catch (Exception e)
{
Console.WriteLine("\n\nERROR: " + e.Message);
}
Console.WriteLine("\n\n**************** NO BUG *************\n");
}
}
public static class Funcs
{
public static DocumentModel Create(string uniqueId, int nbLines)
{
Random random = new Random(2000000);
List<MyDocumentSubElement> listOk = new List<MyDocumentSubElement>();
for (int lines = 0; lines < nbLines; ++lines)
{
Dictionary<string, SnapCellValueStyle> newDico = new Dictionary<string, SnapCellValueStyle>();
for (int i = 0; i < 10; ++i)
{
int idSnap = random.Next();
var snap = new SnapCellValueStyle()
{
alignment = idSnap,
Value = "okkkkkkkkkkzzzzkk"
};
newDico.Add("newKey_" + idSnap.ToString(), snap);
}
MyDocumentSubElement doc = new MyDocumentSubElement()
{
Icon = 516,
Name = "name du truc",
ParentId = "parent id",
type = SubElementType.T3,
UniqueId = "uniqueId_" + random.Next().ToString(),
MapColumnNameToCellValue = newDico
};
listOk.Add(doc);
}
int headerId = random.Next();
MyDocumentHeader temp = new MyDocumentHeader()
{
Comment = "comment",
Date = DateTime.Now,
ExtractionId = headerId,
Id = "id ok _ " + headerId,
Name = "Name really interesting name",
OwnerId = 95115,
RootFolioId = 51,
SnapshotViewId = MyDocumentType.Type2
};
DocumentModel toInsert = new DocumentModel()
{
_id = uniqueId,
Header = temp,
XmlMarketDates = "<xmlPrefok65464f6szf65ze4f6d2f1ergers5fvefref3e4f05e4f064z68f4xd35f8eszf40s6e40f68z4f0e8511xf340ed53f1d51zf68d4z61ef644dcdce4f64zef84zOKok>><>>",
XmlPreference = "<<zefaiunzhduiaopklzpdpakzdplapdergergfdgergekâzda4684z16ad84s2dd0486za04d68a04z8d0s1d d4az80d46az4d651s1d8 154efze40f6 4ze65f40 65ze40f6z4e>><>>>",
Lines = listOk
};
return toInsert;
}
}
// Imitation of SnapshotDocModel
[DataContract]
public class DocumentModel
{
[DataMember]
public string _id { get; set; }
[DataMember]
public MyDocumentHeader Header { get; set; }
[DataMember]
public string XmlPreference { get; set; }
[DataMember]
public string XmlMarketDates { get; set; }
[DataMember]
public IEnumerable<MyDocumentSubElement> Lines { get; set; }
}
[DataContract]
public class MyDocumentHeader
{
[DataMember]
public string Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int OwnerId { get; set; }
[DataMember]
public string Comment { get; set; }
[DataMember]
public DateTime Date { get; set; }
[DataMember]
public int RootFolioId { get; set; }
[DataMember]
public MyDocumentType SnapshotViewId { get; set; }
[DataMember]
public int ExtractionId { get; set; }
}
[DataContract]
public class MyDocumentSubElement
{
[DataMember]
public string UniqueId { get; set; }
[DataMember]
public string ParentId { get; set; }
[DataMember]
public int Icon { get; set; }
[DataMember]
public SubElementType type { get; set; }
[DataMember]
public Dictionary<string, SnapCellValueStyle> MapColumnNameToCellValue { get; set; }
[DataMember]
public string Name { get; set; }
}
public class SnapCellValueStyle
{
public object Value { get; set; }
public int alignment { get; set; }
public int Red { get; set; }
public int Green { get; set; }
public int Blue { get; set; }
public int currency { get; set; }
public int decimalPoint { get; set; }
public int kind { get; set; }
public int style { get; set; }
}
#region enum
public enum MyDocumentType
{
Undefined,
Type1,
Type2,
Type3,
Type4,
Type5
}
public enum SubElementType
{
T1,
T2,
T3
}
#endregion
}
If you test it, you will see that there is the point where the AsQueryable method does not work anymore where as the document is still retrievable by the method using a filter.
Problem fixed in the version 2.2.4 of the c# mongo db driver (probably with the bug https://jira.mongodb.org/browse/CSHARP-1645)
Thanks :)
I wonder if there's any way to match the names in a list with the elements in a class:
I have a class:
public class exampleClass
{
public string name { get; set; }
public string value { get; set; }
}
and a List: List<exampleClass> EnfSist
So that's the way the list is made. Now I would like to know how to match or identify the string inside "name" from my list. To match this class:
tbl_sistematicas b = new tbl_sistematicas
{
ap_enf_id_enfermedad = Convert.ToInt32(EnfSist[0].value),
ap_pac_inicio = Convert.ToInt32(EnfSist[1].value),
ap_pac_inicio_periodo = Convert.ToInt32(2].value),
ap_pac_duracion = Convert.ToInt32(EnfSist[3].value),
ap_pac_duracion_periodo = Convert.ToInt32(EnfSist[4].value),
ap_pac_tratamiento = EnfSist[5].value
};
Once being able to match the same names I won't have to specify each index of every element in the list. The elements in the list have the same name as in the table. Not all elements of the class are being used.
I have something like this: tbl_sistematicas bh = EnfSist.FindAll(x => x.name == bh.?????? );
If I understand the question, you can do this using something like automapper or ValueInjector
An example using ValueInjector
void Main()
{
List<exampleClass> EnfSist = new List<exampleClass>();
EnfSist.Add(new exampleClass { name = "ap_enf_id_enfermedad", value = "12" });
EnfSist.Add(new exampleClass { name = "apap_pac_inicio" , value = "34" });
// etc
tbl_sistematicas b = new tbl_sistematicas();
b.InjectFrom<MyInjection>(EnfSist);
}
public class MyInjection : KnownSourceValueInjection<List<exampleClass>>
{
protected override void Inject(List<exampleClass> source, object target)
{
foreach(var entry in source)
{
var property = target.GetProps().GetByName(entry.name, true);
if (property != null)
property.SetValue(target, Convert.ChangeType(entry.value, property.PropertyType));
}
}
}
public class exampleClass
{
public string name { get; set; }
public string value { get; set; }
}
public class tbl_sistematicas
{
public int ap_enf_id_enfermedad { get; set; }
public int apap_pac_inicio { get; set; }
public int ap_pac_inicio_periodo { get; set; }
public int ap_pac_duracion { get; set; }
public int ap_pac_duracion_periodo { get; set; }
public string ap_pac_tratamiento { get; set; }
}
Note, this will throw an exception if the value can not be converted to an int