1
votes

I am using code first with an existing database, EF5, Web API and Breeze and I havent used any of these techs before. I am writing my own pocos.

I am trying to expose a read only property that requires several table joins to obtain the data. If we were using Web API only, we could just run some sql, populate the property and send some JSON back to the client.

Because we are using EF and breeze this obviously changes quite alot.

For example:

    public class Employee
{
    [Key]
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    [NotMapped]
    public string FooBar
    {
        get { return getFooBar(); }
    }

}
    private string getFooBar()
    {
        // Do stuff here
    }

This will send FooBar back to the client in the JSON result but because it is not mapped, and consequently not in the Metadata, I dont seem to be able to use it within Breeze.

I have read articles that say I can do this when using designer based methods (ie edit the edmx file) but how can it be done using code first?

I am aware that I can extend a Breeze entity on the client side but Im not really sure how I would get this value which hasnt been mapped, after Breeze has created all of the entities.

What I really want is to extend my code first entity. I also vaguely understand that this might not be in line with EF ideals but I also struggle with the idea that I dont have the freedom to define what is and what isnt a property of my employee.

I dont need to track changes. I dont need to save. I dont seem to be able the use the EF context provider to join the (many) tables and get the data because the entities for each table dont share a primary key and dont inherit from the same class.

I think this SO post here suggests something similar but once again its for generated classes. Is there a way to do this? Thanks.

Edit
In reply to Wards suggestion I tried a few tests.

My client side constructor:

function Employee() {
    this.DisplayName = ""; // unmapped property  
};

My Controller:

function TestController($scope, $routeParams) {
var manager = new breeze.EntityManager('breeze/employees');

var metadataStore = manager.metadataStore;
metadataStore.registerEntityTypeCtor("Employee", Employee);

var query = new breeze.EntityQuery()
    .from("Employees")
    .orderBy("FirstName");

manager.executeQuery(query).then(function (data) {

    // Check unmapped property name
    var employeeType = metadataStore.getEntityType("Employee");
    var unmapped = employeeType.unmappedProperties;
    alert(unmapped[0].name)     // Returns 'DisplayName'
    alert(employeeType.dataProperties[3].name) // Returns 'DisplayName'

    var prop = manager.metadataStore.getEntityType('Employee').getProperty('DisplayName');
    alert(prop.name) // Returns 'DisplayName'

    var first = data.results[0]
    var fullName = first.DisplayName
    alert(fullName) // Returns empty string                

    $scope.employees = data.results;
    $scope.$apply();

}).fail(function (e) {
    alert(e);
});
};

My Angular:

<div>
<ul>
    <li data-ng-repeat="employee in employees">
        {{employee.DisplayName}}
    </li>
</ul>
</div>

So the property seems to be setup correctly as an unmapped property, but it only returns the empty string. If I change

this.DisplayName = ""; // unmapped property

to

this.DisplayName = "Foo"; // unmapped property

then DisplayName always contains "Foo". The values from the payload are not being applied to DisplayName.

Am I missing something?

2

2 Answers

0
votes

It's pretty easy on the Breeze client as explained in the Extending Entities documentation topic: you define an unmapped property in a custom constructor and register that constructor.

var metadataStore = myEntityManager.metadataStore;

metadataStore .registerEntityTypeCtor("Employee", Employee);

function Employee () 
  this.FooBar = ""; // unmapped property
};

Now the Breeze metadata includes a definition of the FooBar unmapped property. The server will send a value for FooBar to the client and Breeze will populate that client Employee entity (unmapped) property when it materializes Employee entities from a query.

How you obtain that FooBar property value on the server is up to you. I don't know enough about your app. What you've shown us is a perfectly valid Code First entity definition.

Maybe you're asking an Entity Framework question rather than a Breeze question.

0
votes

One way to get this working has been discussed in this SO answer from CassidyK. Here is the code snippet.

 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 dont know what the side effects of this are. Perhaps one of the Breeze devs can better explain.

It seems this is only a problem when Breeze is configured for Angular.

IE

breeze.config.initializeAdapterInstance("modelLibrary", "backingStore", true);