One to Many relationship using breezejs - angularjs

I have the following one to many relationship setup between two tables (Case and Recipient)
Case table has one primary key which is identified as an identity (auto-increment) tblRecipient has one primary key which is identified as an identity (auto-increment),and a foreign key relationship with Case.
Case table and related tblRecipient data is pulled:
function searchCases(caseID, caseNum)
{
var qPredicate;
if (caseID != '')
{
qPredicate = new breeze.Predicate("pkCaseID", "==", parseInt(caseID));
}
else if (caseNum != null)
{
qPredicate = new breeze.Predicate("CaseNumber", "Contains", caseNum);
}
var query = breeze.EntityQuery
.from("case")
.where(qPredicate)
.expand("tblRecipients")
return manager.executeQuery(query);
}
When a button is pressed to add a new recipient, the following code is used to create a new recipient entity:
function createNewRecipient(CaseID)
{
var recipientEntityType = manager.metadataStore.getEntityType("tblRecipient");
var newRecipient = manager.createEntity(recipientEntityType, {fkCaseID: CaseID});
return newRecipient;
}
This code returns this error:
Error: Cannot attach an object to an EntityManager without first setting its key or setting its entityType 'AutoGeneratedKeyType' property to something other than 'None'
The AutoGeneratedKeyType in the metadata shows None instead of Identity as in the Case table. We are not sure what we need to change or how to really debug this. Any help would be appreciated.

Related

Update Parent Object Field in Apex Trigger

I have three objects
1)Truck__c
2)Booking__C
3)Payment___C.
Truck and Booking has master detail relationship where Truckk is master and Booking is detail.
Booking and payment has lookup relationship where Booking is parent and payment is child.
I want to update a field present isBooked(checkbox) in Truck based on the value present in a field remainingAmount in payment object. I have added trigger on Payment object like below
trigger TestTrigger on Payment__c (after insert) {
if(Trigger.isAfter && Trigger.isUpdate){
for (Payment__c pay:Trigger.New)
{
payId.add(pay.id);
paidAmount =Integer.valueOf(pay.Paid_Amount__c);
}
Map <id,Booking__c> bookingMap = new Map <id,Booking__c> ();
for (Booking__c obj: [Select Id, Booking_ID__c from Booking__c where Booking_ID__c in:payId])
{
bookingMap.put(obj.Booking_ID__c, obj);
}
for(Booking__c objBooking:matchingIdsMap){
Truck__c truckObj = [Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id];
//Integer paidAmount = Payment__c.Paid_Amount__c;
Integer totalAmount = Integer.valueOf(truckObj.Price__c);
Integer remainingAmount = totalAmount-paidAmount;
If(remainingAmount == 0){
truckObj.Booked__c = true
}
update truckObj;
}
}
}
Here first I am getting payment ID's and based on that I am fetching Booking objects which is lookup parent of payment. After this I am trying to fetch the truck object which is master of Booking. But I don't know how to query on this as where clause in query giving error
Truck__c truckObj = [Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id];
Please note there is no direct relationship between Truck and Payment
How can I fetch truck object
Thanks in Advance
Short answer: while referring to parent fields, you should use the relationship name, so Truck__r instead of Truck__c.
Anyway it is not the only problem with that code.
Long answer:
your trigger is in after insert, but you check for an after update event: if(Trigger.isAfter && Trigger.isUpdate). Probably you want to run this trigger in both after insert and after update.
You never declared payId nor paidAmount which you use in your first for-loop. Anyway paidAmount would just hold the last value, which probably you won't need.
[Select Id, Booking_ID__c from Booking__c where Booking_ID__c in:payId] this query should return an empty list because in payId you've stored the ids of Payment__c, that is a child of Booking__c, while in the first loop you should have stored the ids of parents Booking__c
[Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id] Here there is no reason to write where Truck__c.id It should be just WHERE Id = :objBooking.Truck__c.
Beware: putting a SOQL in a loop you will easily hit the Governor Limit about SOQL, which will raise a System.LimitException: Too many SOQL queries: 101.
The same goes by putting a DML in a loop.
I'm going to assume that the API Name of the lookup fields is the same of the parent object, so that a Booking__c field exists on Payment__c object and a Truck__c exists on Booking__c object.
If I got the logic right about how setting the flag on Truck object, this should be the trigger code.
trigger TestTrigger on Payment__c (after insert, after update) {
if(Trigger.isAfter && (Trigger.isInsert || Trigger.isUpdate)) {
Map<Id, List<Payment__c>> mapBookingIdPaymentList = new Map<Id, List<Payment__c>>();
for (Payment__c pay : Trigger.New) {
List<Payment__c> paymentList = mapBookingIdPaymentList.get(pay.Booking__c);
if (paymentList == null) {
paymentList = new List<Payment__c>();
mapBookingIdPaymentList.put(pay.Booking__c, paymentList);
}
paymentList.add(pay);
}
Map<Id, Decimal> mapTruckPrice = new Map<Id, Decimal>();
Map<Id, Integer> mapTruckRemainingAmount = new Map<Id, Integer>();
for (Booking__c booking: [SELECT Id, Truck__c, Truck__r.Price__c FROM Booking__c WHERE Id IN :mapBookingIdPaymentList.keySet()]) {
mapTruckPrice.put(booking.Truck__c, booking.Truck__r.Price__c);
Integer sumOfRemainingAmount = mapTruckRemainingAmount.containsKey(booking.Truck__c) ? mapTruckRemainingAmount.get(booking.Truck__c) : 0;
for (Payment__c pay : mapBookingIdPaymentList.get(booking.Id)) {
sumOfRemainingAmount += pay.Paid_Amount__c != null ? pay.Paid_Amount__c.intValue() : 0;
}
mapTruckRemainingAmount.put(booking.Truck__c, sumOfRemainingAmount);
}
List<Truck__c> trucksToUpdate = new List<Truck__c>();
for (Id truckId : mapTruckPrice.keySet()) {
// There is no need to query a record just to update it if you already have its Id.
Truck__c truck = new Truck__c(Id = truckId);
truck.Booked__c = mapTruckPrice.get(truckId) - mapTruckRemainingAmount.get(truckId) == 0;
trucksToUpdate.add(truck);
}
update trucksToUpdate; // dml outside the loop
}
}
By the way, you should move the logic in an handler class, following the best practices.

Problem when inserting two consecutive lines to the database

I have this function and it is working perfectly
public DemandeConge Creat(DemandeConge DemandeConge)
{
try
{
var _db = Context;
int numero = 0;
//??CompanyStatique
var session = _httpContextAccessor.HttpContext.User.Claims.ToList();
int currentCompanyId = int.Parse(session[2].Value);
numero = _db.DemandeConge.AsEnumerable()
.Where(t => t.companyID == currentCompanyId)
.Select(p => Convert.ToInt32(p.NumeroDemande))
.DefaultIfEmpty(0)
.Max();
numero++;
DemandeConge.NumeroDemande = numero.ToString();
//_db.Entry(DemandeConge).State = EntityState.Added;
_db.DemandeConge.Add(DemandeConge);
_db.SaveChanges();
return DemandeConge;
}
catch (Exception e)
{
return null;
}
}
But just when i try to insert another leave demand directly after inserting one (without waiting or refreshing the page )
An error appears saying that this new demand.id exists
I think that i need to add refresh after saving changes?
Any help and thanks
Code like this:
numero = _db.DemandeConge.AsEnumerable()
.Where(t => t.companyID == currentCompanyId)
.Select(p => Convert.ToInt32(p.NumeroDemande))
.DefaultIfEmpty(0)
.Max();
numero++;
Is a very poor pattern. You should leave the generation of your "numero" (ID) up to the database via an Identity column. Set this up in your DB (if DB First) and set up your mapping for this column as DatabaseGenerated.Identity.
However, your code raises lots of questions.. Why is it a String instead of an Int? This will be a bugbear for using an identity column.
The reason you will want to avoid code like this is because each request will want to query the database to get the "max" ID, as soon as you get two requests running relatively simultaneously you will get 2 requests that say the max ID is "100" before either can reserve and insert 101, so both try to insert 101. By using Identity columns the database will get 2x inserts and give them an ID first-come-first-serve. EF can manage associating FKs around these new IDs automatically for you when you set up navigation properties for the relations. (Rather than trying to set FKs manually which is the typical culprit for developers trying to fetch a new ID app-side)
If you're stuck using an existing schema where the PK is a combination of company ID and this Numero column as a string then about all you can do is implement a retry strategy to account for duplicates:
const int MAXRETRIES = 5;
var session = _httpContextAccessor.HttpContext.User.Claims.ToList();
int currentCompanyId = int.Parse(session[2].Value);
int insertAttemptCount = 0;
while(insertAttempt < MAXRETRIES)
{
try
{
numero = Context.DemandeConge
.Where(t => t.companyID == currentCompanyId)
.Select(p => Convert.ToInt32(p.NumeroDemande))
.DefaultIfEmpty(0)
.Max() + 1;
DemandeConge.NumeroDemande = numero.ToString();
Context.DemandeConge.Add(DemandeConge);
Context.SaveChanges();
break;
}
catch (UpdateException)
{
insertAttemptCount++;
if (insertAttemptCount >= MAXRETRIES)
throw; // Could not insert, throw and handle exception rather than return #null.
}
}
return DemandeConge;
Even this won't be fool proof and can result in failures under load, plus it is a lot of code to work around a poor DB design so my first recommendation would be to fix the schema because coding like this is prone to errors and brittle.

MVC Model is using values for old table entries, but new entries return NULL

I have an interesting little problem. My controller is assigning values to the properties in my model using two tables. In one of the tables, I have some entries that I made a while ago, and also some that I've just added recently. The old entries are being assigned values correctly, but the new entries assign NULL even though they're in the same table and were created in the same fashion.
Controller
[HttpPost]
[Authorize]
public ActionResult VerifyReservationInfo(RoomDataView model)
{
string loginName = User.Identity.Name;
UserManager UM = new UserManager();
UserProfileView UPV = UM.GetUserProfile(UM.GetUserID(loginName));
RoomAndReservationModel RoomResModel = new RoomAndReservationModel();
List<RoomProfileView> RoomsSelectedList = new List<RoomProfileView>();
GetSelectedRooms(model, RoomsSelectedList);
RoomResModel.RoomResRmProfile = RoomsSelectedList;
RoomResModel.GuestId = UPV.SYSUserID;
RoomResModel.FirstName = UPV.FirstName;
RoomResModel.LastName = UPV.LastName;
RoomResModel.PhoneNumber = UPV.PhoneNumber;
return View(RoomResModel);
}
GetUserProfile from the manager
public UserProfileView GetUserProfile(int userID)
{
UserProfileView UPV = new UserProfileView();
ResortDBEntities db = new ResortDBEntities();
{
var user = db.SYSUsers.Find(userID);
if (user != null)
{
UPV.SYSUserID = user.SYSUserID;
UPV.LoginName = user.LoginName;
UPV.Password = user.PasswordEncryptedText;
var SUP = db.SYSUserProfiles.Find(userID);
if (SUP != null)
{
UPV.FirstName = SUP.FirstName;
UPV.LastName = SUP.LastName;
UPV.PhoneNumber = SUP.PhoneNumber;
UPV.Gender = SUP.Gender;
}
var SUR = db.SYSUserRoles.Find(userID);
if (SUR != null)
{
UPV.LOOKUPRoleID = SUR.LOOKUPRoleID;
UPV.RoleName = SUR.LOOKUPRole.RoleName;
UPV.IsRoleActive = SUR.IsActive;
}
}
}
return UPV;
}
The issue I see is that this database has a somewhat poor design, and that particular record fell into the trap of that poor design. Consider that you have two ID's on that table:
SYSUserProfileID
SYSUserID
That's usually an indication of a bad design (though I'm not sure you can change it), if you can, you should merge anything that uses SYSUserID to use SYSUserProfileID.
This is bad because that last row has two different ID's. When you use db.Find(someId) Entity Framework will look for the Primary Key (SYSUserProfileID in this case) which is 19 for that row. But by the sounds of it, you also need to find it by the SYSUserID which is 28 for that row.
Personally, I'd ditch SYSUserID if at all possible. Otherwise, you need to correct the code so that it looks for the right ID column at the right times (this will be a massive PITA in the future), or correct that record so that the SYSUserID and SYSUserProfileID match. Either of these should fix this problem, but changing that record may break other things.

BreezeJS Reconstruct SaveResult on Server in .NET

Instead of using Breeze server side to save JObject, I'm using a dummy contextprovider to extract the EntityMaps and then performing custom validations on each entity and saving them myself. If the save succeeds, how do I reconstruct the SaveResult object to return back to the client so that BreezeJS client knows about my changes?
Currently I'm returning the following SaveResult:
// Using example here (https://github.com/Breeze/breeze.js.samples/issues/33)
// to extract EntityMaps from JObject.
// The return result is a Dictionary<Type, EntityInfo>.
var entityMaps = SaveBundleToSaveMap.Convert(saveBundle);
// ... Code to save entities to DB
// SaveResult to be returned to the client.
return new SaveResult()
{
Entities = entityMaps.SelectMany(innerEi => innerEi.Value.Select(ie => ie.Entity)).ToList<object>(),
Errors = null,
KeyMappings = new List<KeyMapping>()
};
How do I construct the KeyMapping list for single primary keys? How do I construct the KeyMapping for composite keys?
After some trial and error, I found that the SaveResult.KeyMapping list contain only the old and new keys generated from inserted entities. This makes sense because the client has the key of updates and do not need to worry about deleted entities.
To construct the SaveResult.KeyMapping list, first need to set the EntityInfo.AutoGeneratedKey.TempValue of each insert entity to the key sent from the client side, save the entities to get the new keys, and then create KeyMapping list and return back to the client.
1.Loop the entityMaps and set the TempValue for each EntityInfo to the key/id sent from the client side.
// Extract out from loop to make it more readable.
Action<EntityInfo> processEntityInfo = (ei) =>
{
if (ei.EntityState == EntityState.Added && (ei.AutoGeneratedKey != null && ei.AutoGeneratedKey.AutoGeneratedKeyType == AutoGeneratedKeyType.Identity))
{
var entity = ei.Entity;
var tempValue = ei.AutoGeneratedKey.Property.GetValue(entity);
ei.AutoGeneratedKey.TempValue = tempValue;
}
};
entityMaps.ToList().ForEach(map => map.Value.ForEach(ei => processEntityInfo(ei)));
2.Save the entities by calling the the Context.SaveChanges() on the EntityFramework Context. This will generate keys for all the inserted entities.
3.Loop through the saved entities in the entity maps and construct and return the KeyMapping list.
return entityMaps.SelectMany(entityMap =>
entityMap.Value
.Where(entityInfo => entityInfo.EntityState == EntityState.Added && (entityInfo.AutoGeneratedKey != null && entityInfo.AutoGeneratedKey.AutoGeneratedKeyType == AutoGeneratedKeyType.Identity))
.Select(entityInfo => new KeyMapping
{
EntityTypeName = entityInfo.Entity.GetType().FullName,
RealValue = entityInfo.AutoGeneratedKey.Property.GetValue(entityInfo.Entity),
TempValue = entityInfo.AutoGeneratedKey.TempValue
}));

MVC: The INSERT statement conflicted with the FOREIGN KEY constraint

I have two tables that are supposed to be related.
Tables and Coloums Specification
Primary key table
ProductCategory
ProductCategoryID
Foreign key table
SubProductCategory2
ProductCategoryID
In the controller I have the following methods when creating sub category...
public ActionResult Create()
{
ViewBag.ProductCategory = db.ProductCategories.OrderBy(p =>
p.ProductCategoryID).ToList();
ViewBag.SubProductCategory2 = db.SubProductCategory2.OrderBy(a =>
a.ProductCategoryID).ToList();
var PC2 = new SubProductCategory2();
return View(PC2);
}
public ActionResult Create(SubProductCategory2 Createsubcat2,
FormCollection values)
{
if (ModelState.IsValid)
{
db.AddToSubProductCategory2(Createsubcat2);
db.SaveChanges();
//error pointing here and the full error message I am getting is...
/*error: System.Data.SqlClient.SqlException:
* The INSERT statement conflicted with the FOREIGN KEY constraint
* "FK_SubProductCategory2_ProductCategory". The conflict occurred in
* database "MyHouseDB", table "dbo.ProductCategory", column
* 'ProductCategoryID'. The statement has been terminated.*/
return RedirectToAction("/");
}
ViewBag.ProductCategory = db.ProductCategories.OrderBy(p =>
p.ProductCategoryID).ToList();
ViewBag.SubProductCategory2 = db.SubProductCategory2.OrderBy(a =>
a.ProductCategoryID).ToList();
return View(Createsubcat2);
}
ViewBag.ProductCategory = db.ProductCategories.OrderBy(p =>
p.ProductCategoryID).ToList();
ViewBag.SubProductCategory2 = db.SubProductCategory2.OrderBy(a =>
a.ProductCategoryID).ToList();
return View(Createsubcat2);
in the views I have the following code...
<div class="editor-label">
#Html.LabelForModel()
</div>
<div class="editor-field">
#Html.DropDownList("CategoryName", new
SelectList((System.Collections.IEnumerable)ViewData["ProductCategory"],
"ProductCategoryID", "CategoryName"))
#Html.ValidationMessageFor(model => model.ProductCategory.CategoryName)
Could some tell me how to solve the The INSERT statement conflicted with the FOREIGN KEY constraint error message. Correct me if I'm wrong, have I created the relationship between two tables incorrectly or the problem else where? Thanks in advance.
This error happens when the following conditions are true 1) The value that you selected for "ProductCategoryID" is not present in the "ProductCategory" table OR 2) The product category table is empty.
Does you have values in the product category table?
What value are you choosing for ProductCategoryID?
I have found the problem. It was my sql database design not MVC coding side. I removed CategoryName column from SubCategory table. I could have felt the CategoryName as long as Allow Null was set to true. There was no point in doing that has PrimaryKey had been setup correctly for the two tables.

Resources