0
votes

i'm evaluating Breeze.Js for a large enterprise, data oriented, Angular 5 application in order to take advantage of the following features that are missing in the vanilla Angular framework:

  • client side data store
  • client side model state tracking
  • client side model validation rules
  • bulk data persistence (SaveChanges() method to persist all entities).

For test purposes i've written the following simple BreezeController in my ASP.NET WebApi + EntityFramework server side:

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [BreezeController]
    public class PeopleController : ApiController
    {
        private AdventureWorksDbContext db = new AdventureWorksDbContext();


        #region "Breeze"

        readonly EFContextProvider<AdventureWorksDbContext> _contextProvider =
        new EFContextProvider<AdventureWorksDbContext>();

        // ~/breeze/todos/Metadata 
        [HttpGet]
        public string Metadata()
        {
            return System.Text.Encoding.UTF8.GetString(AdventureWorks.WebApi.Properties.Resources.WebApiMetadata); 
        }

        // ~/breeze/todos/Todos
        // ~/breeze/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt 
        [HttpGet]
        public IQueryable<PersonDTO> GetPeople()
        {
            return db.People.ProjectTo<PersonDTO>();
        }

        // ~/breeze/todos/SaveChanges
        [HttpPost]
        public SaveResult SaveChanges(Newtonsoft.Json.Linq.JObject saveBundle)
        {
            return _contextProvider.SaveChanges(saveBundle);
        }

        #endregion
}

As you can see in my example (it uses AdventureWorks DB) i've done the following modifications: 1) "GetPeople()" endpoint returns a queryable of DTO ("ProjectTo" extension is provided by Automapper). I need to do this in order to shape the model in a usable way for the client, avoid recursions, deep dive in the schema, big fields serialization and so on.

2) "Metadata()" endpoint returns a string resource that represents metadata of the DTO class. I builded it using "PocoMetadata" tool of the "Breeze Tooling Suite" (https://github.com/Breeze/breeze.tooling). This is needed because i can't return the _contextProvider.Metadata() result as long as i'm using DTO's and not EF POCO class.

Now, if in my Angular 5 client i issue an ODATA query like the following i can see that executeQuery() method actually works:

export class BreezeDataStoreComponent implements OnInit {

private _em: EntityManager;

  constructor() {

    this._em = new EntityManager({
      serviceName: 'http://localhost:31328/breeze/People'
    });
   }

  ngOnInit() {

    const query = EntityQuery.from('GetPeople')
                           .where('FirstName', FilterQueryOp.StartsWith, 'F')
                           .orderBy('LastName', true);
    this._em.executeQuery(query).then(res => {

      // Here i can get all People instances.
      // Now i try to get the first, edit the name and saveChanges.
      (res.results[0] as any).FirstName = 'Franklino';

      this._em.saveChanges().then(saveResult => {
          const test = saveResult.entities;
      });
    });
  }    
}

Unfortunately problems comes with SaveChanges(). When the Angular client calls that method, in my server side i get the following error:

System.InvalidOperationException: Sequence contains no matching element

I think it's due to the fact that i'm calling SaveChanges() over an EF context provider passing a JObject bundle referred to DTO instead of POCO class.

So my question is:

Is it possible to use BreezeJs query and bulk persistence (SaveChanges() method) using DTO's? It's a pretty common need in big data-centric enterprise applications since i think it's a bad practice exposing EF POCOs on WebApi.

should i rely instead over a classic WebApi that respond to the POST\PUT\DELETE HTTP verbs? In that case, how to configure Breeze client in order to contact those endpoints instead of "SaveChanges" when persisting data?

If Breeze is not suitable for this needs are there other technolgies that provides the 4 abovementioned points?

Thank you very much.

1
Did you see the advice of breeze themselves?Gert Arnold
Yes I read it, it shows how to generate metadata from EF, it should be the same thing I've done using the Poco Metadata generator of Breeze Tooling (I read it so a few days ago) . Unfortunately this tutorial doesn't show how to handle persistence. Querying works but what about saving changes?GiveEmTheBoot

1 Answers

1
votes

To make SaveChanges work with your DTOs, you would need to either

  1. Write your own method to unpack the JObject saveBundle, or
  2. Use the BeforeSaveChanges method to modify the dictionary of DTOs and replace them with entities that EF understands.

Number 2 seems like the better choice. If you do not have a 1:1 match between entities and DTOs, some logic would be required when doing the mapping.