I'm new to EF and I've only worked with EF 6. When I use it to access the data from a database that I have already designed I don't see any method that allows me to manipulate data. To solve this temporarily I created some stored procedures for adding, deleting and updating data.
I would like to know if what I am doing is the right way to manipulate data in EF or not. In case it is not the right way how can I do this using the built in features of EF6. MSDN said there is an add object but couldn't find it.
There have been some changes in the API. EF6 does not use ObjectContext anymore, it uses a DbContext. This can be generated from a Database Model, or created using a Model first approach.
Old syntax:
objectContext.AddToUsers(user);
is now:
dbContext.Users.Add(user);
Here are some basic samples:
insert:
using(var dbContext = new MyDbContext())
{
var user = new User { ID=1, Name="Test" };
dbContext.Users.Add(user); // Add user
dbContext.SaveChanges(); // Save changes to DB
}
update:
using(var dbContext = new MyDbContext())
{
var user = dbContext.Users.Find(1);// find by ID = 1
user.Name = "New Name"; // Change name
dbContext.SaveChanges(); // Save changes to DB
}
delete:
using(var dbContext = new MyDbContext())
{
var user = dbContext.Users.Find(1);// find by ID = 1
dbContext.Users.Remove(user); // delete user
dbContext.SaveChanges(); // Save changes to DB
}
So, no need for stored procedures.. definetly not needed for simple CRUD.
Related
I'm using entity framework for first time. I have to create many entities(tables) with using of Entity Framework 6, is there any way how automatically create history table for all the entities and insert old version of a row into EntityHistory table whenever is row changed?
If i am not wrong we can manage it With the help of Entity Framework Snapshot History.
Click here to view the similar answer.
Disclaimer: I'm the owner of the project Entity Framework Plus
Documentations: EF+ Audit
This library allows you to audit & save information in a database with the AutoSavePreAction Action.
AuditManager.DefaultConfiguration.AutoSavePreAction = (context, audit) =>
// ADD "Where(x => x.AuditEntryID == 0)" to allow multiple SaveChanges with same Audit
(context as EntityContext).AuditEntries.AddRange(audit.Entries);
// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntityContext();
// ... ctx changes ...
var audit = new Audit();
audit.CreatedBy = "ZZZ Projects"; // Optional
ctx.SaveChanges(audit);
// Access to all auditing information
var entries = audit.Entries;
foreach(var entry in entries)
{
foreach(var property in entry.Properties)
{
}
}
I have a problem with saving changes to the database with entity framework. I'm using three tables; AspNetUsers, tblCountry, and tblCountry_AspNetUsers.
tblCountry_AspNetUsers consists of two columns; UserId and CountryId, which creates a one-to-many relationship between tblCountry and AspNetUsers.
Currently, what I want to do is change the country of a specific user, but entity framework doesn't let me access the tblCountry_AspNetUsers database, and instead creates an ICollection of AspNetUsers on tblCountry. I could assign the Id on the AspNetUser directly, but I don't want to start adding/removing columns from identity tables just yet since I'm using database first, and I've heard it can lead to problems.
Anyway, I can remove just fine from the ICollection and save those changes to the database, but when I try to add the same user to a different country, it doesn't save to the database properly, but I can find the user in the context object when debugging.
I've tried attaching and changing the entitystate to both added and modified, but when I try to do this, it breaks out of the method and doesn't update the database. (basically it freezes when I try to attach)
My code for editing a user looks like the following:
(Note that UserManager handles the identity usermodel, ApplicationUser,, which is not the same as AspNetUsers in this aspect)
(Also, tblCountry.AspNetUsers refers to the ICollection of users assigned to a specific country)
...
var aspuser = new AspNetUsers();
using (DbContext dc = new DbContext())
{
aspuser = dc.AspNetUsers.First(x => x.Id == userid);
var user = await UserManager.FindByIdAsync(userid);
user.Email = updatedUser.UserName;
user.UserName = updatedUser.Email;
var result = await UserManager.UpdateAsync(user);
aspuser.tblCountry.AspNetUsers.Remove(aspuser);
await dc.SaveChangesAsync();
}
using (DbContext dc = new DbContext())
{
var c = _country.GetById(newcountryid);
c.AspNetUsers.Add(aspuser);
await dc.SaveChangesAsync();
}
return Users(userid);
}
It would be extremely easy if it was a table I could access directly, but with an ICollection like this I'm confused as to what I should do to make it work, and I appreciate any input!
Cheers
This line aspuser = dc.AspNetUsers.First(x => x.Id == userid); is inside using (DbContext dc = new DbContext()) which means that object aspuser is detached from context when you leave that block. You should do:
var c = _country.GetById(newcountryid);
c.AspNetUsers.Add(aspuser);
await dc.SaveChangesAsync();
in the same using block as the above code, why did you separate it?
I want to use EF DbContext/POCO entities in a detached manner, i.e. retrieve a hierarchy of entities from my business tier, make some changes, then send the entire hierarchy back to the business tier to persist back to the database. Each BLL call uses a different instance of the DbContext. To test this I wrote some code to simulate such an environment.
First I retrieve a Customer plus related Orders and OrderLines:-
Customer customer;
using (var context = new TestContext())
{
customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}
Next I add a new Order with two OrderLines:-
var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;
Finally I persist the changes (using a new context):-
using (var context = new TestContext())
{
context.Customers.Attach(customer);
context.SaveChanges();
}
I realise this last part is incomplete, as no doubt I'll need to change the state of the new entities before calling SaveChanges(). Do I Add or Attach the customer? Which entities states will I have to change?
Before I can get to this stage, running the above code throws an Exception:
An object with the same key already exists in the ObjectStateManager.
It seems to stem from not explicitly setting the ID of the two OrderLine entities, so both default to 0. I thought it was fine to do this as EF would handle things automatically. Am I doing something wrong?
Also, working in this "detached" manner, there seems to be an lot of work required to set up the relationships - I have to add the new order entity to the customer.Orders collection, set the new order's Customer property, and its CustomerId property. Is this the correct approach or is there a simpler way?
Would I be better off looking at self-tracking entities? I'd read somewhere that they are being deprecated, or at least being discouraged in favour of POCOs.
You basically have 2 options:
A) Optimistic.
You can proceed pretty close to the way you're proceeding now, and just attach everything as Modified and hope. The code you're looking for instead of .Attach() is:
context.Entry(customer).State = EntityState.Modified;
Definitely not intuitive. This weird looking call attaches the detached (or newly constructed by you) object, as Modified. Source: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
If you're unsure whether an object has been added or modified you can use the last segment's example:
context.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
You need to take these actions on all of the objects being added/modified, so if this object is complex and has other objects that need to be updated in the DB via FK relationships, you need to set their EntityState as well.
Depending on your scenario you can make these kinds of don't-care writes cheaper by using a different Context variation:
public class MyDb : DbContext
{
. . .
public static MyDb CheapWrites()
{
var db = new MyDb();
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
return db;
}
}
using(var db = MyDb.CheapWrites())
{
db.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
db.SaveChanges();
}
You're basically just disabling some extra calls EF makes on your behalf that you're ignoring the results of anyway.
B) Pessimistic. You can actually query the DB to verify the data hasn't changed/been added since you last picked it up, then update it if it's safe.
var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then
db.Entry(existing).CurrentValues.SetValues(customer);
I am using EF 4.1 in a MVC 3 project and I am having problems with inserting an object. I am using session as well to hold onto some objects. In concrete :
I have a simple class with a parent child relationship:
public class Event
{
public User Promotor {get;set;}
}
The promotor is based on the CurrentUser. In my application I store the CurrentUser in http session. Now when I add an event like this the user (and all related objects) gets inserted one more time with a new primary key.
//1st request inserts/loads the user
User user;
using (var context = new MyDbContext())
{
user = new User();
context.Users.Add(user);
context.SaveChanges();
}
//2nd request saves the event
var before = db.Users.Count();
var #event = new Event
{
Promotor = user, //user was kept in Session
};
db.Entry(#event).State = EntityState.Added;
db.SaveChanges();
When i check the state of the user it is 'added' as well although the primary key is not 0 and EF should know it is already persistent. How can fix this without adding a lot of to my persistency code. Do I have to reattach my currentuser to the new dbcontext on every request? This will lead to db code 'leaking' into my application. I want to keep the DB stuff in a data layer. I am using a repository like this :
public void Save(T entity)
{
dbContext.Entry(entity).State = IsPersistent(entity) ?
EntityState.Modified : EntityState.Added;
dbContext.SaveChanges();
}
As you've already mentioned yourself, reattaching the user to your current context should solve the problem. The only other way I know of, would be to retrieve the object once again in your context based on the primary key value.
I'm currently working on a project which is using EF Code First with POCOs. I have 5 POCOs that so far depends on the POCO "User".
The POCO "User" should refer to my already existing MemberShip table "aspnet_Users" (which I map it to in the OnModelCreating method of the DbContext).
The problem is that I want to take advantage of the "Recreate Database If Model changes" feature as Scott Gu shows at: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx - What the feature basically does is to recreate the database as soon as it sees any changes in my POCOs. What I want it to do is to Recreate the database but to somehow NOT delete the whole Database so that aspnet_Users is still alive. However it seems impossible as it either makes a whole new Database or replaces the current one with..
So my question is: Am I doomed to define my database tables by hand, or can I somehow merge my POCOs into my current database and still take use of the feature without wipeing it all?
As of EF Code First in CTP5, this is not possible. Code First will drop and create your database or it does not touch it at all. I think in your case, you should manually create your full database and then try to come up with an object model that matches the DB.
That said, EF team is actively working on the feature that you are looking for: altering the database instead of recreating it:
Code First Database Evolution (aka Migrations)
I was just able to do this in EF 4.1 with the following considerations:
CodeFirst
DropCreateDatabaseAlways
keeping the same connection string and database name
The database is still deleted and recreated - it has to be to for the schema to reflect your model changes -- but your data remains intact.
Here's how: you read your database into your in-memory POCO objects, and then after the POCO objects have successfully made it into memory, you then let EF drop and recreate the database. Here is an example
public class NorthwindDbContextInitializer : DropCreateDatabaseAlways<NorthindDbContext> {
/// <summary>
/// Connection from which to ead the data from, to insert into the new database.
/// Not the same connection instance as the DbContext, but may have the same connection string.
/// </summary>
DbConnection connection;
Dictionary<Tuple<PropertyInfo,Type>, System.Collections.IEnumerable> map;
public NorthwindDbContextInitializer(DbConnection connection, Dictionary<Tuple<PropertyInfo, Type>, System.Collections.IEnumerable> map = null) {
this.connection = connection;
this.map = map ?? ReadDataIntoMemory();
}
//read data into memory BEFORE database is dropped
Dictionary<Tuple<PropertyInfo, Type>, System.Collections.IEnumerable> ReadDataIntoMemory() {
Dictionary<Tuple<PropertyInfo,Type>, System.Collections.IEnumerable> map = new Dictionary<Tuple<PropertyInfo,Type>,System.Collections.IEnumerable>();
switch (connection.State) {
case System.Data.ConnectionState.Closed:
connection.Open();
break;
}
using (this.connection) {
var metaquery = from p in typeof(NorthindDbContext).GetProperties().Where(p => p.PropertyType.IsGenericType)
let elementType = p.PropertyType.GetGenericArguments()[0]
let dbsetType = typeof(DbSet<>).MakeGenericType(elementType)
where dbsetType.IsAssignableFrom(p.PropertyType)
select new Tuple<PropertyInfo, Type>(p, elementType);
foreach (var tuple in metaquery) {
map.Add(tuple, ExecuteReader(tuple));
}
this.connection.Close();
Database.Delete(this.connection);//call explicitly or else if you let the framework do this implicitly, it will complain the connection is in use.
}
return map;
}
protected override void Seed(NorthindDbContext context) {
foreach (var keyvalue in this.map) {
foreach (var obj in (System.Collections.IEnumerable)keyvalue.Value) {
PropertyInfo p = keyvalue.Key.Item1;
dynamic dbset = p.GetValue(context, null);
dbset.Add(((dynamic)obj));
}
}
context.SaveChanges();
base.Seed(context);
}
System.Collections.IEnumerable ExecuteReader(Tuple<PropertyInfo, Type> tuple) {
DbCommand cmd = this.connection.CreateCommand();
cmd.CommandText = string.Format("select * from [dbo].[{0}]", tuple.Item2.Name);
DbDataReader reader = cmd.ExecuteReader();
using (reader) {
ConstructorInfo ctor = typeof(Test.ObjectReader<>).MakeGenericType(tuple.Item2)
.GetConstructors()[0];
ParameterExpression p = Expression.Parameter(typeof(DbDataReader));
LambdaExpression newlambda = Expression.Lambda(Expression.New(ctor, p), p);
System.Collections.IEnumerable objreader = (System.Collections.IEnumerable)newlambda.Compile().DynamicInvoke(reader);
MethodCallExpression toArray = Expression.Call(typeof(Enumerable),
"ToArray",
new Type[] { tuple.Item2 },
Expression.Constant(objreader));
LambdaExpression lambda = Expression.Lambda(toArray, Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(tuple.Item2)));
var array = (System.Collections.IEnumerable)lambda.Compile().DynamicInvoke(new object[] { objreader });
return array;
}
}
}
This example relies on a ObjectReader class which you can find here if you need it.
I wouldn't bother with the blog articles, read the documentation.
Finally, I would still suggest you always back up your database before running the initialization. (e.g. if the Seed method throws an exception, all your data is in memory, so you risk your data being lost once the program terminates.) A model change isn't exactly an afterthought action anyway, so be sure to back your data up.
One thing you might consider is to use a 'disconnected' foreign key. You can leave the ASPNETDB alone and just reference the user in your DB using the User key (guid). You can access the logged in user as follows:
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
And then use the User's key as a FK in your DB:
Guid UserId = (Guid) currentUser.ProviderUserKey ;
This approach decouples your DB with the ASPNETDB and associated provider architecturally. However, operationally, the data will of course be loosely connected since the IDs will be in each DB. Note also there will be no referential constraints, whcih may or may not be an issue for you.