ASP.NET MVC + EF 4.3.1 + DbContext - some null associations - asp.net-mvc

I am using ASP.NET MVC 3 with EF 4.3.1, and the "EF 4.x DbContext Generator for C#" extension. I'm using the repository pattern.
The problem I'm having is that EF is not populating associations in some cases where I would expect it to. Is there something I need to do to trigger the population of all associations?
For example, I have an Assignment model that contains the properties "CompanyPartyId" and "EmployeePartyId". Both these properties have FKs to Parties.PartyId. That causes two associations to exist, Party (pointing to the Party specified by CompanyPartyId) and Party1 (pointing to the Party specified by EmployeePartyId).
For some reason, after saving a new Assignment, and retrieving it, the Party association is null. Both CompanyPartyId and EmployeePartyId properties are set to values which exist in Parties.PartyId. I have a similar issue with several other properties/associations; the properties are not null and contain valid values, but the association is null.
If it's relevant, Assignment uses a composite key; CompanyPartyId, EmployeePartyId, AssignmentTypeCode, and AssignmentStartDate.
The code in my controller looks like this (omitting validation and irrelevant logic):
[HttpPost]
[Authorize(Roles = "assignment_edit")]
public ActionResult Create(AssignmentViewModel assignment)
{
var newAssignment = assignment.NewAssignment;
partiesRepository.SaveAssignment(newAssignment);
var savedAssignment =
partiesRepository.Assignments.First(x => x.CompanyPartyId == newAssignment.CompanyPartyId &&
x.EmployeePartyId == newAssignment.EmployeePartyId &&
x.AssignmentTypeCode == newAssignment.AssignmentTypeCode &&
x.AssignmentStartDate == newAssignment.AssignmentStartDate);
var companyPartyId = savedAssignment.CompanyPartyId; // 1
var companyParty = savedAssignment.Party; // null
var employeePartyId = savedAssignment.EmployeePartyId; // 2
var employeeParty = savedAssignment.Party1; // not null
return View("Index");
}
The repository code is simple:
public IQueryable<Assignment> Assignments
{
get { return _context.Assignments; }
}
public void SaveAssignment(Assignment assignment)
{
var data = (from a in _context.Assignments
where a.CompanyPartyId == assignment.CompanyPartyId
&& a.EmployeePartyId == assignment.EmployeePartyId
&& a.AssignmentTypeCode == assignment.AssignmentTypeCode
&& a.AssignmentStartDate == assignment.AssignmentStartDate
select a);
if (!data.Any())
{
_context.Assignments.Add(assignment);
}
_context.SaveChanges();
}

Related

Best Way to Update only modified fields with Entity Framework

Currently I am doing like this:
For Example:
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
db.Entry(oldobj).CurrentValues.SetValues(model);
}
It works, but for example,
I have 50 columns in my table but I displayed only 25 fields in my form (I need to partially update my table, with remaining 25 column retain same old value)
I know it can be achieve by "mapping columns one by one" or by creating "hidden fields for those remaining 25 columns".
Just wondering is there any elegant way to do this with less effort and optimal performance?
This is a very good question. By default I have found that as long as change tracking is enabled (it is by default unless you turn it off), Entity Framework will do a good job of applying to the database only what you ask it to change.
So if you only change 1 field against the object and then call SaveChanges(), EF will only update that 1 field when you call SaveChanges().
The problem here is that when you map a view model into an entity object, all of the values get overwritten. Here is my way of handling this:
In this example, you have a single entity called Person:
Person
======
Id - int
FirstName - varchar
Surname - varchar
Dob - smalldatetime
Now let's say we want to create a view model which will only update Dob, and leave all other fields exactly how they are, here is how I do that.
First, create a view model:
public class PersonDobVm
{
public int Id { get; set; }
public DateTime Dob { get; set; }
public void MapToModel(Person p)
{
p.Dob = Dob;
}
}
Now write the code roughly as follows (you'll have to alter it to match your context name etc):
DataContext db = new DataContext();
Person p = db.People.FirstOrDefault();
// you would have this posted in, but we are creating it here just for illustration
var vm = new PersonDobVm
{
Id = p.Id, // the Id you want to update
Dob = new DateTime(2015, 1, 1) // the new DOB for that row
};
vm.MapToModel(p);
db.SaveChanges();
The MapToModel method could be even more complicated and do all kinds of additional checks before assigning the view model fields to the entity object.
Anyway, the result when SaveChanges is called is the following SQL:
exec sp_executesql N'UPDATE [dbo].[Person]
SET [Dob] = #0
WHERE ([Id] = #1)
',N'#0 datetime2(7),#1 int',#0='2015-01-01 00:00:00',#1=1
So you can clearly see, Entity Framework has not attempted to update any other fields - just the Dob field.
I know in your example you want to avoid coding each assignment by hand, but I think this is the best way. You tuck it all away in your VM so it does not litter your main code, and this way you can cater for specific needs (i.e. composite types in there, data validation, etc). The other option is to use an AutoMapper, but I do not think they are safe. If you use an AutoMapper and spelt "Dob" as "Doob" in your VM, it would not map "Doob" to "Dob", nor would it tell you about it! It would fail silently, the user would think everything was ok, but the change would not be saved.
Whereas if you spelt "Dob" as "Doob" in your VM, the compiler will alert you that the MapToModel() is referencing "Dob" but you only have a property in your VM called "Doob".
I hope this helps you.
I swear by EntityFramework.Extended. Nuget Link
It lets you write:
db.Person
.Where(x => x.ID == model.ID)
.Update(p => new Person()
{
Name = newName,
EditCount = p.EditCount+1
});
Which is very clearly translated into SQL.
Please try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
// Newly Inserted Code
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}
I have solved my Issue by using FormCollection to list out used element in form, and only change those columns in database.
I have provided my code sample below; Great if it can help someone else
// Here
// collection = FormCollection from Post
// model = View Model for Person
var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault();
if (result != null)
{
List<string> formcollist = new List<string>();
foreach (var key in collection.ToArray<string>())
{
// Here apply your filter code to remove system properties if any
formcollist.Add(key);
}
foreach (var prop in result.GetType().GetProperties())
{
if( formcollist.Contains(prop.Name))
{
prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null));
}
}
db.SaveChanges();
}
I still didn't find a nice solution for my problem, so I created a work around. When loading the Entity, I directly make a copy of it and name it entityInit. When saving the Entity, I compare the both to see, what really was changed. All the unchanged Properties, I set to unchanged and fill them with the Database-Values. This was necessary for my Entities without Tracking:
// load entity without tracking
var entityWithoutTracking = Context.Person.AsNoTracking().FirstOrDefault(x => x.ID == _entity.ID);
var entityInit = CopyEntity(entityWithoutTracking);
// do business logic and change entity
entityWithoutTracking.surname = newValue;
// for saving, find entity in context
var entity = Context.Person.FirstOrDefault(x => x.ID == _entity.ID);
var entry = Context.Entry(entity);
entry.CurrentValues.SetValues(entityWithoutTracking);
entry.State = EntityState.Modified;
// get List of all changed properties (in my case these are all existing properties, including those which shouldn't have changed)
var changedPropertiesList = entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified).ToList();
foreach (var checkProperty in changedPropertiesList)
{
try
{
var p1 = entityWithoutTracking.GetType().GetProperty(checkProperty).GetValue(entityWithoutTracking);
var p2 = entityInit.GetType().GetProperty(checkProperty).GetValue(entityInit);
if ((p1 == null && p2 == null) || p1.Equals(p2))
{
entry.Property(checkProperty).CurrentValue = entry.Property(checkProperty).OriginalValue; // restore DB-Value
entry.Property(checkProperty).IsModified = false; // throws Exception for Primary Keys
}
} catch(Exception) { }
}
Context.SaveChanges(); // only surname will be updated
This is way I did it, assuming the new object has more columns to update that the one we want to keep.
if (theClass.ClassId == 0)
{
theClass.CreatedOn = DateTime.Now;
context.theClasses.Add(theClass);
}
else {
var currentClass = context.theClasses.Where(c => c.ClassId == theClass.ClassId)
.Select(c => new TheClasses {
CreatedOn = c.CreatedOn
// Add here others fields you want to keep as the original record
}).FirstOrDefault();
theClass.CreatedOn = currentClass.CreatedOn;
// The new class will replace the current, all fields
context.theClasses.Add(theClass);
context.Entry(theClass).State = EntityState.Modified;
}
context.SaveChanges();
In EF you can do like this
var result = db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
if(result != null){
result.Name = newName;
result.DOB = newDOB;
db.Person.Update(result);
}
Or you can use
using (var db= new MyDbContext())
{
var result= db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
result.Name= newName;
result.DOB = newDOB;
db.Update(result);
db.SaveChanges();
}
For more detail please EntityFramework Core - Update Only One Field
No Worry guys
Just write raw sql query
db.Database.ExecuteSqlCommand("Update Person set Name='"+_entity.Name+"' where Id = " + _entity.ID + "");

How to get original values of an entity in Entity Framework?

In EF 4.0, if I understand it right, there are two type of values in Entity : current values and original values.
We can set original values by calling ApplyOriginalValues(TEntity) method but how to get original values ?
#Eranga answer is outdated for EF 5. For some reason, EF 5 doesn't work fine when getting original values using an statement like this:
var originalValues = context.Entry(myEntity).OriginalValues;
My working solution uses AsNoTracking() method from DbSet, like the example below:
var originalEntity = context.MyEntities.AsNoTracking().FirstOrDefault(me => me.MyEntityID == myEntity.MyEntityID);
You can access them through ObjectStateEntry
var originalValues = context
.ObjectStateManager.GetObjectStateEntry(myEntity).OriginalValues;
This could be refined further to the following:
var originalEntity = context.MyEntities.AsNoTracking()
.FirstOrDefault(me => me.MyEntityID == myEntity.MyEntityID);
The Where in the above, good, response is not needed.
var originalEntity = (EntityType)context.Entry(editEntity).OriginalValues.ToObject();
Sorry for my english.
With this way you can get the original entity values in the form of the object entity with no changes on the edit values.
Example:
If you like edit a Person the line in top look like this
var originalPerson = (Person)context.Entry(editPerson).OriginalValues.ToObject();
I ran into a similar problem and AsNoTracking was not an option for my situation so i came up with something that works well enough for me: first "clone" the entity then do changes.
public T Clone<T>(T entity)
where T : class, new() {
var clone = new T();
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy)
.Where(a => a.CanRead &&
a.CanWrite &&
a.GetMethod.IsFinal);
foreach (var property in properties) {
property.SetValue(clone, property.GetValue(entity));
}
return clone;
}
and then compare the clone to the changed.
public string GenerateChangeText<T>(T original, T current)
where T : class, new() {
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy)
.Where(a => a.CanRead &&
a.CanWrite &&
a.GetMethod.IsFinal);
var changes = string.Empty;
foreach (var property in properties) {
var originalValue = property.GetValue(original);
var currentValue = property.GetValue(current);
if (originalValue == null && currentValue == null) continue;
if ((originalValue != null && !originalValue.Equals(currentValue)) ||
(currentValue != null && !currentValue.Equals(originalValue))) {
changes += $" changed {property} from {original ?? "NULL"} to {current ?? "NULL"}.";
}
}
return changes;
}
There are a few versions of Entity Framework in use.
I myself prefer Code First and with that API it's easy as
_context.Entry(Entity).Reload();
Docs
https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbentityentry.reload(v=vs.103).aspx
The older API's have a Refresh method on the ObjectContext which can help in certain use cases
ObjectContext.Refresh(RefreshMode.StoreWins, Entity);
Docs https://msdn.microsoft.com/en-us/library/bb896255(v=vs.110).aspx
This answer refers to Entity Framework 6. In EF 6 there is an Original value and Current value https://msdn.microsoft.com/en-us/library/gg679512(v=vs.113).aspx After looking and not finding a good answer I came up with the following test function and thought I would post it for others needing to do the same.
private void test()
{
// table has a field Description of type varchar(200)
WDMDBEntities context = new WDMDBEntities();
var query = context.Brands;
List<Brand> records = query.ToList();
if (records.Count > 0)
{
Brand currentRecord = records[0];
currentRecord.Description = "some new text";
string originalValue = null;
switch (context.Entry(currentRecord).State)
{
case System.Data.Entity.EntityState.Added:
originalValue = null;
break;
case System.Data.Entity.EntityState.Deleted:
case System.Data.Entity.EntityState.Detached:
case System.Data.Entity.EntityState.Modified:
case System.Data.Entity.EntityState.Unchanged:
originalValue = context.Entry(currentRecord).Property(u => u.Description).OriginalValue;
break;
}
}
context.Dispose();
}

Update Data using Linq2Sql

I have this in my mvc ActionResult
[HttpPost]
public ActionResult _ChangeDetails( [Bind(Prefix="ContactDetails")] userDetail UserDetail )
{
MemberChangeDetailsFormViewModel fvm = new MemberChangeDetailsFormViewModel();
if (ModelState.IsValid)
{
//save
UserDetailRepository repository = new UserDetailRepository();
repository.Save(UserDetail);
return RedirectToAction("Index", "Member");
}
fvm.ContactDetails = UserDetail;
return View(fvm);
}
Then in my repository I have;
if (userDetail.id != Guid.Empty)
{
userDetail orig = dc.userDetails.Where(x => x.id == userDetail.id).Single();
dc.userDetails.Attach(userDetail, orig);
dc.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, userDetail);
dc.SubmitChanges();
}
However the "attach" is generating an error; Cannot add an entity with a key that is already in use.
I understand that it's caused because L2S already has the object attached. I have tried doing this with and without the orig object but get the same message.
What would be the best practice to update the data in the table from the model?
Linq-to-SQL can't deal with two objects with the same key in the same data context. Some options:
Option 1: Use two different data contexts:
public void Update(UserDetail modifiedUser)
{
using (UserDetailDataContext dc1 = new UserDetailDataContext())
using (UserDetailDataContext dc2 = new UserDetailDataContext())
{
UserDetail originalUser = dc1.UserDetails.Single(u => u.id == modifiedUser.id);
dc2.UserDetails.Attach(modifiedUser, originalUser);
dc2.SubmitChanges();
}
}
Option 2: Don't pull the original object; annotate the new object as an update:
public void Update(UserDetail modifiedUser)
{
using (UserDetailDataContext dc = new UserDetailDataContext())
{
dc.UserDetails.Attach(modifiedUser);
dc.Refresh(RefreshMode.KeepCurrentValues, modifiedUser);
dc.SubmitChanges();
}
}
There's more discussion in this question, and Rick Strahl has a blog entry that covers a versioning-based strategy, if you're willing to modify the table schema to add a timestamp.

Pass multiple data items from controller to view

I am using VS 2010, MVC, VS 2005
I create .dbml file as my model and map tables inside .dbml file
i join tables using LINQ to SQL. I want to display record of two tables i.e. tbl_class, tbl_subject
COde in my controller looks like this
public ActionResult SubjectByTeacher()
{
var DataContext = new SMSAPPDataContext();
var resultclass = (from t in DataContext.tbl_teachers
from e in DataContext.tbl_teacherenrollments
from b in DataContext.tbl_batches
from c in DataContext.tbl_classes
from s in DataContext.tbl_subjects
where
t.Teacher_ID == e.Teacher_ID
&&
e.Batch_ID == b.Batch_ID
&&
b.Class_ID == c.Class_ID
&&
e.Sub_ID == s.Sub_ID
&&
t.Teacher_Name == "ABC"
select c;
var resultsubject = from t in DataContext.tbl_teachers
from e in DataContext.tbl_teacherenrollments
from b in DataContext.tbl_batches
from c in DataContext.tbl_classes
from s in DataContext.tbl_subjects
where
t.Teacher_ID == e.Teacher_ID
&&
e.Batch_ID == b.Batch_ID
&&
b.Class_ID == c.Class_ID
&&
e.Sub_ID == s.Sub_ID
&&
t.Teacher_Name == "ABC"
select s;
return View();
}
Then i create a class in controller to map above two variables i.e. resultclass, resultsubject
public class MyViewModel
{
public MyViewModel(SMSAPPDataContext resultclass, SMSAPPDataContext resultsubject)
{
this.rc = resultclass;
this.rs = resultsubject;
}
public SMSAPPDataContext rc { get; private set; }
public SMSAPPDataContext rs { get; private set; }
}
This class will be used in creating view as model in strongly typed view.
But i cant figure it out, what to pass in return view ?????
It may be just like i.e. return view(new myviewmodel);
But this gives error, Should i use ToList() property any where in code ???
If any one can told me any other way to do this, please help
Regards
Assuming your view is strongly typed to use MyViewModel, e.g:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<YourNameSpaceHere.MyViewModel>" %>
Then using something like
return View(new MyViewModel(resultclass, resultsubject));
In the controller should work. Although I don't think SMSAPPDataContext is the correct type that the LINQ query will be returning.

ASP.NET MVC: Get all controllers

Is it possible to get all controllers available to a ControllerFactory?
What I want to do is get a list of all controller types in application, but in a consistent way.
So that all controllers I get are the same ones default request resolution is using.
(The actual task is to find all action methods that have a given attribute).
You can use reflection to enumerate all classes in an assembly, and filter only classes inherit from Controller class.
The best reference is asp.net mvc source code. Take a look of the implementations of ControllerTypeCache and ActionMethodSelector class.
ControllerTypeCache shows how to get all controller classes available.
internal static bool IsControllerType(Type t) {
return
t != null &&
t.IsPublic &&
t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
!t.IsAbstract &&
typeof(IController).IsAssignableFrom(t);
}
public void EnsureInitialized(IBuildManager buildManager) {
if (_cache == null) {
lock (_lockObj) {
if (_cache == null) {
List<Type> controllerTypes = GetAllControllerTypes(buildManager);
var groupedByName = controllerTypes.GroupBy(
t => t.Name.Substring(0, t.Name.Length - "Controller".Length),
StringComparer.OrdinalIgnoreCase);
_cache = groupedByName.ToDictionary(
g => g.Key,
g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
StringComparer.OrdinalIgnoreCase);
}
}
}
}
And ActionMethodSelector shows how to check if a method has desired attribute.
private static List<MethodInfo> RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos) {
// remove all methods which are opting out of this request
// to opt out, at least one attribute defined on the method must return false
List<MethodInfo> matchesWithSelectionAttributes = new List<MethodInfo>();
List<MethodInfo> matchesWithoutSelectionAttributes = new List<MethodInfo>();
foreach (MethodInfo methodInfo in methodInfos) {
ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);
if (attrs.Length == 0) {
matchesWithoutSelectionAttributes.Add(methodInfo);
}
else if (attrs.All(attr => attr.IsValidForRequest(controllerContext, methodInfo))) {
matchesWithSelectionAttributes.Add(methodInfo);
}
}
// if a matching action method had a selection attribute, consider it more specific than a matching action method
// without a selection attribute
return (matchesWithSelectionAttributes.Count > 0) ? matchesWithSelectionAttributes : matchesWithoutSelectionAttributes;
}
I don't think it's possible to give a simple answer to this question, because it depends on a lot of different things, including the implementation of IControllerFactory.
For instance, if you have a completely custom-built IControllerFactory implementation, all bets are off, because it may use any sort of mechanism to create Controller instances.
However, the DefaultControllerFactory looks after the appropriate Controller type in all the assemblies defined in the RouteCollection (configured in global.asax).
In this case, you could loop through all the assemblies associated with the RouteCollection, and look for Controllers in each.
Finding Controllers in a given assembly is relatively easy:
var controllerTypes = from t in asm.GetExportedTypes()
where typeof(IController).IsAssignableFrom(t)
select t;
where asm is an Assembly instance.

Resources