We have a customization where we are running a processing screen against customer locations. When we try to update the location in the Location cache, we are getting the following error:
Unable to cast object of type
'PX.Data.PXCache`1[PX.Objects.AR.SelectedCustomerLocation]' to type
'PX.Data.PXCache`1[PX.Objects.CR.Location]'.
Here is a simplified version of the code we are using. The ProcessLocation method is the PXProcessing delegate:
public class LocationMaint_Extension : PXGraphExtension<LocationMaint>
{
public virtual void ProcessLocation(Location loc)
{
LocationExt locExt = loc.GetExtension<LocationExt>();
locExt.UsrCustomField = "New Value";
Base.Caches<Location>().Update(loc);
}
}
We are aware that SelectedCustomerLocation derives from SelectedLocation, which derives from Location. Because of that, we would expect the above code to run successfully, since there is an inheritance present.
If we write the following code, the method succeeds, and the screen is updated successfully. However, the change we made to the custom field is not persisted to the database:
public class LocationMaint_Extension : PXGraphExtension<LocationMaint>
{
public virtual void ProcessLocation(Location loc)
{
LocationExt locExt = loc.GetExtension<LocationExt>();
locExt.UsrCustomField = "New Value";
Base.Caches<SelectedCustomerLocation>().Update(loc as SelectedCustomerLocation);
// We had this code in as well, which did not seem to help
Base.Caches<SelectedCustomerLocation>().Persist(PXDBOperation.Insert);
Base.Caches<SelectedCustomerLocation>().Persist(PXDBOperation.Update);
}
}
Any help would be greatly appreciated. Thank you.
In C# (and .NET generally), you cannot cast an object of type G<Derived> to G<Base> where G is a generic type. This is kind of a pain sometimes. In the past I have made custom methods to do this, like for a List<T> you might use something like this:
static List<TBase> Upcast<TBase, TDerived>(this List<TDerived> list)
where TDerived : TBase
{
var result = new List<TBase>();
foreach (var d in list)
{
result.Add(d);
}
return result;
}
Related
I have a piece of code that retrieves a long list of members via a library.
public List<InstinctGuildMember> PrepareGuildMembersList()
{
List<GuildMember> warcraftGuildMembers = _warcraftClient.GetGuildRosterAsync("dragonmaw", "instinct", "profile-eu")
.Result.Value.Members.OrderBy(members => members.Rank)
.ThenBy(members => members.Character.Name)
.ToList();
// We cast a new List from our old List.
List<InstinctGuildMember> instinctGuildMembers = new List<InstinctGuildMember>(warcraftGuildMembers.Cast<InstinctGuildMember>());
return instinctGuildMembers;
}
namespace guild_instinct.Models.GuildData
{
public class InstinctGuildMember : GuildMember
{
public string RankName { get; set; }
}
}
The reason I want to convert to my own Model is because the provided Model does not have all the information I need. A friend of mine suggested making my own Model and inheriting the model from the library.
I found this solution on a related StackOverflow question. However, I couldn't get it to work as I kept receiving the error following error
System.InvalidCastException: 'Unable to cast object of type 'ArgentPonyWarcraftClient.GuildMember' to type 'guild_instinct.Models.GuildData.InstinctGuildMember'.'
It's likely that I just lack the knowledge so far to do it correctly and the answer is staring me in the face.
I am clueless on how to continue. What knowledge should I delve into to further my progress? Am I in the right direction but do I lack some fundamentals to make it work? I hope my question is concise enough to warrant help.
You cannot cast the original list that way, since a GuildMember instance is not a InstinctGuidMember.
You can use projection, i.e. project the original list onto a new list of a different type:
var instinctGuildMembers = warcraftGuildMembers.Select(x => new InstinctGuildMember(x, rank)).ToList();
To have your model contain data from GuildMember and additional data, there are 2 ways:
Composition - Make your model have a GuildMember instance.
class InstinctGuildMember {
private GuildMember _guildMember;
private string _rankName;
public InstinctGuildMember(GuildMember guildMember, string rankName)
{
_guildMember = guildMember;
_rankName = rankName;
}
}
Inheritance - Make your model be a GuildMember by inheriting the type.
class InstinctGuildMember : GuildMember {
private string _rankName;
//the constructor here depends on what constructors are available on GuildMember.
}
You cannot cast but you can convert.
List<InstinctGuildMember> instinctGuildMembers =
warcraftGuildMembers.ConvertAll<InstinctGuildMember>(ConvertFunction);
And for ConvertFunction :
static InstinctGuildMember ConvertFunction(GuildMember x)
{
InstinctGuildMember y = new InstinctGuildMember();
y.prop1 = x.prop1;
y.prop2 = x.prop2;
y.RankName = "";
return y;
}
I have a library of fairly heavy-weight DTOs that is currently being used by some WCF services. We are attempting to bring it into protobuf-net world, with as little modification as possible. One particular set of items is giving me trouble in serialization. I'm going to simply them here because it gets a little complicated, but the gist of the problem is:
public class Key
{
public string Id {get; set;}
}
public class KeyCollection : IEnumerable<Key>
{
private readonly List<Key> list;
#region IEnumerable
// etc...
#endregion
}
public class Item
{
public long Id { get; set; }
}
public abstract class ContainerBase
{ }
public abstract class ContainerBase<T> : ContainerBase
where T : Item
{ }
public abstract class ContainerType1Base : ContainerBase<Item>
{
public KeyCollection Keys { get; set; }
}
public class ContainerType1 : ContainerType1Base
{ }
I've left out the decorators because I don't they're the problem, mostly because if I add void Add(Key item) { } to KeyCollection the whole thing seems to work. Otherwise, I run into problems attempting to serialize an instance of ContainerType1.
Actually, changing the signature of KeyCollection is kind of prohibitive, so I'm attempting to follow this answer to try to do it programatically. Specifically, setting itemType and defaultType to null on the "Keys" ValueMember of ContainerType1, ContainerType1Base and ContainerBase<Item>. I also set IgnoreListHandling to true on KeyCollection... which totally doesn't work. I get a generic "failed to deserialize" exception on the client, which I can post here if it would help. On the server side, I serialize it out using Serializer.Serialize(), and I spit out Serializer.GetProto<>() as well as JSON of the object, and they all seem to be work okay.
How can I turn off the list handling? Related to that, is there a way to turn on extra debugging while serializing to try to get some more information of the problem?
Fundamentally, the code shown looks fine. Unfortunately, there's currently a "feature" in gRPC that means that it discards the original exception when a marshaller (serializer) fails for some reason, so gRPC does not currently expose the actual problem. I have submitted a fix for this - it may or may not be accepted.
In the interim, I suggest that you simply remove gRPC from the equation, and simulate just the marshaller workload; to do this, on the server: generate the data you are trying to send, and do:
var ms = new MemoryStream();
Serializer.Serialize(ms, yourDataHere);
var payload = Convert.ToBase64String(ms.ToArray());
and obtain the value of payload (which is just a string). Now at the client, reverse this:
var ms = new MemoryStream(Convert.FromBase64String(thatStringValue));
Serialize.Deserialize<YourTypeHere>(ms);
My expectation here is that this should throw an exception that will tell you what the actual problem is.
If the gRPC change gets merged, then the fault should be available via:
catch (RpcException fault)
{
var originalFault = fault.Status.DebugException;
// ^^^
}
In general terms, a program I'm making involves storing a small number of entries (probably less than 30 at any given time) which can be categorized. I want to allow these entries to be seen but not altered from outside the class using them. I made a class called Entry which could be modified and another called ReadOnlyEntry which is a wrapper for an Entry object. The easiest way to organize these Entry objects it seems is to create a List<List<Entry>>, where each List<Entry> is a category. But then exposing that data in a readonly way became messy and complicated. I realized I would have to have one object of each of the following types:
List<List<Entry>> data;
List<List<ReadOnlyEntry>> // Where each ReadOnlyEntry is a wrapper for the Entry in the same list and at the same index as its Entry object.
List<IReadOnlyCollection<ReadOnlyEntry>> // Where each IReadOnlyCollection is a wrapper for the List<ReadOnlyEntry> at the same index in data.
IReadOnlyCollection<IReadOnlyCollection<ReadOnlyList>> readOnlyList // Which is a wrapper for the first item I listed.
The last item in the list would be exposed as public. The first lets me change entries, the second lets me add or delete entries, and the third lets me add or delete categories. I would have to keep these wrappers accurate whenever the data changes. This seems convoluted to me, so I'm wondering if there's a blatantly better way to handle this.
Edit 1:
To clarify, I know how to use List.asReadOnly(), and the stuff I proposed doing above will solve my problem. I'm just interested in hearing a better solution. Let me give you some code.
class Database
{
// Everything I described above takes place here.
// The data will be readable by this property:
public IReadOnlyCollection<IReadOnlyCollection<ReadOnlyList>> Data
{
get
{
return readOnlyList;
}
}
// These methods will be used to modify the data.
public void AddEntry(stuff);
public void DeleteEntry(index);
public void MoveEntry(to another category);
public void AddCategory(stuff);
public void DeleteCategory(index);
}
You can use List<T>.AsReadOnly() to return ReadOnlyCollection<T>.
Also, you're torturing the List<T> class storing the data the way you are. Build your own hierarchy of classes which store your individual lists.
.NET collections should support covariance, but they don't support it themselves (instead some interfaces support covariance https://msdn.microsoft.com/ru-ru/library/dd233059.aspx). Covariance means List<Conctrete> behaves like subclass of List<Base> if Concrete is subclass of Base. You can use interfaces covariation or just use casting like this:
using System.Collections.Generic;
namespace MyApp
{
interface IEntry
{
}
class Entry : IEntry
{
}
class Program
{
private List<List<Entry>> _matrix = null;
public List<List<IEntry>> MatrixWithROElements
{
get
{
return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
}
}
public IReadOnlyList<List<IEntry>> MatrixWithRONumberOfRows
{
get
{
return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
}
}
public List<IReadOnlyList<IEntry>> MatrixWithRONumberOfColumns
{
get
{
return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry) as IReadOnlyList<IEntry>);
}
}
public IReadOnlyList<IReadOnlyList<IEntry>> MatrixWithRONumberOfRowsAndColumns
{
get
{
return _matrix.ConvertAll(row => row.ConvertAll(item => item as IEntry));
}
}
public void Main(string[] args)
{
}
}
}
Thanks to Matthew Watson for pointing on errors in my previous answer version.
You could make an interface for Entry which contains only getters; you would expose elements via this interface to provide read-only access:
public interface IEntry
{
int Value { get; }
}
The writable implementation would be simply:
public sealed class Entry : IEntry
{
public int Value { get; set; }
}
Now you can take advantage of the fact that you can return a List<List<Entry>> as a IReadOnlyCollection<IReadOnlyCollection<IEntry>> without having to do any extra work:
public sealed class Database
{
private readonly List<List<Entry>> _list = new List<List<Entry>>();
public Database()
{
// Create your list of lists.
List<Entry> innerList = new List<Entry>
{
new Entry {Value = 1},
new Entry {Value = 2}
};
_list.Add(innerList);
}
public IReadOnlyCollection<IReadOnlyCollection<IEntry>> Data => _list;
}
Note how simple the implementation of the Data property is.
If you need to add new properties to IEntry you would also have to add them to Entry, but you wouldn't need to change the Database class.
If you're using C#5 or earlier, Data would look like this:
public IReadOnlyCollection<IReadOnlyCollection<IEntry>> Data
{
get { return _list; }
}
Given this code:
public enum Stuff
{
a,b,c
}
public class RuleManager
{
public Stuff Stuff()
{
return Stuff.a;
}
public int BizRule()
{
Stuff currStuff = Stuff();
return 1; //who cares, just proving a point
}
}
I have an error on the public Stuff Stuff() return statement. Saying that Stuff() is a method which is not valid in the given context. I understand that its since the method and the enum share the same token, but I want to know why the compiler can't discern the difference and how I could make it do so.
Ok to further clarify, I have this code from an existing project that compiles just fine, but when placed into a new one the compiler has this issue.
Why would it compile fine in one class library project but not another?
I want to know why the compiler can't discern the difference
Because of C# Specification chapter 7.4 Member lookup:
A member lookup is the process whereby the meaning of a name in the context of a type is determined.
This process follows a set of rules which ultimately search from the closest scope on outward, where the method RuleManager.Stuff() is found first and searching is then stopped. Because you can't invoke a method without parentheses, it's invalid and the given error is shown.
An easy fix would be to introduce a namespace:
namespace EnumNamespace
{
public enum Stuff
{
a, b, c
}
}
public class RuleManager
{
public EnumNamespace.Stuff Stuff()
{
return EnumNamespace.Stuff.a;
}
public int BizRule()
{
EnumNamespace.Stuff currStuff = Stuff();
return 1; //who cares, just proving a point
}
}
I can't quite see how your code would compile. Try to create an example that reproduces that problem ("Why does this invalid C# code compile?").
I have defined a generic class that derives from BindingList and has a nested non-generic class:
class Generic<T> : BindingList<Generic<T>.Inner>
{
public class Inner
{
public object Foo { get; set; }
}
}
A StackOverflowException occurs in mscorlib when attempting to access the Value property via a dynamic reference like so:
dynamic d = new Generic<string>.Inner();
var value = d.Foo; // StackOverflowException
var value = d.Bar // StackOverflowException as well, not a
// 'RuntimeBinderException' like you would expect when
// trying to access a non-existing member
This is the smallest reproduction i was able to make.
Deriving from BindingList is an important detail, if i change it to a List the program executes correctly.
Why does this happen?
Edit:
This is the top of the call stack:
[Managed to Native Transition]
mscorlib.dll!System.RuntimeTypeHandle.Instantiate(System.Type[] inst)
mscorlib.dll!System.RuntimeType.MakeGenericType(System.Type[] instantiation)
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.CType.CalculateAssociatedSystemTypeForAggregate(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateType aggtype)
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.CType.CalculateAssociatedSystemType(Microsoft.CSharp.RuntimeBinder.Semantics.CType src)
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.CType.AssociatedSystemType.get()
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.GetAggregate(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg, Microsoft.CSharp.RuntimeBinder.Semantics.AggregateType atsOuter, Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray typeArgs)
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.GetAggregate(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg, Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray typeArgsAll)
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.GetAggregate(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg, Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray typeArgsAll)
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeCore(Microsoft.CSharp.RuntimeBinder.Semantics.CType type, Microsoft.CSharp.RuntimeBinder.Semantics.SubstContext pctx)
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeArray(Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray taSrc, Microsoft.CSharp.RuntimeBinder.Semantics.SubstContext pctx)
I think the problem is in this place
Generic<T> :BindingList<Generic<T>.Inner>
Notice you use the declared class as a generic parameter in the parent class BindingList. So I believe reflection just ends up with an infinitive loop and you get StackOverflow.
When you use
var d = new Generic<string>.Inner();
compiler just replaces it with Generic.Inner
so it is the same like
Generic<string>.Inner d = new Generic<string>.Inner();
But when you use
dynamic d = new Generic<string>.Inner();
You really use reflection. Again reflection starts digging deeper in your class structure and it goes like... your class => BindingList = > generic parameter of BindingList => your class(because it's a generic parameter of BindingList) = > BindingList = > and so on until you get StackOverflow.
You can change to Generic<T> : BindingList<string> to break this infinitive loop and it works!
Thank you very much for your correction! I investigated this I would say very interesting moment and found that I was right.
First of all, this is not a BUG! This is just how the Microsoft team solved this issue. Again all what I wrote above I believe is true!
So as I said you end up with an infinitive loop and get StackOverflow, but it seems to me that you get it very very fast. So no any long periods when you have no any access to your machine and just it looks like it's dead. I started digging deeper into the structure of BindingList and here the results.
I created
class Example<T> : Level1<Example<T>>
{
public string Name = "111";
}
public class Level1<T>
{
}
and in the main
dynamic d = new Example<string>();
var value = d.Name;
and it works! Then I added another level
public class Level1<T> : Level2<T>
{
}
public class Level2<T>
{
}
and I got StackOverflow. I changed to
public class Level1<T> : Level2
{
}
public class Level2
{
}
and it works again!
So I think that the guys from Microsoft just said ... so this is the max level after no way through and throw the exception.
Now let's look at BindingList<T>
public class BindingList<T> : Collection<T>,
IBindingList, IList, ICollection, IEnumerable, ICancelAddNew,
IRaiseItemChangedEvents
Notice Collection<T>
And look at List<T>
public class List<T> : IList<T>, ICollection<T>,
IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>,
IEnumerable
Just interfaces....
Therefore it works with List but not with BindingList!My example proves that!I believe they did it intentionally to stop infinitive looping.