So I'm trying to prevent a race condition between applications.
Using IsolationLevel/TransactionScope, I can lock the table the way I need to, but need to run the update operation first, then operate on the list of modified objects.
To do this, I need to run the update and get the list of updated ID's all in one shot.
If I were to try to take the IDs first, that wouldn't lock the table, and another app instance could query for that same list, before they were flagged.
Is there a way to do something like:
//modify some objects
var updatedIds = context.SaveChanges();
//Process updatedIds
Is there a way to do this? I've tried looking through the ObjectContext entries, but after the Save there doesn't seem to be anything.
Maybe I'll have to do an sproc?
This code can go into your Context class and should give you what you need
public override int SaveChanges()
{
using (var scope = new System.Transactions.TransactionScope())
{
//pre processing
var result = base.SaveChanges();
//post processing
scope.Complete();
return result;
}
}
Related
Hiy!
I want all objects(rows in Test Type) with ModelService
So I could iterate through collection and update a Single row (object)'s attribute with new value
I see getModelService.create(TestModel.class) and getModelService.save()
but will they not create a new object/row rather than update a existing object?right
I don't want to create a new one rather selecting one of the existing matching my criteria and update one attribute of that
can somebody help with List<TestModel> testModels = getModelService.get(TestModel.class) will that return me all rows (collection) of Test Type/Table?
unfortunately I can't test it so need help
Actually I am in validateInterceptor ... and on the basis of this intercepted model changed attribute value I have to update another model attribute value...
thanks
ModelService.create(new TestModel.class) will create a single instance of the specified type and attach it to the modelservice's context.
But it will only be saved to the persistence store when you call modelService.save(newInstance)
ModelService.get() returns a model object but expects a Jalo object as input, (Jalo being the legacy persistence layer of hybris) so that won't work for you.
To retrieve objects you can either write your own queries using the FlexibleSearchService or you can have a look at the DefaultGenericDao which has a bunch of simple find() type of methods.
Typically you would inject the dao like e.g.:
private GenericDao<TestModel> dao;
[...]
public void myMethod()
{
List<TestModel> allTestModels = dao.find();
[...]
}
There are a lot more methods with which you can create WHERE type of statements to restrict your result.
Regarding ValidateInterceptor:
Have a look at the wiki page for the lifecycle of interceptors:
https://wiki.hybris.com/display/release5/Interceptors
It's not a good idea to modify 'all' objects of a type while being an interceptor of that type.
So if you're in an interceptor declared for the Test item type, then don't try to modify the items there.
If you happen to be in a different interceptor and want to modify items of a different type:
E.g. you have Type1 which has a list of Type2 objects in it and in the interceptor for Type1 you want to modify all Type2 objects.
For those scenarios you would have to add the instances of Type2 that you modify to the interceptor context so that those changes will be persisted.
That would be something like:
void onValidate(Test1 model, InterceptorContext ctx) throws InterceptorException
{
...
List<Type2> type2s = dao.find();
for (Type2 type2 : type2s)
{
// do something with it
// then make sure to persist that change
ctx.registerElementFor(type2, PersistenceOperation.SAVE);
[...]
}
}
First of all - i think it's not a good idea, to create/update models in any interceptor, especially in 'validation' one.
Regarding your question:
ModelService in most of the cases works with single model, and
designed for create/update/delete operations.
To retreive all models of certain type, you have to use FlexibleSearchService
Then to update each retrieved TestType model, you can use ModelService's save method.
A query to retreive all TestType models will look like:
SELECT PK FROM {TestType}
You could simply use the Flexible Search Service search by example method, and the model service to save them all. Here is an example using Groovy script, with all products :
import java.util.List
import de.hybris.platform.core.model.product.ProductModel
import de.hybris.platform.servicelayer.search.FlexibleSearchService
import de.hybris.platform.servicelayer.model.ModelService
FlexibleSearchService fsq = spring.getBean("flexibleSearchService")
ModelService ms = spring.getBean("modelService")
ProductModel prd = ms.create(ProductModel.class)
List<ProductModel> products = fsq.getModelsByExample(prd)
//Do Whatever you want with the objects in the List
ms.saveAll(products)
I am new to BreezeJS and would like to know if there are any examples on how to use Breeze with an SQL Stored Procedure?
We have some pretty complex queries and want to be able to call them via an SP. Also, how can we tell Breeze that a column returned from a SP is the Key? We don't want to use Views, because we need to pass variables to the SP query each time we call it.
Thanks.
bob
Ok, the basic idea would be to use Breeze's EntityQuery.withParameters method to pass parameters to a server side method that calls your stored proc and returns an IEnumerable. ( i.e. the result of the stored proc).
If you want to treat this result as collection of Breeze entities then you will either need to shape the results into an existing entity type that Breeze knows about from Metadata OR manually create and add a new EntityType on the client that matches the shape that you want to return.
You may want to look at the EntityQuery.toType method to force breeze to convert your returned data into a specific EntityType or you might alternately want to use a "jsonResultsAdapter" to do the same thing.
Any data that is returned from a query and is converted into an Breeze EntityType is automatically wrapped according the "modelLibrary" in use, i.e. Knockout, Angular, Backbone etc.
If breeze is not able to construct entities out of the returned data then it will still be returned but without any special processing to wrap the result.
Hope this helps!
A sample to access Sql Stored Procedures from Breeze; the store procedure (GoStoCde) has been imported by EF.
Breeze Controller :
[HttpGet]
public object GetCdes(long jprod, int jqte, long jorder)
{
//output params
var owrk = new System.Data.Objects.ObjectParameter("wkres", typeof(string));
owrk.Value = "";
var oeror = new System.Data.Objects.ObjectParameter("ceror", typeof(int));
oeror.Value = 0;
//invoke stored procedure
var envocde = _contextProvider.Context.GoStoCde(jprod, jqte, jorder, owrk, oeror);
//stored procedure results
var cdeResult = new {
dwork = owrk.Value,
deror = oeror.Value,
};
return new { cdeResult };
}
Datacontext :
function reqLnecde(iprod, iqte, iorder, vxeror) {
logger.log("commande en cours...");
var query = new EntityQuery.from("GetCdes")
.withParameters({ jprod: iprod, jqte: iqte, jorder: iorder });
return manager
.executeQuery(query)
.then(querySucceeded)
.fail(cqueryFailed);
function querySucceeded(data) {
//stored procedure results
vxeror(data.results[0]);
//stored procedure object member value
keror = vxeror().cdeResult.deror;
if (keror === 0) {
logger.log("commande done");
} else {
logger.log("article absent");
}
}
function queryFailed(data) {
logger.log("commande failed"); //server errors
}
}
If you prefer to return entity in lieu of object, code consequently and its must also work.
Hope this helps!
Not really an answer here, just a few thoughts.
I think that the ability to return arbitrarily shaped data (read viewmodel) through the use of a stored procedure using withParameters would be an excellent way to inegerate with something like dapper.net. Upon resubmission of said viewmodel you could use the overloads to reconstruct actual entities out of your viewmodel and save changes. The only problem I have though is that one would need a way to easily and automaticaly rerun the sproc and send the data back to the client...
I would like to know if this makes sense to anyone else and/or if anyone has done it already.
For this sort of scenario I would think that you would need to disable the tracking features provided by breeze and/or write a smart enough data service that can handle the viewmodels in such a way that the javascript on the client knows when adding/removing/updating parts x,y,z of viewmodel a that you create objects jx, jy, jz (j for javascript) and submit them back and save as you go (reverse idea of what was mentioned above in a way)
Thoughts?
I'm trying to update an entity using Session.Update then continue to execute another SQL query. The other query did not see the changed value. When i traced it using profiler, Session.Update did nothing.
public class InvoiceService()
{
public void Update(Invoice invoice)
{
using (var trans = BeginTransaction())
{
Session.Update(invoice); //Nhibernate did not update invoice.
ExecuteNamedQuery(); //Query executed before invoice updated.
trans.Commit(); //Invoice updated.
}
}
}
Then i add Session.Flush after Session.Update.
using (var trans = BeginTransaction())
{
Session.Update(invoice);
Session.Flush()
ExecuteNamedQuery();
trans.Commit();
}
After Session.Flush executed, SQL query for update is executed also.
It works perfectly. The execution order is correct. But then i executed another method to get all invoices. Committing transaction makes nhibernate execute update query to update my updated invoice earlier with old values. (ex: Quantity = 20, updated to 10, then updated again to 20)
public void FindAll()
{
using (var trans = BeginTransaction())
{
var invoices = Session.CreateCriteria<Invoice>().List<Invoice>();
trans.Commit(); // In here invoice that i updated earlier get updated again, using old values.
return invoices;
}
}
Why it's getting updated again?
What's the solution for this problem?
Thanks in advance.
Update is an unfortunate name for the method; the purpose of Update is to attach a transient instance to a new session. See the documentation for update and make sure you understand instance states.
The invoice is updated to the original values because NHibernate thinks it has changed. This "phantom" update may be caused by a property changing unexpectedly. A typical root cause is a nullable database column mapped to a non-nullable property (or vice-versa). The easiest way to troubleshoot is to turn on dynamic-update in the session factory configuration so that you can see which properties NHibernate detects as dirty.
I have recently started evaluating Dapper as a potential replacement for EF, since I was not too pleased with the SQL that was being generated and wanted more control over it. I have a question regarding mapping a complex object in my domain model. Let's say I have an object called Provider, Provider can contain several properties of type IEnumerable that should only be accessed by going through the parent provider object (i.e. aggregate root). I have seen similar posts that have explained using the QueryMultiple and a Map extension method but was wondering how if I wanted to write a method that would bring back the entire object graph eager loaded, if Dapper would be able to do this in one fell swoop or if it needed to be done piece-meal. As an example lets say that my object looked something like the following:
public AggregateRoot
{
public int Id {get;set;}
...//simple properties
public IEnumerable<Foo> Foos
public IEnumerable<Bar> Bars
public IEnumerable<FooBar> FooBars
public SomeOtherEntity Entity
...
}
Is there a straightforward way of populating the entire object graph using Dapper?
I have a similar situation. I made my sql return flat, so that all the sub objects come back. Then I use the Query<> to map the full set. I'm not sure how big your sets are.
So something like this:
var cnn = sqlconnection();
var results = cnn.Query<AggregateRoot,Foo,Bars,FooBar,someOtherEntity,AggregateRoot>("sqlsomething"
(ar,f,b,fb,soe)=>{
ar.Foo = f;
ar.Bars = b;
ar.FooBar = fb;
ar.someotherentity = soe;
return ar;
},.....,spliton:"").FirstOrDefault();
So the last object in the Query tag is the return object. For the SplitOn, you have to think of the return as a flat array that the mapping will run though. You would pick the first return value for each new object so that the new mapping would start there.
example:
select ID,fooid, foo1,foo2,BarName,barsomething,foobarid foobaritem1,foobaritem2 from blah
The spliton would be "ID,fooid,BarName,foobarid". As it ran over the return set, it will map the properties that it can find in each object.
I hope that this helps, and that your return set is not too big to return flat.
I have a User table and a ClubMember table in my database. There is a one-to-one mapping between users and club members, so every time I insert a ClubMember, I need to insert a User first. This is implemented with a foreign key on ClubMember (UserId REFERENCES User (Id)).
Over in my ASP.NET MVC app, I'm using LinqToSql and the Repository Pattern to handle my persistence logic. The way I currently have this implemented, my User and ClubMember transactions are handled by separate repository classes, each of which uses its own DataContext instance.
This works fine if there are no database errors, but I'm concerned that I'll be left with orphaned User records if any ClubMember insertions fail.
To solve this, I'm considering switching to a single DataContext, which I could load up with both inserts then call DataContext.SubmitChanges() only once. The problem with this, however, is that the Id for User is not assigned until the User is inserted into the database, and I can't insert a ClubMember until I know the UserId.
Questions:
Is it possible to insert the User into the database, obtain the Id, then insert the ClubMember, all as a single transaction (which can be rolled back if anything goes wrong with any part of the transaction)? If yes, how?
If not, is my only recourse to manually delete any orphaned User records that get created? Or is there a better way?
You can use System.Transactions.TransactionScope to perform this all in an atomic transaction, but if you are using different DataContext instances, it will result in a distributed transaction, which is probably not what you really want.
By the sounds of it, you're not really implementing the repository pattern correctly. A repository should not create its own DataContext (or connection object, or anything else) - these dependencies should be passed in via a constructor or public property. If you do this, you'll have no problem sharing the DataContext:
public class UserRepository
{
private MyDataContext context;
public UserRepository(MyDataContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public void Save(User user) { ... }
}
Use the same pattern for ClubMemberRepository (or whatever you call it), and this becomes trivial:
using (MyDataContext context = new MyDataContext())
{
UserRepository userRep = new UserRepository(context);
userRep.Save(user);
ClubMemberRepository memberRep = new ClubMemberRepository(context);
memberRep.Save(member);
context.SubmitChanges();
}
Of course, even this is a little bit iffy. If you have a foreign key in your database, then you shouldn't even need two repositories, because Linq to SQL manages the relationship. The code to create should simply look like this:
using (MyDataContext context = new MyDataContext())
{
User user = new User();
user.Name = "Bob";
user.ClubMember = new ClubMember();
user.ClubMember.Club = "Studio 54";
UserRepository userRep = new UserRepository(context);
userRep.Save(user);
context.SubmitChanges();
}
Don't fiddle with multiple repositories - let Linq to SQL handle the relationship for you, that's what ORMs are for.
Yes, you can do it in a single transaction. Use the TransactionScope object to begin and commit the transaction (and rollback if there is an error of course)