UnMapped property on the Angular/Breeze SPA template

醉酒当歌 提交于 2019-12-29 08:24:28

问题


I am using the Angular/Breeze SPA template in visual studio 2012. I have added an unmapped property in the TodoList server model.

[NotMapped]
public string MyUnmappedProperty{ get{return "testString";}} 

I have registered the unmapped property in the constructor on the client model, specifically in the todo.model.js like Ward suggested here : Handling calculated properties with breezejs and web api

function TodoList() {
        this.title = "My todos"; // defaults
        this.userId = "to be replaced";
         this.myUnmappedProperty="";
    }

When the getTodos() method is called on the todo.controller.js for the first time, the "myUnmappedProperty" has the value of the empty string as set in the constructor. Only after getTodos() is called for the second time( I have added a button that does that) with 'forceRefresh' set to true (getTodos(true)) in order to query the server, only then I see the myUnmappedProperty getting the value "testString".

I am wondering why I am getting this behaviour. Is it possible to have the unmapped property filled with the server value the first time the getTodos() is called?

Thank you.


回答1:


Did you find a solution to this problem? I have been struggling with the same problem and just recently found this SO post by CassidyK, in which he solves a very similar problem by changing a single line in the Breeze source code (breeze.debug.js). Here is the code snippet he changed (taken from CassidyK's post):

proto.initializeFrom = function (rawEntity) {
    // HACK:
    // copy unmapped properties from newly created client entity to the rawEntity.
    // This is so that we don't lose them when we update from the rawEntity to the target.
    // Something that will occur immediately after this method completes. 
    var that = this;
    this.entityType.unmappedProperties.forEach(function(prop) {
        var propName = prop.name;
        that[propName] = rawEntity[propName];  // CassidyK 
        //rawEntity[propName] = that[propName]; // Breeze 
    });

    if (!this._backingStore) {
        this._backingStore = { };
    }
};

I have tried this solution and it seems to work just fine. Notice that this change will probably get you into trouble if you ever have a case in which you have a server side property that's not mapped in the database, and need the client side to overwrite this value when the object is created at the client side.




回答2:


Some background for the reader.

It's important to differentiate

  • "unmapped" from the EF perspective
  • "not serialized" from the Json.Net perspective
  • "unmapped" from the Breeze perspective

[NotMapped] is an EF attribute that tells EF "this property is not mapped to the database".

[JsonIgnore] is a Json.Net attribute that tells JSON.Net "don't serialize this property when you send it to the client"

Only metadata can tell Breeze which properties are "mapped" ... from its perspective.

Breeze generally ignores incoming properties that aren'd defined in metadata. However, if a non-metadata property that is defined in the constructor, Breeze adds it to metadata and classifies it as "unmapped".

For Breeze that means "serialize this property, notify the UI when it changes, but this is not part of the entity's data model and should not be change-tracked (i.e., changes to it do not affect the EntityState)."

Let's put these ideas together to understand your question.

You told EF not to map the MyUnmappedProperty property. You're probably using the EFContextProvider to generate the metadata so that property isn't in the metadata either.

But Json.Net doesn't know about any of this. Therefore, whenever it serializes an instance of your TodoList, it sends the MyUnmappedProperty property ("testString") to the Breeze client.

At first Breeze won't do anything with it. If the server sends this property with any value, Breeze will ignore it.

But you added the MyUnmappedProperty property to the TodoList type constructor so now Breeze will recognize this property as "unmapped" and will materialize its value if it appears in the JSON query results.

When you create a new instance of TodoList (with new or entityManager.CreateEntity(...)), then MyUnmappedProperty property is set to '' per the ctor.

When you query for TodoList, the Json.NET sends {MyUnmappedProperty: "testString} in the JSON results. The Breeze client recognizes the MyUnmappedProperty, and accordingly sets that property on the materialized TodoList entity instance.

That's what is supposed to happen.

Can you demonstrate that a queried TodoList is ever materialized such that its MyUnmappedProperty is not "testString"?

You've checked the network traffic and you see that {MyUnmappedProperty: "testString} is actually coming over the wire but is not materialized? And you say that it is materialized in a second query?.

I need a repro of that!




回答3:


@Ward, Thank you for your answer.

First of all I forgot to mention that I am using Breeze version 1.4.0.

You've checked the network traffic and you see that {MyUnmappedProperty: "testString} is actually coming over the wire : Yes I have checked it and it is include in the JSON send from the server.

3 small additions are needed to the spa template to get the behavior I am experiencing.

In the todo.view.html I add the button "myGetBtn" and a span "mySpan" with an angular binding :

...
<button data-ng-click="addTodoList()">Add Todo list</button>
<br/>
<p>
    <button id="myGetBtn" ng-click="getTodos(true)">Get todos</button>
</p>
<article class="todoList" data-ng-repeat="list in todoLists">
    <header>
        <form data-ng-submit="endEdit(list)">
            <span id="mySpan">myNotMappedProperty: {{list.MyNotMappedProperty}}</span>
            <input  data-ng-model="list.title" 
                    data-selected-when="list.isEditingListTitle"
                    data-ng-click="clearErrorMessage(list)" 
                    data-on-blur="endEdit(list)"/>           
        </form>
    </header>
...

In the .Net POCO class TodoList I add

[NotMapped]
public string MyNotMappedProperty { get { return "testString"; } }

In the todo.model.js I modify the constructor like this:

function TodoList() {
        this.title = "My todos"; // defaults
        this.userId = "to be replaced";
        this.MyNotMappedProperty = "";
    }

Now if you run the app and successfully login the {{list.MyNotMappedProperty}} shows the empty string, if you then press the "Get Todos" button the {{list.MyNotMappedProperty}} shows the value "testString" .

In both cases the query is the same and the Json returned is the following:

[{"$id":"1","$type":"TodoTest.Models.TodoList, TodoTest","TodoListId":5,"UserId":"kostas","Title":"My todos","MyNotMappedProperty":"testString","Todos":[{"$id":"2","$type":"TodoTest.Models.TodoItem, TodoTest","TodoItemId":4,"Title":"dasdas","IsDone":false,"TodoListId":5,"TodoList":{"$ref":"1"}}]}]

I would like to get the "testString" shown in the first place without pressing the button I added.

I don't know if the info I provided is sufficient to reproduce the behavior so please tell me if you need any additional info.

Thank you.



来源:https://stackoverflow.com/questions/18011242/unmapped-property-on-the-angular-breeze-spa-template

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!