Breeze JS Query before calling SaveChanges causes failure - sql

I have the following very standard Breeze API Controller endpoint in my app:
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
My backend database uses int IDs that are configured as identities.
When called from a client with a valid change set, all is well.
However, if, prior to the call to my _contextProvider.SaveChanges(saveBundle) function, I make a query of any sort. For example:
public SaveResult SaveChanges(JObject saveBundle)
{
int incomingUserId = Convert.ToInt32(saveBundle["entities"][0]["Id"]);
AspNetUser EditedUser = (from u in Context.AspNetUsers where u.Id == incomingUserId select u).FirstOrDefault();
// ......
// do something with the EditedUser (like validations of any sort)
// ......
return _contextProvider.SaveChanges(saveBundle);
}
the save fails with error:
Saving or accepting changes failed because more than one entity of type 'xxxx.App_Data.AspNetUser' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration.
Given what I am doing, is this expected behavior? Is there any other way to do an initial, separate query just prior to the SaveChanges call without upsetting something and making the query fail?

You'll need to create a second Context for performing the query. In the code you have above, the EditedUser object is being cached in the Context, because that's what EF does. Then, when _contextProvider.SaveChanges(saveBundle) is called, the contextProvider tries to create an object with the same type and same key in the same Context. Hence the error.
This SO post gives some hints about creating the second Context.

Related

In Identity for Entity Framework Core 3.x, how do I find users by Email (minding normalization) when Email uniqueness is not enforced?

userManager.FindByEmailAsync(myEmail) throws an exception if there are multiple users with the same email.
I could use:
await context.ApplicationUsers
.FirstOrDefaultAsync(x => x.NormalizedEmail == myEmail.ToUpperInvariant());
That seems to work okay. But I'm not sure if ToUpperInvariant is the right way to check, because System.Text also has Normalize(). It won't matter right now since we are using SQL Server with a case-insensitive configuration, but I don't want things to break if we ever change that.
Am I normalizing in a way that is consistent with how Entity Framework does it? I tried to find the source code, but what I found doesn't use the NormalizedEmail field, so it's likely old.
The normalization is not done by the EF Core, but the UserManager class (using ILookupNormalizer service injected via constructor or set via KeyNormalizer property).
UserManager.FindByEmailAsync method does the normalization for you before calling the store method. The problem is that EF Core store method implementation uses SingleOrDefaultAsync which throws if there are duplicate normalized emails in the database.
To fix that, you could use UserManager.NormalizeEmail method to do the normalization, and then use FirstOrDefaultAsync query as in your sample:
var normalizedEmail = userManager.NormalizeEmail(myEmail);
var firstDuplicate = await userManager.Users
.FirstOrDefaultAsync(x => x.NormalizedEmail == normalizedEmail);

Save or Merge Patch Entity

I want following functionality in spring data rest.
If I post to a collection resource end point, server should check if the object exists. if it exists already it should perform the same functionality as it does with merge-patch on item resource. If object does not exist already it should create it.
Is this achievable in spring data rest. If so then how?
If it is possible in your use case, you might want to use PUT instead of POST, as PUT should work as you expected.
Solution with POST
You can achieve the desired behavior with Spring Data REST Event handlers.
Just create a Handler method which accepts your entity and annotate it with #HandleBeforeCreate. In this method, you can implement your behavior, i.e. check if the object exists and update it manually or just do nothing and let the Spring Data REST handle the entity creation.
#RepositoryEventHandler
public class EntityEventHandler {
#Autowired
private EntityService entityService;
#HandleBeforeCreate
public void handleEntityCreate(Entity e) {
if (entityService.exists(e)) {
entityService.update(e);
}
}
}
EDIT:
I just realized that you would also need to stop the create event after your update. You might try throwing a custom Exception and Handling it to return 200 and the updated entity.

System.InvalidOperationException in EF core when trying to update

Problem
I am creating a web app in asp.net core with ef core using with Repository pattern. Ii am trying to insert an entity it works fine when i trying to check whether it is already exist in db or then if yes than update the same entity in same api it gives me this error.
System.InvalidOperationException: 'The instance of entity type 'Consultation' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.'
Image
code
insert api
[HttpPost]
public ApiResponse InsertConsultation([FromBody] Consultation consultation)
{
if (!ModelState.IsValid)
{
return new ApiResponse(StatusCodes.Status400BadRequest, error: "error");
}
var consult = _consultationService.GetConsultationById(consultation.Id);
if (consult !=null)
{
_consultationService.UpdateConsultation(consultation);
return new ApiResponse(StatusCodes.Status200OK, success: "isSuccess");
}
_consultationService.InsertConsultation(consultation);
return new ApiResponse(StatusCodes.Status201Created, success: "isSuccess");
}
The update process is
1- Retrieve the object which is consult in your code:
var consult = _consultationService.GetConsultationById(consultation.Id);
2- make change to the retrieval object (copy content from consultation to consult) you don't have it.
3- update the object consult
4- save change.
Maybe try this for the line before SaveChanges().
_context.AddOrUpdate(entity);
This needs the System.Data.Entity.Migrations namespace.
Your question might already be answered here:
Update Row if it Exists Else Insert Logic with Entity Framework

Knowledge & Connect PHP API, Found object(Account or Answer) but contains only null fields

I'm facing some strange issues when I try to fetch(Connect PHP API)/searchContent(Knowledge Foundation API) following the tutorials/documentations.
Behaviour and output
Following the documentation, we initialize the API. The function error_get_last() (called after the fetch) states that the core read-only file (we are not allowed to modify it) contains an error:
Array ( [type] => 8 [message] => Undefined index: REDIRECT_URL [file] => /cgi-bin/${interface_name}.cfg/scripts/cp/core/framework/3.2.4/init.php [line] => 246 )
After initialization, we call the fetch function to retrieve an account. If we give a wrong ID, it returns an error:
Invalid ID: No such Account with ID = 32
Otherwise, furnishing a correct ID returns an Account object with all fields populated as NULL:
object(RightNow\Connect\v1_2\Account)#22 (25) {
["ID"]=>
NULL
["LookupName"]=>
NULL
["CreatedTime"]=>
NULL
["UpdatedTime"]=>
NULL
["AccountHierarchy"]=>
NULL
["Attributes"]=>
NULL
["Country"]=>
NULL
["CustomFields"]=>
NULL
["DisplayName"]=>
NULL
["DisplayOrder"]=>
NULL
["EmailNotification"]=>
NULL
["Emails"]=>
NULL
["Login"]=>
NULL
/* [...] */
["StaffGroup"]=>
NULL
}
Attempts, workaround and troubleshooting information
Configuration: The account used using the InitConnectAPI() has the permissions
Initialization: Call to InitConnectAPI() not throwing any exception(added a try - catch block)
Call to the fetch function: As said above, the call to RNCPHP\Account::fetch($act_id) finds the account (invalid_id => error) but doesn't manage to populate the fields
No exception is thrown on the RNCPHP::fetch($correct_id) call
The behaviour is the same when I try to retrieve an answer following a sample example from the Knowledge Foundation API : $token = \RNCK::StartInteraction(...) ; \RNCK::searchContent($token, 'lorem ipsum');
Using PHP's SoapClient, I manage to retrieve populated objects. However, It's not part of the standard and a self-call-local-WebService is not a good practice.
Code reproducing the issue
error_reporting(E_ALL);
require_once(get_cfg_var('doc_root') . '/include/ConnectPHP/Connect_init.phph');
InitConnectAPI();
use RightNow\Connect\v1_2 as RNCPHP;
/* [...] */
try
{
$fetched_acct = RNCPHP\Account::fetch($correct_usr_id);
} catch ( \Exception $e)
{
echo ($e->getMessage());
}
// Dump part
echo ("<pre>");
var_dump($fetched_acct);
echo ("</pre>");
// The core's error on which I have no control
print_r(error_get_last());
Questions:
Have any of you face the same issue ? What is the workaround/fix which would help me solve it ?
According to the RNCPHP\Account::fetch($correct_usr_id) function behaviour, we can surmise that the issue comes from the 'fields populating' step which might be part of the core (on which I have no power). How am I supposed to deal with this (fetch is static and account doesn't seem abstract) ?
I tried to use the debug_backtrace() function in order to have some visibility on what may go wrong but it doesn't output relevant information. Is there any way I can get more debug information ?
Thanks in advance,
Oracle Service Cloud uses lazy loading to populate the object variables from queried data using Connect for PHP APIs. When you output the result of an object, it will appear as each variable is empty, per your example. However, if you access the parameter, then it becomes available. This is only an issue when you try to print your object, like this example. Accessing the data should be immediate.
To print your object, like in your example, you would need to iterate through the object variables and access each one first. You could build a helper class to do that through reflection. But, to illustrate with a single field, do the following:
$acct = RNCPHP\Account::fetch($correctId);
$acct->ID;
print_r($acct); // Will now "show" ID, but none of the other fields have been loaded.
In the real world, you probably just want to operate on the data. So, even though you cannot "see" the data in the object, it's there. In the example below, we're accessing the updated time of the account and then performing an action on the object if it meets a condition.
//Set to disabled if last updated < 90 days ago
$acct = RNCPHP\Account::fetch($correctId);
$chkDate = time() - 7776000;
if($acct->UpdatedTime < $chkDate){
$acct->Attributes->PermanentlyDisabled = true;
$acct->save(RNCPHP\RNObject::SuppressAll);
}
If you were to print_r the object after the if condition, then you would see the UpdatedTime variable data because it was loaded at the condition check.

How to save and then update same class instance during one request with NHibernate?

I'm relatively new to NHibernate and I've got a question about it.
I use this code snippet in my MVC project in Controller's method:
MyClass entity = new MyClass
{
Foo = "bar"
};
_myRepository.Save(entity);
....
entity.Foo = "bar2";
_myRepository.Save(entity);
The first time entity saved in database succesfully. But the second time not a single request doesnt go to database. My method save in repository just does:
public void Save(T entity)
{
_session.SaveOrUpdate(entity);
}
What should I do to be able to save and then update this entity during one request? If I add _session.Flush(); after saving entity to database it works, but I'm not sure, if it's the right thing to do.
Thanks
This is the expected behavior.
Changes are only saved on Flush
Flush may be called explicitly or implicitly (see 9.6. Flush)
When using an identity generator (not recommended), inserts are sent immediately, because that's the only way to return the ID.
you should be using transactions.
a couple of good sources: here and here.
also, summer of nHibernate is how I first started with nHibernate. it's a very good resource for learning the basics.