I have a .cs file which looks like the following
namespace TarkovMapper.ClassObjects
{
class PointCloud_Object
{
public void AddPoint(PointEntry_Object point)
{
PointLayer pointLayer = LoadPointLayer(path);
pointLayer.Points[point.Location_x,point.Location_y]++;
}
private PointLayer LoadPointLayer(string path)
{
if (!File.Exists(path)) return new PointLayer(this.Width, this.Height);
Stream s = File.OpenRead(path);
BinaryFormatter b = new BinaryFormatter();
PointLayer returnObject = (PointLayer) b.Deserialize(s);
s.Close();
return returnObject;
}
}
[Serializable]
class PointLayer
{
public PointLayer(int width, int height)
{
this.Points = new int[width, height];
}
public int[,] Points { get; private set; } // <- private set!!!
public int Maximum { get; private set; }
}
}
My Question is regarding the Variable "Points" in the class PointLayer.
Eventhough I have the Modifier private set; the following line in PointCloudObject is no issue pointLayer.Points[point.Location_x,point.Location_y]++;.
why is that?
The modifier refers to the Points array, not the array's individual elements.
The PointCloud_Object class cannot assign a new array to the PointLayer.Points variable, but it can manipulate the individual array elements.
Related
I have a public class with some public immutable properties (only get;). It also has a custom constructor, which takes one additional parameter, required not for the setting of a property, but only for the calculations. I think, because of that, this class cannot be deserialized properly via Newtonsoft Json.
Other classes with only corresponding input parameters to the properties work fine.
So, this class deserializes itself not properly, returning zeros. It has additional parameter 'value' which is not related to any property, and just used for a calcualtions and presenting the data.
public class DurationData
{
public DateTime Start { get; }
public int Index { get; }
public double ActivityDurationInHours { get; }
public string Activetime { get; }
public ShiftDurationData(DateTime start, int index, TimeSpan value )
{
Start = start;
Index = shiftIndex;
ActivityDurationInHours = Math.Round(value.TotalHours, 1);
Activetime = $"{(int)value.TotalHours:d2}:{value.Minutes:D2}:{value.Seconds:D2}";
}
}
if I set the mock of this like below or just with mutable properties (get;set) and without a constructor it deserializes itself properly.
public class DurationData
{
public DateTime Start { get; }
public int Index { get; }
public double ActivityDurationInHours { get; }
public string Activetime { get; }
public ShiftDurationData(DateTime start, int index, double activityDurationInHours, string activeTime)
{
Start = start;
Index = shiftIndex;
ActivityDurationInHours = activityDurationInHours
ActiveTime = activetime ?? throw new ArgumentNullException(nameof(activeTime))
}
}
But I don't wan't to create additional mock class and would like to work with original. How to do this properly
In Entity framework I have objectsets like
public partial class Building
{
public int BuildingID { get; set; }
public string BuildingName { get; set; }
}
public partial class Town
{
public int TownID { get; set; }
public string TownName { get; set; }
}
I want to create a generic query like
T.OrderBy(o=>o.Id).Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
T is generic class can be Building or Town but problem is BuildingId and TownId has different name.I don't want to change their name as Id and create interface IIdentity.
Maybe you could try something like this:
var query = (typeof(T) == typeof(Building) ?
context.Buildings.Select(b => new { Id = b.BuildingId, Name = b.BuildingName }) :
context.Towns.Select(t => new { Id = t.TownId, Name = b.TownName }))
.OrderBy(o => o.Id)...
Not tested but that's worth a test...
You can create generic method which find a field decorated with KeyAttribute, and then performs sorting by found key field. I have tested your model, works perfectly. Look at code snippet.
DbContext:
using System.Collections.Generic;
using System.Data.Entity;
namespace ConsoleApplication28.Entities
{
public class AppDbContext : DbContext
{
public AppDbContext()
{
Database.Connection.ConnectionString = #"Data Source=NOTEBOOK-PC;Initial Catalog=StackOverflowTest;Integrated Security=True";
Database.SetInitializer(new AppDbInitializer());
}
public DbSet<Town> Towns { get; set; }
public DbSet<Building> Buildings { get; set; }
}
public class AppDbInitializer : DropCreateDatabaseIfModelChanges<AppDbContext>
{
protected override void Seed(AppDbContext context)
{
context.Buildings.AddRange(new List<Building>
{
new Building {BuildingName = "Building1"},
new Building {BuildingName = "Building2"},
});
context.Towns.AddRange(new List<Town>
{
new Town {TownName = "Town1"},
new Town {TownName = "Town2"},
});
context.SaveChanges();
base.Seed(context);
}
}
}
Building
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28.Entities
{
public class Building
{
[Key]
public int BuildingID { get; set; }
public string BuildingName { get; set; }
}
}
Town
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28.Entities
{
public class Town
{
[Key]
public int TownID { get; set; }
public string TownName { get; set; }
}
}
Program
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ConsoleApplication28.Entities;
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28
{
class Program
{
static void Main(string[] args)
{
const int maxDispItem = 10;
const int page = 1;
var db = new AppDbContext();
var towns = db.Towns.OrderByKey().Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
var buildings = db.Buildings.OrderByKey().Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
}
}
public static class Extensions
{
/// <summary>
/// Sorts the elements of a sequence in ascending order according to a key specified using KeyAttribute
/// </summary>
public static IOrderedQueryable<T> OrderByKey<T>(this IQueryable<T> source, bool isAsc = true)
{
var type = typeof(T);
var keyProperty = type.GetProperties().Single(x => x.GetCustomAttributes(typeof(KeyAttribute)).Any());
return source.OrderBy(keyProperty.Name, isAsc);
}
#region COPIED FROM THERE http://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property, bool isAsc)
{
return isAsc ? source.OrderBy(property) : source.OrderByDescending(property);
}
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
#endregion
}
}
My problem is that my JsonConverter doesn't seem to get invoked by the json.net de-serialization process when the converter is applied to an implementation of an interface, and the propertytype is the interface.
I use TypeNameHandling = TypeNameHandling.Objects to add $type to the json. I do so both on serialization and on de-serialization.
And when I have a property that is of an implementation of the interface the class' converter is invoked properly.
But when I have a property of interface type, the concrete class' converter is not invoked.
When I deserialize this class my JsonDataBagCreationConverter will be invoked by the RealTelephone but not by the Telephone because this is an interface.
Even though they are both serialized with the correct $type.
This results in RealTelephone having its .Data filled whereas Telephones .Data is null.
[JsonConverter(typeof(JsonDataBagCreationConverter<ContainerForITelephone>))]
public class ContainerForITelephone : IDataBag
{
private object _data;
private DataBagTypeEnum _dataBagTypeEnum;
public ITelephone Telephone { get; set; }
public Telephone RealTelephone { get; set; }
public object Data
{
get { return _data; }
set { _data = value; }
}
public DataBagTypeEnum DataBagType_Enum
{
get { return _dataBagTypeEnum; }
}
}
This jsonconverter is not invoked for the Telephone property. But it is for RealTelephone.
public class JsonDataBagCreationConverter<T> : JsonConverter where T : IDataBag, new()
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.Null)
{
var jsonObject = JObject.Load(reader);
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target);
((IDataBag)target).Data = jsonObject.ToString();
return target;
}
return null;
}
}
[JsonConverter(typeof(JsonDataBagCreationConverter<Telephone>))]
public class Telephone : ITelephone
{
public string Name { get; set; }
public string AreaCode { get; set; }
public string Number { get; set; }
public SubPhone SubPhone { get; set; }
public object Data { get; set; }
public DataBagTypeEnum DataBagType_Enum { get; set; }
}
I look forward to hearing from you, thanks
Jan
SOLVED:
public class JsonDataBagCreationConverter<T> : JsonConverter where T:IDataBag
{
//, new() prevented us from using interfaces. Activator.CreateInstance did the trick in Create
//Used when the object decorated with [JsonConverter(typeof(JsonDataBagCreationConverter<xxxx>))] is de-serialized
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
if (objectType.IsInterface)
{
// Interfaces cannot be instantiated but must be converted to their "real" implemented type
// Because we serialize with settings.TypeNameHandling = TypeNameHandling.Objects;
// A $type property is added to the json by the deserializer.
string type = jsonObject["$type"].ToString();
var typesAsArray = type.Split(',');
var wrappedTarget = Activator.CreateInstance(typesAsArray[1], typesAsArray[0]);
var realTarget = wrappedTarget.Unwrap() as IDataBag;
serializer.Populate(jsonObject.CreateReader(), realTarget); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
((IDataBag)realTarget).Data = jsonObject.ToString(); // This is where custom data is stored in databag
return realTarget;
}
// Non interface
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
((IDataBag)target).Data = jsonObject.ToString(); // This is where custom data is stored in databag
return target;
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
return false;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new Exception("WriteJson not implemented");
}
protected IDataBag Create(Type objectType, JObject jsonObject)
{
var aa = Activator.CreateInstance(objectType);
return aa as IDataBag;
// return new T(); // this demands ,new() on the class and then it will not work with interfaces
}
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
}
I have an auto generated class from an xml like the following:
public partial class XmlClass {
private decimal num1;
private ClassA[] classField;
/// <remarks/>
public decimal num1 {
get;
set;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("classA")]
public ClassA[] classA {
get{...};
set{...};
}
}
public partial class ClassA {
private object[] itemsField;
private string typeField;
[System.Xml.Serialization.XmlElementAttribute("commands", typeof(classACommands))]
[System.Xml.Serialization.XmlElementAttribute("minVersion", typeof(string))]
public object[] Items {
get {
return this.itemsField;
}
set {
this.itemsField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string type {
get {
return this.typeField;
}
set {
this.typeField = value;
}
}
}
ClassA has the string and commands class as Objects in the Object[]. I can see everything is deserialized perfectly and get them by: (commands)myXmlClass.classA.ElementAt(i).Items[3], where i is from the index of the ClassA array. But how can I get or set them without using '3'? It might be different in different ClassA elements.
Not sure I totally understand your question, but you can use a foreach loop:
foreach(ClassA a in myXmlClass.classA) {
Console.WriteLine(a.num1.ToString());
}
I'd like to define in class declaration which items are index, something like:
public class MyClass {
public int SomeNum { get; set; }
[THISISANINDEX]
public string SomeProperty { get; set; }
}
so to have the same effect as ensureIndex("SomeProperty")
Is this possible?
I think this is a nice idea, but you have to do this yourself, there's no built-in support for it. If you have an access layer you can do it in there. You'd need an attribute class, something like this;
public enum IndexConstraints
{
Normal = 0x00000001, // Ascending, non-indexed
Descending = 0x00000010,
Unique = 0x00000100,
Sparse = 0x00001000, // allows nulls in the indexed fields
}
// Applied to a member
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class EnsureIndexAttribute : EnsureIndexes
{
public EnsureIndex(IndexConstraints ic = IndexConstraints.Normal) : base(ic) { }
}
// Applied to a class
[AttributeUsage(AttributeTargets.Class)]
public class EnsureIndexesAttribute : Attribute
{
public bool Descending { get; private set; }
public bool Unique { get; private set; }
public bool Sparse { get; private set; }
public string[] Keys { get; private set; }
public EnsureIndexes(params string[] keys) : this(IndexConstraints.Normal, keys) {}
public EnsureIndexes(IndexConstraints ic, params string[] keys)
{
this.Descending = ((ic & IndexConstraints.Descending) != 0);
this.Unique = ((ic & IndexConstraints.Unique) != 0); ;
this.Sparse = ((ic & IndexConstraints.Sparse) != 0); ;
this.Keys = keys;
}
}//class EnsureIndexes
You could then apply attributes at either the class or member level as follows. I found that adding at member level was less likely to get out of sync with the schema compared to adding at the class level. You need to make sure of course that you get the actual element name as opposed to the C# member name;
[CollectionName("People")]
//[EnsureIndexes("k")]// doing it here would allow for multi-key configs
public class Person
{
[BsonElement("k")] // name mapping in the DB schema
[BsonIgnoreIfNull]
[EnsureIndex(IndexConstraints.Unique|IndexConstraints.Sparse)] // name is implicit here
public string userId{ get; protected set; }
// other properties go here
}
and then in your DB access implementation (or repository), you need something like this;
private void AssureIndexesNotInlinable()
{
// We can only index a collection if there's at least one element, otherwise it does nothing
if (this.collection.Count() > 0)
{
// Check for EnsureIndex Attribute
var theClass = typeof(T);
// Walk the members of the class to see if there are any directly attached index directives
foreach (var m in theClass.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
{
List<string> elementNameOverride = new List<string>(1);
EnsureIndexes indexAttr = null;
// For each members attribs
foreach (Attribute attr in m.GetCustomAttributes())
{
if (attr.GetType() == typeof(EnsureIndex))
indexAttr = (EnsureIndex)attr;
if (attr.GetType() == typeof(RepoElementAttribute))
elementNameOverride.Add(((RepoElementAttribute)attr).ElementName);
if ((indexAttr != null) && (elementNameOverride.Count != 0))
break;
}
// Index
if (indexAttr != null)
{
if (elementNameOverride.Count() > 0)
EnsureIndexesAsDeclared(indexAttr, elementNameOverride);
else
EnsureIndexesAsDeclared(indexAttr);
}
}
// Walk the atributes on the class itself. WARNING: We don't validate the member names here, we just create the indexes
// so if you create a unique index and don't have a field to match you'll get an exception as you try to add the second
// item with a null value on that key
foreach (Attribute attr in theClass.GetCustomAttributes(true))
{
if (attr.GetType() == typeof(EnsureIndexes))
EnsureIndexesAsDeclared((EnsureIndexes)attr);
}//foreach
}//if this.collection.count
}//AssureIndexesNotInlinable()
EnsureIndexes then looks like this;
private void EnsureIndexesAsDeclared(EnsureIndexes attr, List<string> indexFields = null)
{
var eia = attr as EnsureIndexes;
if (indexFields == null)
indexFields = eia.Keys.ToList();
// use driver specific methods to actually create this index on the collection
var db = GetRepositoryManager(); // if you have a repository or some other method of your own
db.EnsureIndexes(indexFields, attr.Descending, attr.Unique, attr.Sparse);
}//EnsureIndexes()
Note that you'll place this after each and every update because if you forget somewhere your indexes may not get created. It's important to ensure therefore that you optimise the call so that it returns quickly if there's no indexing to do before going through all that reflection code. Ideally, you'd do this just once, or at the very least, once per application startup. So one way would be to use a static flag to track whether you've already done so, and you'd need additional lock protection around that, but over-simplistically, it looks something like this;
void AssureIndexes()
{
if (_requiresIndexing)
AssureIndexesInit();
}
So that's the method you'll want in each and every DB update you make, which, if you're lucky would get inlined by the JIT optimizer as well.
See below for a naive implementation which could do with some brains to take the indexing advice from the MongoDb documentation into consideration. Creating indexes based on queries used within the application instead of adding custom attributes to properties might be another option.
using System;
using System.Reflection;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using NUnit.Framework;
using SharpTestsEx;
namespace Mongeek
{
[TestFixture]
class TestDecorateToEnsureIndex
{
[Test]
public void ShouldIndexPropertyWithEnsureIndexAttribute()
{
var server = MongoServer.Create("mongodb://localhost");
var db = server.GetDatabase("IndexTest");
var boatCollection = db.GetCollection<Boat>("Boats");
boatCollection.DropAllIndexes();
var indexer = new Indexer();
indexer.EnsureThat(boatCollection).HasIndexesNeededBy<Boat>();
boatCollection.IndexExists(new[] { "Name" }).Should().Be.True();
}
}
internal class Indexer
{
private MongoCollection _mongoCollection;
public Indexer EnsureThat(MongoCollection mongoCollection)
{
_mongoCollection = mongoCollection;
return this;
}
public Indexer HasIndexesNeededBy<T>()
{
Type t = typeof (T);
foreach(PropertyInfo prop in t.GetProperties() )
{
if (Attribute.IsDefined(prop, typeof (EnsureIndexAttribute)))
{
_mongoCollection.EnsureIndex(new[] {prop.Name});
}
}
return this;
}
}
internal class Boat
{
public Boat(Guid id)
{
Id = id;
}
[BsonId]
public Guid Id { get; private set; }
public int Length { get; set; }
[EnsureIndex]
public string Name { get; set; }
}
internal class EnsureIndexAttribute : Attribute
{
}
}