LINQ2SQL Entities - Updating only the fields that have changed - asp.net-mvc-3

I was hoping there was an easier way to do this in my MVC 3 project. In my database, I have a Customer table that is mapped in my application via LINQ2SQL. There is also a partial customer class where I perform updates, look-up etc - which where I have an update method like this:
public static void Update(Customer customer)
{
if (customer == null)
return;
using(var db = new DataBaseContext)
{
var newCustomer = db.Customers.Where(c => c.customer_id = customer.customer_id).SingleOrDefault();
if(newCustomer == null)
return;
newCustomer.first_nm = customer.first_nm;
// ...
// ... Lot's of fields to update
// ...
newCustomer.phone_num = customer.phone_nm;
db.SubmitChanges();
}
}
What I was hoping to find was a less-cumbersome method to update the fields in newCustomer with the corresponding fields in customer that are different.
Any suggestions? Thanks.

I think you can implement IEqualityComparer:
public class Customer
{
public string first_nm { get; set; }
public int phone_num { get; set; }
}
class CustomerComparer : IEqualityComparer<Customer>
{
public bool Equals(Customer x, Customer y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the customer' properties are equal.
return x.first_nm == y.first_nm && x.phone_num == y.phone_num ;
}
}
and do it as follows:
if (newCustomer != customer)
{
myDbContext.Customers.Attach(customer,true); // true means modified.
}
Or implement ICloneable and set newCustomer to customer.Clone(). then there's no need to attach customer since newCustomer is already attached.
in EF(4.1), I think You just have to attach the entity as modified:
myDbContext.Customers.AttachAsModified(customer, this.ChangeSet.GetOriginal(customer), myContext);
UPDATE:
Well, it seems like L2S needs original values of the entity. In reply to your comment, you have a couple choices: Using a timestamp column, returning a subset of entities, or having the original entity in your hand. In your scenario, you have the original entity already:
// This is your original entity
var newCustomer = db.Customers.Where(c => c.customer_id = customer.customer_id).SingleOrDefault();
So you will most probably can do:
if (customer != newCustomer)
{
myDbContext.Customers.Attach(customer, newCustomer);
}
Note: I'd rename newCustomer to originalCustomer if I were you since it's more related to the entity's state.
The problem with this approach is that you have an extra trip to database to get your original customer (newCustomer in your code). Take a look at here, here and definitely here to see how you can use TimeStamp columns to prevent the extra database trip.

Related

How to delete a single record from a list of records stored in a session

I have the following code
here is how I add a list of values to session
public ActionResult Add(Product product)
{
if (Session["AddToCart"] == null)
{
Session["AddToCart"] = new List<Product>();
}
var list = (List<Product>)Session["AddToCart"];
list.Add(product);
}
but how to remove a single record when a session contains multiple records. I am trying to pass an Id but it is not removing the record from the session. Here is how I perform the next step.
Public ActionResult Remove(Product product)
{
Product prod=db.Products.Single(x=>x.Id==product.Id);
var list=(List<Product>)Session["AddToCart"];
//Is this the correct approach
list.Remove(prod);
}
The above code doesn't works. Am I correct or is there anything missing plz correct the above code. Thanks.
Try this,
var list=(List<Product>)Session["AddToCart"];
list.RemoveAll(p => p.Id == product.Id);
Your choice of finding the product with the code db.Products.Single(x=>x.Id==product.Id); may not be the same object with the one in the session.
Edit:
Or you can implement IEquatable<Product> interface. In this case your code would work too.
public class Product : IEquatable<Product>
{
public int Id;
public bool Equals(Product prod)
{
return prod.Id == Id;
}
// Rest of the class
}

How to use InsertOrReplace in sqlite.net PCL?

I am using the PCL version of sqlite.net from here (https://github.com/oysteinkrog/SQLite.Net-PCL).
Here is my simple class.
public class LogEntry
{
[PrimaryKey, AutoIncrement]
public int Key { get; set;}
public DateTime Date { get; set; }
}
When a new instance of LogEntry is created, the Key is automatically set to 0. I set the Date to something and then call InsertOrReplace. The record does get saved in my database. The Key field gets the autoincrement value which happens to be 0 since it is the first record.
I then create a new instance of LogEntry (Key is automatically initialized to 0) and set the date to something else. I then call InsertOrReplace. Since there is an existing record with a Key of 0 that record gets updated.
What is the proper way to deal with this? I considered initializing the Key to -1, but that didn't seem to work either.
Does anyone have an example of this working?
If you change the Key to a nullable type (int?) it should work. then SQLite sees null coming in and generates new id when needed.
public class LogEntry
{
[PrimaryKey, AutoIncrement]
public int? Key { get; set;}
public DateTime Date { get; set; }
}
I experienced the same issue as you are describing. Try
var rowsAffected = Connection.Update(object);
if(rowsAffected == 0) {
// The item does not exists in the database so lets insert it
rowsAffected = Connection.Insert(object);
}
var success = rowsAffected > 0;
return success;
I just tried above and it works as expected
The way this works is the source of much confusion but whereas Insert treats zeroed primary keys as a special case when AutoIncrement is set, InsertOrReplace does not.
So with:
[PrimaryKey, AutoIncrement]
public int id { get; set; }
if you InsertOrReplace a series of zero id records into a new table, the first will be stored at id: 0 and each subsequent one will save over it. Whereas if you just Insert each one then because of the AutoIncrement the first will save at id: 1 and the next at id: 2 etc. as you might expect.
If you change the key type to a nullable int, then records with null ids will be treated as inserts by InsertOrReplace, and you don't actually need the AutoIncrement attribute at all in this case, they will still save in sequence starting at 1.
[PrimaryKey]
public int? id { get; set; }
If you can't use that for some reason you can do your own check for zero ids and for those call Insert instead, e.g.
Func<Foo, int> myInsertOrReplace = x =>
{
return x.id == 0 ? _db.Insert(x) : _db.InsertOrReplace(x);
};
but in this case you must use the AutoIncrement attribute, otherwise first zero insert will be saved at 0 and the second will throw a constraint exception when it attempts insert another such.
To get the result you want, you need to make the id property of your class nullable. see here
link
My solution for this is kind of similar to Joacar's, but instead of doing an update, I select the item, if it's null, I create a new item, otherwise update that items values, and then call InserOrReplace.
var existingKey = await this.GetItem(key);
Item item;
if (existingKey.Value != null)
{
profile = new Item
{
Id = existingKey.Id,
Key = existingKey.Key,
Value = newValue,
};
this.InsertOrReplaceAsync(item);
}
else
{
item = new Item
{
Key = key,
Value = value,
};
this.InsertAsync(item);
}
It might not be optimal, but it worked for me.
No need for InsertOrReplace.
Just await InsertAsync.
Guaranteed to work...
if (object.ID != 0)
{
// Update an existing object.
var T = DatabaseAsyncConnection.UpdateAsync(object);
T.Wait();
return T;
}
else
{
// Save a new object.
var T = DatabaseAsyncConnection.InsertAsync(object);
T.Wait();
return T;
}

Linq Grouping looses the child entities

I have the following query:
var _customers = (from c in _db.UserProfiles.Include(x=>x.ParentCompanies).Include(x=>x.cProfile).Include(x=>x.cProfile.PhoneNumbers).Include(x=>x.cProfile.Addresses)
where (c.ParentCompanies.Any(pc => pc.CompanyUsers.Any(cu => cu.UserName == userName)) && c.cProfile != null)
group c by c.FirstName.Substring(0, 1).ToUpper() into customerGroup
select new ContactsViewModel
{
FirstLetter = customerGroup.Key,
Customers = customerGroup
}).OrderBy(letter => letter.FirstLetter);
if I take out the group, it works well and includes all the children (parentCompanies, cProfile, ...) as soon as I put the group back in it looses all of the children. How do I solve this issue?
update
I guess I should also include the view model that I'm usign to put the result in.
public class ContactsViewModel
{
public string FirstLetter { get; set; }
public IEnumerable<UserProfile> Customers { get; set; }
}
Include only applies to items in the query results (i.e. the final projection) and cannot contain operations that change the type of the result between Include and the outermost operation (e.g. GroupBy())
http://wildermuth.com/2008/12/28/Caution_when_Eager_Loading_in_the_Entity_Framework
If you want to eager load, do the grouping client-side (i.e. enumerate the query then call the GroupBy method on the results)

EF, POCO, DbContext and Validating Deletions

I'm fairly new to the world of MVC and EF, and I've come quite a ways on my own, but one thing I haven't been able to find online is how people validate for "do not delete" conditions.
I'm using EF4.1 database-first POCO classes generated with the DbContext T4 template. In my partial class files I've already decorated all of my classes with the "IValidatableObject" interface that gets called on changes for my business rules that go beyond the standard MetaData attribute type of validations.
What I need now is a validation that works via the same mechanism (and is, therefore, transparent to the UI and the controller) for checking if deletions are OK. My thought was to create an interface like so:
public interface IDeletionValidation
{
DbEntityValidationResult ValidateDeletion(DbEntityValidationResult validationResults);
}
...and then do this in an override to ValidateEntity in the DbContext...
public partial class MyEntityContext
{
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
DbEntityValidationResult val = base.ValidateEntity(entityEntry, items);
if (entityEntry.State == EntityState.Deleted)
{
IDeletionValidation delValidationEntity = entityEntry.Entity as IDeletionValidation;
if (delValidationEntity != null)
val = delValidationEntity.ValidateDeletion(val);
}
return val;
}
...and then I could implement the IDeletionValidation interface on those classes that need to have a validation done before they can be safely deleted.
An example (not working, see caveat in comments) of the ValidateDeletion code would be...
public partial class SalesOrder : IDeletionValidation, IValidatableObject
{
public DbEntityValidationResult ValidateDeletion(DbEntityValidationResult validations)
{
// A paid SalesOrder cannot be deleted, only voided
// NOTE: this code won't work, it's coming from my head and note from the actual source, I forget
// what class I'd need to add to the DbEntityValidationResult collection for this type of validation!
if (PaidAmount != 0)
validations.Add(new ValidationResult("A paid SalesOrder cannot be deleted, only voided"));
return validations;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
// Verify that the exempt reason is filled in if the sales tax flag is blank
if (!IsTaxable && string.IsNullOrEmpty(TaxExemptReason))
validations.Add(new ValidationResult("The Tax Exempt Reason cannot be blank for non-taxable orders"));
return validations;
}
....
}
Am I on the right track? Is there a better way?
Thanks,
CList
EDIT --- Summary of the one-interface method proposed by Pawel (below)
I think the one-interface way presented below and my way above is a little bit of a chocolate vs. vanilla argument in terms of how you want to do it. Performance should be about the same for large numbers of updates / deletes, and you may want to have your delete validation be a separate interface that doesn't apply to all of your validated classes, but if you want all of your validations in one place here it is...
Mod your DBContext
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return entityEntry.Sate == EntityState.Deleted ||
base.ShouldValidateEntity(entityEntry);
}
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var myItems = new Dictionary<object, object>();
myItems.Add("IsDelete", (entityEntry.State == EntityState.Deleted));
// You could also pass the whole context to the validation routines if you need to, which might be helpful if the
// validations need to do additional lookups on other DbSets
// myItems.Add("Context", this);
return base.ValidateEntity(entityEntry, myItems);
}
Put the deletion-validation in your entity's Validate
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
bool isDelete = validationContext.Items.ContainsKey("IsDelete")
? (bool)validationContext.Items["IsDelete"]
: false;
if (isDelete)
{
if (PaidAmount != 0)
validations.Add(new ValidationResult("You cannot delete a paid Sales Order Line", new string[] { "PaidAmount" }));
return validations;
}
// Update / Add validations!!
// Verify that the exempt reason is filled in if the sales tax flag is blank
if (!IsTaxable && string.IsNullOrEmpty(TaxExemptReason))
validations.Add(new ValidationResult("The Tax Exempt Reason cannot be blank for non-taxable orders"));
return validations;
}
...and in the interest of brevity and only putting all of the check-if-delete code in one place, you could even create an extension method on the ValidationContext class (if you're into that sort of thing) like so...
public static class MyExtensions
{
public static bool IsDelete(this System.ComponentModel.DataAnnotations.ValidationContext validationContext)
{
return validationContext.Items.ContainsKey("IsDelete")
? (bool)validationContext.Items["IsDelete"]
: false;
}
}
...which gives us this for our validation code...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
if (validationContext.IsDelete())
{
....
I am not really sure why you need a separate interface just for deleted entities. You could pass the entity state (or the EntityEntry object, or the context) to your IValidatableObject.Validate() method by using the items dictionary you pass to the base.ValidateEntity() method. Take a look at the "Custom Validation Sample: Uniqness" section in this blog post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx. This way you could do everything using just one interface - IValidatableObject.
In addition to that - by default EF validates only Added and Modified entities. If you want to validate entities that are in the Deleted state you need to override DbContext.ShouldValidateEntity() method with something like this:
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return entityEntry.Sate == EntityState.Deleted ||
base.ShouldValidateEntity(entityEntry);
}

LINQ-To-Sharepoint Multiple content types for a single list

I'm using SPMetal in order to generate entity classes for my sharepoint site and I'm not exactly sure what the best practice is to use when there are multiple content types for a single list. For instance I have a task list that contains 2 content types and I'm defining them via the config file for SPMetal. Here is my definition...
<List Member="Tasks" Name="Tasks">
<ContentType Class="LegalReview" Name="LegalReviewContent"/>
<ContentType Class="Approval" Name="ApprovalContent"/>
</List>
This seems to work pretty well in that the generated objects do inherit from WorkflowTask but the generated type for the data context is a List of WorkflowTask. So when I do a query I get back a WorkflowTask object instead of a LegalReview or Approval object. How do I make it return an object of the correct type?
[Microsoft.SharePoint.Linq.ListAttribute(Name="Tasks")]
public Microsoft.SharePoint.Linq.EntityList<WorkflowTask> Tasks {
get {
return this.GetList<WorkflowTask>("Tasks");
}
}
UPDATE
Thanks for getting back to me. I'm not sure how I recreate the type based on the SPListItem and would appreciate any feedback.
ContractManagementDataContext context = new ContractManagementDataContext(_url);
WorkflowTask task = context.Tasks.FirstOrDefault(t => t.Id ==5);
Approval a = new Approval(task.item);
public partial class Approval{
public Approval(SPListItem item){
//Set all properties here for workflowtask and approval type?
//Wouldn't there be issues since it isn't attached to the datacontext?
}
public String SomeProperty{
get{ //get from list item};
set{ //set to list item};
}
Linq2SharePoint will always return an object of the first common base ContentType for all the ContentTypes in the list. This is not only because a base type of some description must be used to combine the different ContentTypes in code but also it will then only map the fields that should definitely exist on all ContentTypes in the list. It is however possible to get access to the underlying SPListItem returned by L2SP and thus from that determine the ContentType and down cast the item.
As part of a custom repository layer that is generated from T4 templates we have a partial addition to the Item class generated by SPMetal which implements ICustomMapping to get the data not usually available on the L2SP entities. A simplified version is below which just gets the ContentType and ModifiedDate to show the methodology; though the full class we use also maps Modified By, Created Date/By, Attachments, Version, Path etc, the principle is the same for all.
public partial class Item : ICustomMapping
{
private SPListItem _SPListItem;
public SPListItem SPListItem
{
get { return _SPListItem; }
set { _SPListItem = value; }
}
public string ContentTypeId { get; internal set; }
public DateTime Modified { get; internal set; }
public virtual void MapFrom(object listItem)
{
SPListItem item = (SPListItem)listItem;
this.SPListItem = item;
this.ContentTypeId = item.ContentTypeId.ToString();
this.Modified = (DateTime)item["Modified"];
}
public virtual void MapTo(object listItem)
{
SPListItem item = (SPListItem)listItem;
item["Modified"] = this.Modified == DateTime.MinValue ? this.Modified = DateTime.Now : this.Modified;
}
public virtual void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
{
SPListItem originalItem = (SPListItem)originalListItem;
SPListItem databaseItem = (SPListItem)databaseObject;
DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];
string originalContentTypeIdValue = originalItem.ContentTypeId.ToString();
string dbContentTypeIdValue = databaseItem.ContentTypeId.ToString();
switch(mode)
{
case RefreshMode.OverwriteCurrentValues:
this.Modified = dbModifiedValue;
this.ContentTypeId = dbContentTypeIdValue;
break;
case RefreshMode.KeepCurrentValues:
databaseItem["Modified"] = this.Modified;
break;
case RefreshMode.KeepChanges:
if (this.Modified != originalModifiedValue)
{
databaseItem["Modified"] = this.Modified;
}
else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
{
this.Modified = dbModifiedValue;
}
if (this.ContentTypeId != originalContentTypeIdValue)
{
throw new InvalidOperationException("You cannot change the ContentTypeId directly");
}
else if (this.ContentTypeId == originalContentTypeIdValue && this.ContentTypeId != dbContentTypeIdValue)
{
this.ContentTypeId = dbContentTypeIdValue;
}
break;
}
}
}
Once you have the ContentType and the underlying SPListItem available on your L2SP entity it is simply a matter of writing a method which returns an instance of the derived ContentType entity from a combination of the values of the base type and the extra data for the missing fields from the SPListItem.
UPDATE: I don't actually have an example converter class as we don't use the above mapping extension to Item in this way. However I could imagine something like this would work:
public static class EntityConverter
{
public static Approval ToApproval(WorkflowTask wft)
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = wft.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = wft.SPListItem["field-name"];
return a;
}
}
Or you could put a method on a partial instance of WorkflowTask to return an Approval object.
public partial class WorkflowTask
{
public Approval ToApproval()
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = this.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = this.SPListItem["field-name"];
return a;
}
public LegalReview ToLegalReview()
{
// Create and return LegalReview as for Approval
}
}
In either situation you would need to determine the method to call to get the derived type from the ContentTypeId property of the WorkflowTask. This is the sort of code I would normally want to generate in one form or another as it will be pretty repetitive but that is a bit off-topic.

Resources