Updating object with related objects with EntityFramework 6 - sql

I'm trying to integrate EF6 database first into a project, which uses legacy sql. I have a problem with the following tables:
Product(ID, Name) -- this translates to a Product entity
User(ID, Name) -- this translates to a User entity
UserXProduct(UserID, ProductID) -- this translates into navigational properties
Note that I can't access UserXProduct table through entity framework, because it somehow didn't generate a new table, just the navigational properties. I use the following code to update the table:
var entity = ctx.User.SingleOrDefault(rec => rec.ID == updRec.ID);
if(entity != null){
entity.Product = ctx.Product.Where(rec => updRec.ProductIDs.Contains(rec.ID)).ToList();
ctx.SaveChanges();
}
It works the first time around, when the User record doesn't have any Products associated with it, whenever I add another product to the list, it fails.
SQL Profiler shows, that Entity Framework tries to INSERT UserID and ProductID into UserXProduct table, not just the new one, but all of them. It should either delete previous connections or insert only new ones. How can I achieve such behaviour?

Related

EF Core Bulk Update (Update Range) Increment an existing column value

I have a List of an Entity for e.g List of Customer entity.
I am using a Bulk update as context.Customer.UpdateRange(Customer);
My requirement is I need to increment an Existing Column value with a new value. for e.g Inside Customer table Qty column needs to be updated as
Qty = Qty + current_Value.
I can do this by looping through the Customer List and update the column one by one customer object. But I wish to do this while bulk update (UpdateRange)
The technology I am using - ASP.NET Core, MVC Core, EF Core
The DbContext.UpdateRange method only marks your entities' states as Modified. This way, each entity will be updated, no matter if the data has changed or not (https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.updaterange?view=efcore-2.1).
This method is not supposed to be used to iterate the list of entities and to change its values.
Knowing that you have several solutions :
Iterating your entities and set the Qty in your business layer (as you mentionned)
Using a stored procedure to increment the value (https://www.entityframeworktutorial.net/efcore/working-with-stored-procedure-in-ef-core.aspx)

Duplicate a record and its references in web2py

In my web2py application I have a requirement to duplicate a record and all its references.
For example
one user has a product (sponserid is the user). and this product has so many features stored in other tables (reference to product id).
And my requirement is if an another user is copying this product, the a new record will generate in the product table with new productid and new sponserid. And all the reference table records will also duplicate with the new product id. Effectively a duplicate entry is creating in all the tables only change is product id and sponserid.
The product table fields will change. So I have to write a dynamic query.
If I can write a code like below
product = db(db.tbl_product.id==productid).select(db.tbl_product.ALL).first()
newproduct = db.tbl_product.insert(sponserid=newsponserid)
for field,value in product.iteritems():
if field!='sponserid':
db(db.tbl_product.id==newproduct).update(field=value)
But I cannot refer a field name like this in the update function.
Also I would like to know if there is any other better logic to achieve this requirement.
I would greatly appreciate any suggestions.
For the specific problem of using the .update() method when the field name is stored in a variable, you can do:
db(db.tbl_product.id==newproduct).update(**{field: value})
But an easier approach altogether would be something like this:
product = db(db.tbl_product.id==productid).select(db.tbl_product.ALL).first()
product.update(sponserid=newsponserid)
db.tbl_product.insert(**db.tbl_product._filter_fields(product))
The .update() method applied to the Row object updates only the Row object, not the original record in the db. The ._filter_fields() method of the table takes a record (Row, Storage, or plain dict) and returns a dict including only the fields that belong to the table (it also filters out the id field, which the db will auto-generate).

CRM 2011 sdk - quickest/most efficient way to get contact info + associated list IDs

Our CRM 2011 database contains approx. 20000 contacts, and I need to loop through all of them using the SDK. Currently I'm finding the following linq query takes a very long time to execute:
Dim contactList = From c In orgService.ContactSet
Select New With {
Key .ContactId = c.ContactId,
Key .EMailAddress1 = c.EMailAddress1,
Key .ListIds = From l In c.listcontact_association Select l.ListId
}
As you can see, I just need a couple of fields from each Contact, and a list of associated Marketing List IDs. Perhaps it's taking a long time because it's doing an additional query (to get the list IDs) within each contact result?
I'm fairly new to Linq, so not sure how the above translates to actual FetchXML communication. Is there a more efficient way of getting that info which would result in shorter query run time?
More Info: I'm writing code to sync a CRM database with a CreateSend database. So I do need to go through all contact records, not only adding to the CS database, but also reflecting changes in list membership and updating activity or other info for each contact where needed. The sync process will eventually run nightly on the CRM server itself, so it's expected to take time to run, but of course I want to make it as efficient as possible.
In the absence of more information, it looks like your query is actually two - one query to get the list of contacts, and then another to get the list of list ids. This means SQL Server is returning the full list of 20,000 contacts, then for each contact, your code asks SQL Server for the list of associated list ids.
This means you are making 20,000 + 1 separate calls to SQL Server (though this is actually an abstracted total - the CRM SQL translator actually makes more than that, but it doesn't add significant overhead).
So what you really want to do is make just 1 query that gets you all your data, and then begin to work with it. I'm not proficient enough in VB.NET to make the translation, but the below C# code should get you there most of the way.
// Gets all contacts and list ids in a flat result set
var contacts = from c in orgService.ContactSet
join lms in orgService.ListMemberSet on c.ContactId equals lms.EntityId.Id into leftJoinedContact
from ljc in leftJoinedContact.DefaultIfEmpty()
where ljc.EntityType == Xrm.Contact.EntityLogicalName
select new
{
c.ContactId,
c.EMailAddress1,
ljc.ListId
};
// Calls .ToList() to put the result set in memory
var contactList = contacts.ToList();
// Manipulates the result set in memory, grouping by contact info
var newContactList = contactList.GroupBy(x => new {x.ContactId, x.EMailAddress1})
.Select(x => new {x.Key.ContactId, x.Key.EMailAddress1, Ids = x.Select(y => y.ListId).ToList()})
.ToList();
// var contactsWithListIds = newContactList.Where(x => x.Ids.Where(y => y != null).Any()).ToList();
// var contactsWithoutListIds = newContactList.Where(x => x.Ids.Where(y => y != null).Any()).ToList();
foreach (var contact in newContactList)
{
throw new NotImplementedException();
}
Yes!
But we need more information. The fastest way is to use straight SQL (i.e. filtered views).
The second fastest way is to use straight FetchXML (although some may debate that linq is on par with this in terms of performance).
If you can filter your query to reduce the 20,000 records to only the records you need, you'll save the most time however. So the first question I have is why are you iterating through the 20,000 records? Do you need to process them all or do you need to check them all for certain criteria and then do something based on if they match the criteria?

How do I get NHibernate to save an entity if I assign it an ID, but generate one otherwise?

According to the REST philosophy, a PUT request should update the resource at a URL if it exists, and create it if it doesn't exist. So in other words, if I use the following URL:
PUT http://server/item/5
If an Item exists with an ID of 5, it will be updated. If an Item doesn't exist with an ID of 5, a new Item will be created with an ID of 5.
However, I'm using NHibernate for persistence, and I mapped my IDs as Identity. This means that no matter what value I assign the ID, NHibernate will replace it with its own when I save a new Item.
How do I get NHibernate to save an Item with the ID that I assign it, without changing the ID mapping to Assigned?
If you use Identity, the DB won't allow you to enter a value.
That said, if your DB has some special syntax to allow inserting with explicit values in Identity fields, you can implement your own generator, which I guarantee will be error prone, hard to debug, and not very useful. But it's possible.
Study everything in https://nhibernate.svn.sourceforge.net/svnroot/nhibernate/trunk/nhibernate/src/NHibernate/Id and start creating your Frankenstein :-)

NHibernate update reference

Entities
We have an entity called Product which is loaded using NHibernate.
Product has a category which NHibernate happily populates for me.
Database
In the database, Product has a foreign key for category.
Scenario
User edits this Product (via a web interface) and chooses a different category (say instead of "Fish" we select "Veg").
This is probably a dropdown list, with each category shown. When they choose a different category we get an int key.
Problem
Obviously we now want to save the changes to Product but in effect the only change is to save a new int (say 2, instead of 1).
So we retrieve the existing Product, and now comes the problem.
We don't have a "CategoryID" field on Product, we only have a Category property.
But we don't really want to retrieve the category (by id) just to assign it to the Product.
So I guess what I want to know is should we...
a) Add a CategoryID property to Product
b) Create a new category, assign it the relevant id and attach that to Product (but surely that will cause errors, or overwrite the existing category)
c) Retrieve (lookup) the category from the system (by id) and attach that to the Product
d) Do something else entirely!
It looks like you might be able to using the Session.Load(id) functionality.
Session.Load is a special method that returns a proxy with the ID until you request another property at which point it loads. It throws an error if there is no item matching the ID. Try something like:
product.Category = Session.Load<Category>(2); //2 being the new category ID
Session.SaveOrUpdate(product);
I just did a little testing and it did not seem to pull back the entire Category.
Updated: Session.Load is the correct answer
product.Category = session.Load<Category>(2);
session.Save(product);
Use NH's EnumStringType<T> to map your Category as an enum to the respective database value (which can be a string or a number). You'll find quite a few usage examples, if you google for it.
HTH!