Entity Framework Code First lazy loading non navigation properties - entity-framework-4

I'm using the entity framework code first CTP4.
Is it possible to lazy load non navigation properties like you can in NH 3.
A common example would be having a table containing a binary column. I only want to retrieve this column's data when I explicitly ask for that property in my code e.g. image.ImageData
Thanks
Ben

Vote here
Vote here
Read this
Ugly workaround:
public static void Main()
{
IEnumerable<MyTable> table;
using (Entities context = new Entities())
{
var buffer =
context.MyTable
.Select(myTable => new
{
Id = myTable.Id,
OtherColumn = myTable.OtherColumn
})
.ToArray();
table = buffer
.Select(t => new MyTable
{
Id = t.Id,
OtherColumn = t.OtherColumn
});
}
}
This will not select the rest of the fields.

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 + "");

EF Code First to create multiple databases dynamically

Is it possible to generate different databases according to a specific parameter?
My final goal is john.domain.com => create john db, paul.domain.com => create paul db
How could I achieve this using EF6 code first, MVC5? Could model first do it?
Yes you can change the connection string at runtime, something like.
Need to add reference to System.Data.
public static class ConnectionStringExtension
{
public static void ChangeDatabaseTo(this DbContext db, string newDatabaseName)
{
var conStr = db.Database.Connection.ConnectionString;
var pattern = "Initial Catalog *= *([^;]*) *";
var newConStr = Regex.Replace(conStr, pattern, m =>
{
return m.Groups.Count == 2
? string.Format("Initial Catalog={0}", newDatabaseName)
: m.ToString();
});
db.Database.Connection.ConnectionString = newConStr;
}
}
Usage.
using (var db = new AppContext())
{
// Uses it just before any other execution.
db.ChangeDatabaseTo("MyNewDatabase");
}

Exclude property from updating when SaveChanges() is called

There appears to be two ways to update a disconnected Entity Framework entity using the "attach" method.
Method One is to simply set the disconnected entity's state as modified:
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
myDbContext.SaveChanges();
This will save all fields on the "dog" object. But say you are doing this from an mvc web page where you only allow editing of Dog.Name, and the only Dog property contained on the page is Name. Then one could do Method Two:
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).Property(o => o.Name).CurrentValue = dog.Name;
myDbContext.Entry(dog).Property(o => o.Name).IsModified = true;
myDbContext.SaveChanges();
Method Two could get quite verbose when there are a lot of properties to update. This prompted me to attempt Method Three, setting IsModified = false on the properties I don't want to change. This does not work, throwing the runtime error "Setting IsModified to false for a modified property is not supported":
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
myDbContext.Entry(dog).Property(o => o.Owner).IsModified = false;
myDbContext.SaveChanges();
I'd much prefer to use Method One everywhere, but there are many instances where my asp.net mvc view does not contain every scalar property of the Dog class.
My questions are:
Are there any attributes I could use on the POCO class that would tell Entity Framework that I never want the property to up updated? Eg, [NeverUpdate]. I am aware of the [NotMapped] attribute, but that is not what I need.
Failing that, is there any way I can use Method One above (myDbContext.Entry(dog).State = EntityState.Modified;
) and exclude fields that I don't want updated?
P.S. I am aware of another way, to not use "attach" and simply fetch a fresh object from the database, update the desired properties, and save. That is what I am doing, but I'm curious if there is a way to use "attach," thus avoiding that extra trip to the database, but do it in a way that is not so verbose as Method Two above. By "fetch a fresh object" I mean:
Dog dbDog = myDbContext.Dogs.FirstOrDefault(d => d.ID = dog.ID);
dbDog.Name = dog.Name;
myDbContext.SaveChanges();
The following may work works.
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
var objectContext = ((IObjectContextAdapter) myDbContext).ObjectContext;
foreach (var entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Where(entity => entity.Entity.GetType() == typeof(Dogs)))
{
// You need to give Foreign Key Property name
// instead of Navigation Property name
entry.RejectPropertyChanges("OwnerID");
}
myDbContext.SaveChanges();
If you want to do it in a single line, use the following extension method:
public static void DontUpdateProperty<TEntity>(this DbContext context, string propertyName)
{
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
foreach (var entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Where(entity => entity.Entity.GetType() == typeof(TEntity)))
{
entry.RejectPropertyChanges(propertyName);
}
}
And use it like this
// After you modify some POCOs
myDbContext.DontUpdateProperty<Dogs>("OwnerID");
myDbContext.SaveChanges();
As you can see, you can modify this solution to fit your needs, e.g. use string[] properties instead of string propertyName as the argument.
Suggested Approach
A better solution would be to use an Attribute as you suggested ([NeverUpdate]). To make it work, you need to use SavingChanges event (check my blog):
void ObjectContext_SavingChanges(object sender, System.Data.Objects.SavingChangesEventArgs e)
{
ObjectContext context = sender as ObjectContext;
if(context != null)
{
foreach(ObjectStateEntry entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
{
var type = typeof(entry.Entity);
var properties = type.GetProperties();
foreach( var property in properties )
{
var attributes = property.GetCustomAttributes(typeof(NeverUpdateAttribute), false);
if(attributes.Length > 0)
entry.RejectPropertyChanges(property.Name);
}
}
}
}
// Check Microsoft documentation on how to create custom attributes:
// http://msdn.microsoft.com/en-us/library/sw480ze8(v=vs.80).aspx
public class NeverUpdateAttribute: SystemAttribute
{
}
//In your POCO
public class Dogs
{
[NeverUpdate]
public int OwnerID { get; set; }
}
Warning: I did not compile this code. I'm not at home :/
Warning 2: I have just read the MSDN documentation and it says:
ObjectStateEntry.RejectPropertyChanges Method
Rejects any changes made to the property with the given name since the
property was last loaded, attached, saved, or changes were accepted.
The orginal value of the property is stored and the property will no
longer be marked as modified.
I am not sure what its behavior would be in the case of attaching a modified entity. I will try this tomorrow.
Warning 3: I have tried it now. This solution works. Property that is rejected with RejectPropertyChanges() method are not updated in the persistence unit (database).
HOWEVER, if the entity that is updated is attached by calling Attach(), the current context remains dirty after SaveChanges(). Assume that the following row exists in the database:
Dogs
ID: 1
Name: Max
OwnerID: 1
Consider the following code:
var myDog = new Dogs();
myDog.ID = 1;
myDog.Name = Achilles;
myDog.OwnerID = 2;
myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).State = EntityState.Modified;
myDbContext.SaveChanges();
The current state of database after SaveChanges():
Dogs:
ID: 1
Name: Achilles
OwnerID: 1
The current state of myDbContext after SaveChanges():
var ownerId = myDog.OwnerID; // it is 2
var status = myDbContext.Entry(myDog).State; // it is Unchanged
So what you should do? Detach it after SaveChanges():
Dogs myDog = new Dogs();
//Set properties
...
myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).State = EntityState.Modified;
myDbContext.SaveChanges();
myDbContext.Entry(myDog).State = EntityState.Detached;

mvc LinqToSql add row "Select User" for a dropdown list

I'm new to Linq. I have searched and searched the web for a solution, and can't find anything. I have a Linq query and I want to insert a row ("Select User") to the top before I pass it to the drop down list. I've been trying to use the Union but to now avail (it keeps telling me that my object doesn't support the Union method). My code, prior to attempting to insert a row, is very simple.
public SelectList DropDown_Users()
{
var context = new VivarianDataContext();
var query = from t in context.AspnetUsers
select new { t.UserId, t.LastName };
list = new SelectList(query.AsEnumerable(), "UserId", "LastName");
return list;
}
Now I try to insert a row and I found this on the internet and it seems to say that his solution will work. But it is filled with errors. http://magicode.wordpress.com/2009/08/20/inserting-an-item-in-iqueryable-object-using-union-method-and-linq/
I tried to implement it using the following code, but it doesn't compile.
public SelectList DropDown_Users()
{
SelectList list;
//get the original data
var context = new SQL2005633131VivarianDataContext();
var query = from t in context.AspnetUsers
select new { t.UserId, t.LastName };
//create a dummy table with an empty row
var AllUsers = new List<AspnetUsers>();
var BlankUser = new AspnetUsers()
{UserId=System.Guid.Empty, LastName="Select One"};
AllUsers.Add(BlankUser);
//use Union to join the data - ERRORS HERE - doesn't support Union
var newTable = AllUsers.Union(query);
list = new SelectList(newTable.AsEnumerable(), "UserId", "LastName");
return list;
}
So tired I'm going blind. Any help?
You don't need to touch the query result. You can add that default option "Select User" in the dropdownlist.
Try this:
How can I add an item to a SelectList in ASP.net MVC

Best way to transfer an Entity Framework object over the web and back via JSON

I've got some MVC code that serializes an EF 3.5 object into an anonymous type for return as a JSON result to an AJAX call on my page. The hurdle I have is that when I send the object back to the server via JSON, (and let the ModelBinder deserialize it for me into my EF type), I have to update it in my Entity Framework context manually. Or at least that's what I'm doing now. It has no EntityKey, so attaching it fails. I end up having to look up the old object and update it property by property. Any ideas around this? Is the solution to pass the EntityKey around with my object?
Here's what I have:
public void Update(Album album)
{
using (var db = new BandSitesMasterEntities())
{
var albumToUpdate = db.Album.First(x => x.ID == album.ID);
albumToUpdate.AlbumTitle = album.AlbumTitle;
albumToUpdate.Description = album.Description;
albumToUpdate.ReleaseYear = album.ReleaseYear;
albumToUpdate.ImageURL = album.ImageURL;
albumToUpdate.OtherURL = album.OtherURL;
db.SaveChanges();
}
}
And here's what I'd like to do, or something similar:
public void Update(Album album)
{
using (var db = new BandSitesMasterEntities())
{
db.Attach(album)
db.SaveChanges();
}
}
or you could use AutoMapper to map those fields for you, so you'd just add one extra line to your example.
Why not just use the UpdateModel or TryUpdateModel controller methods instead? It works really well with EF and you can even explicitly set the included property list.
The id parameter will auto-map via the MVC framework to the hidden field on your form specifying the id.
public void Update(int id, FormCollection collection)
{
using (var db = new BandSitesMasterEntities())
{
var albumToUpdate = db.Album.First(x => x.ID == id);
//use UpdateModel to update object, or even TryUpdateModel
UpdateModel(albumToUpdate, new string[] { "AlbumTitle", "Description", "ReleaseYear", "ImageURL", "OtherURL" });
db.SaveChanges();
}
}
This became much easier for us in EF 4.0. This is what we did in EF 3.5:
public static void AttachAsModified(this ObjectContext objectContext, string setName, object entity,
IEnumerable<String> modifiedFields)
{
objectContext.AttachTo(setName, entity);
ObjectStateEntry stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(entity);
foreach (String field in modifiedFields)
{
stateEntry.SetModifiedProperty(field);
}
}
And then:
using (var db = new BandSitesMasterEntities())
{
db.AttachAsModified("Album", album, new string[] { "AlbumTitle", "Description", "ReleaseYear", "ImageURL", "OtherURL" })
db.SaveChanges();
}
It becomes more complicated if you have foreign key constraints, but it looks like you don't.
There is no way around the entity key issue. You either have to add it to your anonymous type or I would recommend you port your code to using data services.
http://www.hanselman.com/blog/jQueryToShipWithASPNETMVCAndVisualStudio.aspx
which would allow you to do all of the db manipulation on the client side.
http://msdn.microsoft.com/en-us/data/bb931106.aspx
Did you try something like:
object original;
var key = contexte..CreateEntityKey("EntitySet", modified);
if(contexte.TryGetObjectByKey(key, out original))
{
var originalEntity = (YourEntityType)original;
// You have to mannualy set your entityKey
originalEntity.YourEntityReference.EntityKey = new EntityKey("Entities.EntitySet", "Id", modified.YourEntity.Id);
contexte.ApplyPropertyChanges("EntitySet", modified);
}
contexte.SaveChanges();
Assuming that your EntityReference are set by dropDown, you'll still have the Id
In your Album entity's partial class you may define a CopyFrom function and call it from your Update function
partial class Album
{
public void CopyFrom(Album album)
{
//individual field copying here
}
}
public void Update(Album album)
{
...
albumToUpdate.CopyFrom(album);
...
}

Resources