In my scenario i've a MVC on iis serializing objects from entity framework where i've overridden GetHashCode and Equal methods since the Id of those objects is immutable once committed to the database.
i also have some client, who can't reach the database, but connect to iis to get those entities serialized via json and deserialize them locally using newtonsoft.json.
When i deserialize them in the clients for a second time, to refresh the data in them, i was expecting the existing instances to be updated automatically.
I'm expecting a little too much?
Should i write some clone method to copy properties and check a cache for existing ids?
Did i wrote something wrong in the Equal and GetHashCode methods?
For instance:
I've a blog entity with a title in the database
The client connect to iis
get a json string containing {"Id" : 1, "Name" : "blogName"}
deserialize it and store it locally
add post to the blog in the same way to an observable collection in the class blog i've used client side
Someone or something Change the blog name in the database
The client try to refresh
get the json string containing {"Id" : 1, "Name" : "newBlogName"}
deserialize it to a new instance of class blog, with same id
What's next? copy the new instance name to the old one, ore there's a better way?
Yes, you expect too much. You can attach new entity (because entity framework context doesn't know about this entity, because you get it from somewhere outside dbcontext) and then save it.
Answer here
//This place is where you can deserialize your entity
var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Blogs.Attach(existingBlog);
// Do some more work...
context.SaveChanges();
}
I think the best solution is to have two database one server side and one client side, with the same shared entity framework but different connection strings, json.net with the keep object reference setted and use the network only to sync the databases and then query it client side.
Related
I am playing around with a OData service and I am very confused when to use this
var oModel = new sap.ui.model.odata.ODataModel("proxy/http/services.odata.org/V3/(S(k42qhed3hw4zgjxfnhivnmes))/OData/OData.svc");
this.getView().setModel(oModel);
vs
var oModel = new sap.ui.model.odata.ODataModel("odatserviceurl", true);
var productsModel = new JSONModel();
oModel.read("/Products",
null,
null,
false,
function _OnSuccess(oData, response) {
var data = { "ProductCollection" : oData.results };
productsModel.setData(data);
},
function _OnError(error) {
console.log(error);
}
);
this.getView().setModel(productsModel);
I have two working example using both approach but I am not able to figure out why using read method if I can achieve same with first version. Please explain or guide me to the documentation which can clear my confusion.
Ok, lets start with the models:
JSON Model : The JSON model is a client-side model and, therefore, intended for small datasets, which are completely available on the client. The JSON model supports two-way binding. NOTE: no server side call is made on filtering, searching, refresh.
OData Model : The OData model is a server-side model: the dataset is only available on the server and the client only knows the currently visible rows and fields. This also means that sorting and filtering on the client is not possible. For this, the client has to send a request to the server. Meaning searching/filtering calls odata service again.
Now, lets look at scenarios where we will use these models:
Scenario 1: Showing data to user in a list/table/display form. Data manipulation is limited to searching and filtering. Here, I would use oData model directly to controls as only fetching of data is required.( your method 1) (NOTE: One way binding). Remember here all changes require a call to server.
Scenario 2: I have an application which has multiple inputs, user can edit changes, also some fields are calculated and mandatory. All in all, many user changes are done which may be temporary and user might not want to save them. Here, you dont want to send these temporary changes to backend as yet. You way want to manipulate, validate data before sending. Here, we will use JSON Model after reading data from odata model ( your method 2). Store the changes in local JSON model, validate and manipulate them and finally send the data using Odata create/update. Remember here all changes DO NOT require a call to server as data is present in local JSON MODEL.
Let me know if this helps you. :)
EDIT : Additional Information :
As per your comment :
Documentation says oModel.read' trigger get request but new sap.ui.model.odata.ODataModel("proxy/http/services.odata.org/V3/(S(k42qhed3hw4zgjxfnhivnmes))/OData/OData.svc")` does the same thing so why and when to use oModel.read
Here, is where you misunderstood. The code
new sap.ui.model.odata.ODataModel("proxy/http/services.odata.org/V3/(S(k42qhed3hw4zgjxfnhivnmes))/OData/OData.svc") will NOT send a read/get Request. It calls the odata services and fetches the metadata of the service. A service can have multiple entities.
For example: the service :http://services.odata.org/Northwind/Northwind.svc/ has mutiple entity sets such as Categories, Customers, Employees etc. So, when I declare : new sap.ui.model.odata.ODataModel("http://services.odata.org/Northwind/Northwind.svc/") it will fetch the metadata for service (not actual data). Only when you call the desired entity set, it will fetch the data. The Entity set is specified :
When you call the read method ( like you have specified '/Products')
Bind the entity set name directly to control like to List,Table etc ( items='{/Products}' )
I use code first in a web application where I have a form to upload text files and import the data into my database.
Each file may have up to 20.000+ records for import.
To speed things up I preload some entities so not to ask the DbContext every time. Then when I create an object for insert, I do for example:
myNewObject.Category = preloadedCategories.First(p => p.Code == code);
I have read some articles on the web because EF is extremey slow on batch inserts, so what I do is:
first use Configuration.AutoDetectChangesEnabled = false;
then every 1000 records I dispose the object and make a new one.
BUT! since the preloaded entities where loaded from a db context that was disposed, after making a new DbContext, I have a problem with preloadedCategories.First(p => p.Code == code). When I make a SaveChanges(), EF tries to also save the preloadedCategories.First(p => p.Code == code) object and fails.
So how can I achive this? I don't want to aks the DbContext every time to load some (non changing) objects. Is it possible?
thanks
When dealing with a large number of records in EF, a few things will help
As #janhartmann states, use .AsNoTracking()
As you stated, use Configuration.AutoDetectChangesEnabled = false, which will require the next point
Use context.Categories.Entry(category).State = EntityState.Modified to attach a disconnected entity to a context and mark is as modified
Also make check that preloadedCategories is no longer an IQuerable and that the data really is local and not trying to lazy load from the database.
If there are no changes to your Category object and you just want to link your myNewObject to an existing category, you have two options
Set the foreign key on myNewObject instead of the navigation property
Use context.Products.Entry(myNewObject).State = EntitySate.Added instead of context.Products.Add(myNewObject) to avoid it adding the entire graph of navigation properties
Good luck
We are parsing the SaveBundle on the server and returning a custom SaveResult. We want to be able to notify the client of additional changed entities as a result of processing the SaveBundle.
For example we have a SaveBundle from the client containing 1 entity to be deleted which when we parse and process on the server we actually delete 2 entities.
As far as we can tell the SaveResult does not contain any properties that would allow us to indicate an entity was 'deleted', rather than say 'modified'.
Is there a way to return additional entity changes through the SaveResult? Or is the only solution to refresh the data by resubmitting a Breeze query client side after the save changes?
I 'think' that if you return the deleted entities with their foreign keys set to null or empty (in the case of non-nullable guids etc.) in the SaveResult then Breeze client-side will detect this and mark them as deleted
I couldn't find anything explicitly in the documentation or the source about this though
here is your answer:
var result = context.SaveChanges(saveBundle);
//create your own EntityInfo object and fill it with the the entity and it's state
var entityInfo = new EntityInfo();
//...
//add it to the result
result.Entities.Add(entityInfo);
//return the result
return result;
Breeze client will then treat that entity like any other entity returned from you normal save proc.
Hope this helps
i´m using the Papa's course CCJS code to investigate Breeze.js and SPA. Using this code i´m trying to manage aditional information that cames from server but that is not an Entity contained in the Metadata that cames from EntityFramework.
So i created a NO-DB class called Esto and a Server method like Lookups:
[HttpGet]
public object Informacion()
{
var a = new Esto(....);
var b = new Esto(.....);
var c = new Esto(......);
return new {a,b,c};
}
then in model.js inside configureMetadataStore i call:
metadataStore.addEntityType({
shortName: "Esto",
namespace:"CodeCamper",
dataProperties:{
id: {dataType: breeze.DataType.Int32,isPartOfKey: true},
name: {dataType: breeze.DataType.String}
}
};
and also define in the model entityNames array: esto:'Esto' as an Entity
now in the context.js i load this creating a server side method like getLookups but called getInformacion:
function getInformacion(){
return EntityQuery.from('Informacion')
.using(manager).execute()
}
and then inside primeData in the success method call this:
datacontext.informacion = {
esto: getLocal('Esto',nombre)};
where getLocal is:
function getLocal(resource, ordering)
{
var query = EntityQuery.from(resource).orderBy(ordering);
return manager.executeQueryLocally(query);
}
I get an error in the query contained in the getLocal that states that Can not find EntityType for either entityTypeName: 'undefined' or resourceName:'Esto'.
What i´m doing wrong?
Thanks
You were almost there! :-) Had you specified the target EntityType in the query I think it would have worked.
Try this:
var query = EntityQuery.from(resource).orderBy(ordering).toType('Esto');
The toType() method tells Breeze that the top-level objects returned by this query will be of type Esto.
Why?
Let's think about how Breeze interprets a query specification.
Notice that you began your query, as we usually do, by naming the resource which will supply the data. This resource is typically a path segment to a remote service endpoint, perhaps the name of a Web API controller method ... a method named "Foos".
It's critical to understand that the query resource name is rarely the same as the EntityType name! They may be similar - "Foos" (plural) is similar to the type name "Foo" (singular). But the resource name could be something else. It could be "GetFoos" or "GreatFoos" or anything at all. What matters is that the service method returns "Foo" entities.
Breeze needs a way to correlate the resource name with the EntityType name. Breeze doesn't know the correlation on its own. The toType() method is one way to tell Breeze about it.
Why do remote queries work without toType()?
You generally don't add toType() to your queries. Why now?
Most of the time [1], Breeze doesn't need to know the EntityType until after the data arrive from the server. When the JSON query results includes the type name (as they do when they come from a Breeze Web API controller for example), Breeze can map the arriving JSON data into entities without our help ... assuming that these type names are in metadata.
Local cache queries are different
When you query the cache ... say with executeQueryLocally ... Breeze must know which cached entity-set to search before it can query locally.
It "knows" if you specify the type with toType(). But if you omit toType(), Breeze has to make do with the query's resource name.
Breeze doesn't guess. Instead, it looks in an EntityType/ResourceName map for the entity-set that matches the query resource name.
The resource name refers to a service endpoint, not a cached entity-set. There is no entity-set named "Informacion", for example. So Breeze uses an EntityType/ResourceName map to find the entity type associated with the query resource name.
EntityType/ResourceName
The EntityType/ResourceName map is one of the items in the Breeze MetadataStore. You've probably never heard of it. That's good; you shouldn't have to think about it ... unless you do something unusual like define your own types.
The map of a new MetadataStore starts empty. Breeze populates it from server metadata if those metadata contain EntityType/Resource mappings.
For example, the Breeze EFContextProvider generates metadata with mappings derived from DbSet names. When you define a Foo class and exposed it from a DbContext as a DbSet named "Foos", the EFContextProvider metadata generator adds a mapping from the "Foos" resource name to the Foo entity type.
Controller developers tend to use DbSet names for method names. The conventional Breeze Web API controller "Foo" query method looks like this:
[Get]
public IQueryable<Foo> Foos() {...}
Now if you take a query such as this:
var query = EntityQuery.from('Foos').where(...);
and apply it to the cache
manager.query.executeLocally(query).then(...);
it just works.
Why? Because
"Foos" is the name of a DbSet on the server
The EFContextProvider generated metadata mapping ["Foos" to Model.Foo]
The Web API Controller offers a Foos action method.
The BreezeJS query specifies "Foos"
The executeLocally method finds the ["Foos"-to-Model.Foo] mapping in metadata and applies the query to the entity-set for Foo.
The end-to-end conventions work silently in your favor.
... until you mention a resource name that is not in the EntityType/ResourceName map!
Register the resource name
No problem!
You can add your own resource-to-entity-type mappings as follows:
var metadataStore = manager.metadataStore;
var typeName = 'some-type-name';
var entityType = metadataStore.getEntityType(typeName);
metadataStore.setEntityTypeForResourceName(resource, entityType);
Breeze is also happy with just the name of the type:
metadataStore.setEntityTypeForResourceName(resource, typeName);
In your case, somewhere near the top of your DataContext, you could write:
var metadataStore = manager.metadataStore;
// map two resource names to Esto
metadataStore.setEntityTypeForResourceName('Esto', 'Esto');
metadataStore.setEntityTypeForResourceName('Informacion', 'Esto');
Don't over-use toType()
The toType() method is a good short-cut solution when you need to map the top-level objects in the query result to an EntityType. You don't have to mess around with registering resource names.
However, you must remember to add toType() to every query that needs it. Configure Breeze metadata with the resource-to-entity-type mapping and you'll get the desired behavior every time.
Notes
[1] "Most of the time, Breeze doesn't need to know the EntityType until after the data arrive from the server." One important exception - out of scope for this discussion - is when the query filter involves a Date/Time.
I think that the problem here is that you are assuming that entity type names and resource names are the same thing. A resource name is what is used to execute a query
var q = EntityQuery.from(resourceName);
In your case the "resourceName" is "Informacion" and the entityType is actually "Esto". Breeze is able to make this connection on a remote query because it can examine the results returned from the server as a result of querying "Informacion" and seeing that they are actually "Esto" instances. This is not possible for a local query because Breeze doesn't know what local collection to start from.
In this case you need to give Breeze a little more information via the MetadataStore.setEntityTypeForResourceName method. Something like this:
var estoType = manager.metadataStore.getEntityType("Esto");
manager.metadataStore.setEntityTypeForResourceName("Informacion", estoType);
Note that this is not actually necessary if the resource was defined via Entity Framework metadata, because Breeze automatically associates all EF EntitySet names to resource names, but this information isn't available for DTO's.
Note also that a single entity type can have as many resourceNames as you like. Just make sure to register the resourceNames before you attempt a local query.
We are trying to develop our own EF provider for our legacy APIs. We managed to get "GET/POST" operation working successfully.
However, for operation "PUT/MERGE", the method "CreateDbCommandDefinition" (of DbProviderServices implementation) fires twice. One with "DbQueryCommandTree" and another with "DbUpdateCommandTree".
I understand that it needs to fetch the entity prior to update it (for change tracking I guess). In our case, I don't need the entity information to be fetched prior to update. I simply want to call our legacy APIs with the entity sent for update. How can we strictly ask it to not to do the work of "DbQueryCommandTree" (and do only the work of "DbUpdateCommandTree") when I working with "PUT/MERGE" operations.
The client code looks something like the one below:
public void CustomerUpdateTest()
{
try
{
Ctxt.MergeOption = MergeOption.NoTracking;
var oNewCus = new Customer()
{
MasterCustomerId = "1001",
SubCustomerId = "0",
FirstName = "abc",
LastName = "123"
};
Ctxt.AttachTo("Customers", oNewCus);
Ctxt.UpdateObject(oNewCus);
//Ctxt.SaveChanges();
Ctxt.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
}
catch (Exception ex)
{
Assert.Fail(ex.Message);
}
You will have to write your own IDataServiceUpdateProvider to make this happen. For EF, the in built EF update provider does 2 queries - one to get the entity which needs to be modified and one for the actual modification. We are planning to make this provider public in our next release, so folks can derive from it and just override one or more methods. But for now, you will have to implement the interface yourself.
For PUT/MERGE requests, WCF Data Services calls IDataServiceUpdateProvider.GetResource to get the entity to update. In your implementation of this method, you can return a token that represents the object that need to get modified (you will have to visit the expression tree that gets passed in this method to find out the entity set and the key value of the entity in question).
In SaveChanges, you can push the update based on the token. That way you can avoid one round trip to the database.
Hope this helps.