I'm working on a C# WPF application that makes use of a Windows Forms PropertyGrid to allow the user to view and change an object's properties. Some properties I want to be visible but locked, so I'm setting their ReadOnly attribute to true.
However, elsewhere in the project we're using the XAML serializer to serialize objects and have found that the properties with the ReadOnly attribute set are being omitted from the serialization.
To demonstrate this:
// Simple class containing 3 properties, one of which has a ReadOnly attribute
public class TestClass
{
public int a { get; set; }
[ReadOnly(true)]
public int b { get; set; }
public int c { get; set; }
}
Running this code:
TestClass test = new TestClass();
test.a = 1;
test.b = 2;
test.c = 3;
string xStr = XamlWriter.Save(test);
Console.WriteLine(xStr);
gives the output:
<TestClass a="1" c="3" xmlns="clr-namespace:myTest;assembly=myTest" />
Clearly the 'b' property is missing.
Is this correct behaviour? Is it possible to serialize properties that have ReadOnly set to true?
If you are happy that your output may not be deserializable then you can achieve what you want by also decorating your [ReadOnly(true)] property with:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
Related
I have this entity:
public int Id { get; private set; }
public string Name { get; private set; }
public Behavior Behavior { get; private set; }
public Product(int id, string name, Behavior behavior)
{
Id = id;
Name = name;
Behavior = behavior;
}
In startup method I'm registering the EdmModel :
var builder = new ODataConventionModelBuilder();
var entitySet = builder.EntitySet<Product>("Products");
entitySet.EntityType.HasKey(x => x.Id);
var model = builder.GetEdmModel();
app.UseMvc(route =>
{
route.Select().Filter().Expand().OrderBy().Count().MaxTop(null);
route.MapODataServiceRoute("odata", null, model);
route.EnableDependencyInjection();
}
);
When I'm running my app, this exception occurs:
InvalidOperationException: The entity 'Product' does not have a key
defined.
If I change private setter to public all is working. Also others properties with private setters are giving: ODataException Product does not contain property with name 'Name'. How can I solve it ?
the question is quite old, I stumbled across the same issue now. Scalar properties (i.e. int, string, bool) with private setters are not recognized by the ODataConventionModelBuilder, even though it recognizes collections with private setters.
I could solve the problem using EntityTypeConfiguration<T> obtained via the model builder:
public class Article
{
public string ArticleNr { get; private set; }
public string SomeProperty { get; private set; }
}
var builder = new ODataConventionModelBuilder();
var articleBuilder = builder.EntityType<Article>();
articleBuilder.HasKey(a => a.ArticleNr);
articleBuilder.Property(a => a.SomeProperty);
builder.EntitySet<Article>("Articles");
var model = builder.GetEdmModel();
This is giving me a model that can be built, it recognizes the key in spite of its private setter and I can also issue queries against SomeProperty. But this way every property must be registered explicitly using a call to Property which seems very error prone when adding new properties. I think it should be able to write a custom convention for it, but I have not tried this so far.
I've recently upgraded to build 2230, and things are working just fine. However, I just updated the RavenDB .NET client assemblies and now I'm having this issue.
This code has been in place for a year or so. This is how I'm saving:
public void Save(EntityBase objectToSave)
{
using (IDocumentSession session = GetOptimisticSession())
{
session.Store(objectToSave, objectToSave.Etag);
session.SaveChanges();
}
}
And this is the object I'm saving.
public class InstallationEnvironment : EntityBase
{
public string Name { get; set; }
public int LogicalOrder { get; set; }
}
Now the base class:
public class EntityBase : NotifyPropertyChangedBase
{
public string Id { get; set; } // Required field for all objects with RavenDB.
}
The problem is that the base class property (Id) is getting persisted in RavenDB, but the derived properties (Name, LogicalOrder) are not.
Why would only the base class properties be saved in RavenDB?
Got it. Through trial and error, I noticed that one derived property was being saved (on a different class than the one shown in my question), and that property was decorated with the [DataMember] attribute. I just recently added it because I'm creating a WCF service for my app, and I started by using that attribute on one property for testing.
As Ayende states here, you have to use [DataMember] on all properties, or on none of them. If [DataMember] exists on a property, all others will be ignored.
Note: This was a problem for me even though [DataMember] was specified on a property in a different class. It seems like if I use [DataMember] anywhere, I have to use it for everything.
I'm testing the new ApiController in asp.net mvc 4 beta but when I try to return an class that looks like the following only a few properties gets serialized?
public class PageModel : IPageModel {
public string Id { get; set; }
public virtual IPageMetadata Metadata { get; private set; }
public PageModel() {
Metadata = new PageMetadata();
}
}
this is the code in my api controller
// GET /api/pages/5
public PageModel Get(string id) {
return new PageModel { Id = "pages/1", Metadata = {Name = "Foo"} };
}
and this is the result
{
Id: "pages/1",
Parent: null
}
Is it possible to get the complete object and not only a few things?
Readonly properties are not serialized. Make the setter of the Metadata property public if you want it to be serialized. I think that this behavior is normal for input parameters but not for output which is your case. IMHO it's a bug that could be workarounded by using a JSON serializer which supports this but maybe they will fix it before the final release and allow readonly properties to be serialized for output parameters.
Actually it's not a big pain, because you should be using view models anyway, so simply map your domain model to a view model and have your method return this view model which will contain only the properties that you need to actually expose to the client. This view model will contain properties with public getters and setters.
I have been spending a couple of days now to get to know the Fluent NHibernate automapping working model. It is quite nice, but I keep detecting new details missing from my schemas. Now I want to add extra properties to my classes, but not have them mapped to the database. A typical case is when I need extra properties with internal logic.
So I read the examples and scanned StackOverflow and found out that this was not another convention to be added, but rather a matter of inheriting the DefaultAutomappingConfiguration and override the ShouldMap method.
Fine, no problem, one minute later I had something like this:
public class CustomAutomappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Member member)
{
var explicitSkip = member.PropertyType.GetCustomAttributes(typeof(SkipMap), false).Length > 0;
if ((member.IsProperty && !member.CanWrite) || explicitSkip)
{
return false;
}
return base.ShouldMap(member);
}
}
/// <summary>
/// Don't map this property to database.
/// </summary>
public class SkipMap : Attribute
{
}
public class DemoClass
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual MyBitwiseEnum Status { get; set; }
public virtual bool IsValid
{
get
{
return (int)Status > 3;
}
}
[SkipMap]
public virtual bool IsBad
{
get
{
return MyBitwiseEnum.HasFlag(MyBitwiseEnum.Bad);
}
set
{
MyEnum = value ? MyBitwiseEnum | MyBitwiseEnum.Bad : MyBitwiseEnum ^ MyBitwiseEnum.Bad;
}
}
}
I know that my demo class is kind of stupid, but it will illustrate my point.
The idea is that I want to manually decide what properties to map to database.
The readonly property works fine because the ShouldMap method will look for property.CanWrite. But the custom attribute that definitely is set will not be detected. Why is that!?
In the convention methods I have used the same approach frequently and there it works fine. Why is the property not able to detect defined attributes here, when it obviously can in the convention setting. Is there a workaround?
have you added your new automapconvention to Automap?
AutoMap.AssemblyOf<>(new CustomAutomappingConfiguration())
Update: you are getting the skip attribute from Boolean class instead of the property
member.PropertyType.GetCustomAttributes(typeof(SkipMap), false)
should be
member.MemberInfo.GetCustomAttributes(typeof(SkipMap), false)
Just to be sure the custom attribute is applicable to properties, try adding [AttributeUsage(AttributeTargets.Property)] to your SkipMap class.
Another possibility is an attribute name clash with another attribute that applies to different targets. Try renaming the class to something like MyVerySpecialSkipMap and retest to verify you don't have an attribute clash. At the very least, write some simple reflection code to test for the SkipMap attribute outside the context of your application to ensure it can be found.
With FluentNHibernate I have mapped a UserPreference entity which references the GeneralPreference, GeneralPreferenceOption, and Profile entities:
public class UserPreference
{
public virtual long Id { get; set; }
public virtual Profile Profile { get; set; }
public virtual GeneralPreference Preference { get; set; }
public virtual GeneralPreferenceOption Value { get; set; }
}
It's easy enough to map a list of UserPreference on my Profile entity, but what I actually would like to do is wrap this list inside another class so that I can simplify operations concerning a user's given preferences:
public class Preferences
{
public IList<UserPreferences> UserPreferences{get;set;}
public Language Language {
{
//look up the language preference here
}
}
This kind of feels like a Component, but Components were not created for this type of scenario. Does anyone have any pointers on how I might map this?
I figured out a way to do this by mapping a private property on my Profile Entity. Using the techniques from the Fluent NHibernate wiki on mapping private properties (http://wiki.fluentnhibernate.org/Fluent_mapping_private_properties) I map a collection of UserPreference on my Profile Entity. Then I create another class PropertyHandler which takes an IEnumerable as a constructor parameter and make an instance of this a public property on Profile as well:
public class Profile
{
private PreferenceHandler _preferenceHandler;
get { return _preferenceHandler ?? (_preferenceHandler = new PreferenceHandler(UserPreferences)); }
private IEnumerable<UserPreference> UserPreferences { get; set; }
public static class Expressions
{
public static readonly Expression<Func<Profile, IEnumerable<UserPreference>>> UserPreferences = x => x.UserPreferences;
}
}
Notice the nested static class. It's used to enable mapping of a private property with FluentNHibernate.
The mapping class looks something like this:
public class ProfileMappings : ClassMap<Profile>
{
public ProfileMappings()
{
//... other mappings
HasMany(Profile.Expressions.UserPreferences);
}
}
I can now use the PreferenceHandler class to create helper methods over my collection of UserPreference.
An alternative is to build extension methods for IEnumberable. This works, but I decided not to do this because
1) I'm not really extending the IEnumerable functionality and
2) my helper methods disappear inamongst all the other IEnumerable extension methods making the whole thing a bit cluttered.