|
I'm using an injected base type for an entity's ancestor to hold common properties for all descendants. If I try to declare and use PropertyMetadata for any property on either the developer version of the injected ancestor class or in the model designer.cs declaration of the injected base type (either by adding it in the T4 or just hacking it after the code gen); with of course, the IsNative bool set to false on the DataEntityProperty since it's not an data-store backed property, I get an exception from an invalid cast unless I have at least one property on the descendant concrete class also declare PropertyMetadata in its developer class (this is from your SL PI App that I reproduced the error in):
A first chance exception of type 'System.InvalidCastException' occurred in IdeaBlade.EntityModel.SL A first chance exception of type 'System.InvalidCastException' occurred in IdeaBlade.Core.SL A first chance exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll System.Windows.Data Error: Cannot get 'TestWithMetadata' value (type 'System.String') from 'DomainModel.Customer' (type 'DomainModel.Customer'). BindingExpression: Path='TestWithMetadata'
DataItem='DomainModel.Customer' (HashCode=64497130); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidCastException: Unable to cast object of type 'System.Guid' to type
'System.String'. at IdeaBlade.EntityModel.DataEntityProperty`2.<.ctor>b__0(DataEntityPropertyGetInterceptorArgs`2 args) at IdeaBlade.Core.PropertyInterceptor`3.HandleAction(Action`1 action, TArgs args) at IdeaBlade.Core.PropertyInterceptor`3.<BuildTargetedCombinedAction>b__3(TArgs args) at IdeaBlade.Core.PropertyInterceptor`3.ExecuteActions(TArgs args) at IdeaBlade.Core.PropertyInterceptor`3.GetValue(TInstance instance, Object context) at IdeaBlade.EntityModel.DataEntityProperty`2.GetValue(TInstance instance, EntityVersion version) at IdeaBlade.EntityModel.DataEntityProperty`2.GetValue(TInstance instance) at DomainModel.BaseCustomer.get_TestWithMetadata() --- End of inner exception stack trace --- at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index) at System.Windows.CLRPropertyListener.get_Value() at System.Windows.PropertyAccessPathStep.ConnectToPropertyInSource(Boolean isSourceCollectionViewCurrentItem).
Notice it's trying to cast the primary key of the Customer class (GUID) to the STRING property that's declared in the ancestor class.
To reproduce this, use your SilverlightPIApp from the 050_PropertyInterceptors DRC_Samples.zip.
Create this simple injected base class with two string properties (and link to it in the SL version of course).
BaseCustomer.cs (it's partial since the EDMX declares it when you specify an injected base type):
using System; using System.Runtime.Serialization; using System.Collections.Generic; using System.Linq; using System.Text; using IdeaBlade.Core; using IdeaBlade.EntityModel; namespace DomainModel { public abstract partial class BaseCustomer { public String Test { get { return "Test"; } set { } } public String TestWithMetadata { // set a debug breakpoint on the getter (assuming you haven't declared a Customer.PropertyMetadata partial class // in the Customer developer class) and then hover over TestWithMetadatda or look at the Output window from debug // and you'll see it throws an exception trying to cast GUID to STRING (the primary key of Customer is GUID) // instead of just null. It seems the abstract partial ancestor class can't use PropertyMetadata unless the concrete descendant // class has at least one declaration. get { return PropertyMetadata.TestWithMetadata.GetValue(this); } set { PropertyMetadata.TestWithMetadata.SetValue(this, value); } } public partial class PropertyMetadata { public static readonly DataEntityProperty<BaseCustomer, string> TestWithMetadata = new DataEntityProperty<BaseCustomer, string>("TestWithMetadata", true, false, ConcurrencyStrategy.None, false, false); } } }
|
Change the NorthwindIB.edmx to set the injected base type to "BaseCustomer" for all entities.
In the Customer.cs class, change it to inherit from BaseCustomer, add a property (I just added another string) and add a partial class for PropertyMetadata with the declaration of the PropertyMetadata for that added string:
using System; using System.Linq; using System.ComponentModel.DataAnnotations; using IdeaBlade.EntityModel; using IdeaBlade.Core; using IdeaBlade.Validation;
namespace DomainModel {
// Customer class with sample property interceptors. public partial class Customer : BaseCustomer { public String CustomCity { get { return City + " ***"; } set { } } public partial class PropertyMetadata { // comment this out (you can leave the class declaration and the CustomCity string property) and BaseCustomer.TestWithMetadata will throw an exception public static readonly DataEntityProperty<Customer, string> CustomCity = new DataEntityProperty<Customer, string>("CustomCity", true, false, ConcurrencyStrategy.None, false, false); } ...the rest of this class are the same as in the sample app's class with some getter/setter interceptors that have nothing to do with the issue... }
|
I also added the columns to the Page.xaml data:DataGrid to force the getters to be hit:
<data:DataGridTextColumn Binding="{Binding CustomCity}" Header="Customer.City + ***"/> <data:DataGridTextColumn Binding="{Binding Test}" Header="BaseCustomer.Test"/> <data:DataGridTextColumn Binding="{Binding TestWithMetadata}" Header="BaseCustomer.TestWithMetadata (null)"/>
|
At first, I thought using PropertyMetadata on an ancestor class wasn't supported but since everything works fine if the child class has one property metadata declared, I assume it's a bug.
|