how does entityframework save changes work? - c#

I am trying to save 2 entity into two different table and these have related by one to one. but there is something weird for me. look below code please
public int CreateNotification(BusinessModel.Notification notification)
{
var db = new DataAccess.ApplicationDbContext();
var dbNotification = new DataAccess.Notification()
{
Name = notification.Name,
};
db.Notifications.Add(dbNotification);
//set NotificationSchedule
var dbNotificationSchedule = new DataAccess.NotificationSchedule()
{
Schedule = JsonConvert.SerializeObject(notification.NotificationSchedule)
};
db.NotificatoinSchedule.Add(dbNotificationSchedule);
***db.SaveChanges();***
this code works and the Foreign key of Notification is set to Notification schedule right. BUT the question is how the Id of notification is set to the Id of notificatioschedule? if I Add the related entity after the savechange there is an error, "update entity error...."
var db = new DataAccess.ApplicationDbContext();
var dbNotification = new DataAccess.Notification()
{
Name = notification.Name,
};
db.Notifications.Add(dbNotification);
***db.SaveChanges();***
//set NotificationSchedule
var dbNotificationSchedule = new DataAccess.NotificationSchedule()
{
Schedule = JsonConvert.SerializeObject(notification.NotificationSchedule)
};
db.NotificatoinSchedule.Add(dbNotificationSchedule);
}
public class Notification
{
public int Id { get; set; }
public string Name { get; set; }
public virtual NotificationSchedule NotificationSchedules { get; set; }
}
public class NotificationSchedule
{
[ForeignKey("Notification")]
public int Id { get; set; }
public string Schedule { get; set; }
public virtual Notification Notification { get; set; }
}

Related

EF Core table first not saving entities in the database

I'm new to EF (table first) and I don't know why these related entities are not saving at all to my database.
These are the related entities, UserProfile has a set of Carts
public partial class UserProfile
{
public UserProfile()
{
Cart = new HashSet<Cart>();
Naquestions = new HashSet<Naquestions>();
}
public int Id { get; set; }
public string BotUserId { get; set; }
public int? PrestashopId { get; set; }
public bool Validated { get; set; }
public int Permission { get; set; }
public DateTime CreationDate { get; set; }
public ICollection<Cart> Cart { get; set; }
public ICollection<Naquestions> Naquestions { get; set; }
}
Cart has a set of OrderLines
public partial class Cart
{
public Cart()
{
OrderLine = new HashSet<OrderLine>();
OrderRequest = new HashSet<OrderRequest>();
}
public int Id { get; set; }
public int UserId { get; set; }
public bool Active { get; set; }
public UserProfile User { get; set; }
public ICollection<OrderLine> OrderLine { get; set; }
public ICollection<OrderRequest> OrderRequest { get; set; }
}
And when I try to add them:
public async Task AddOrderLineToUser(string botId, OrderLine orderLine)
{
using (var context = ServiceProvider.CreateScope())
{
var db = context.ServiceProvider.GetRequiredService<GretaDBContext>();
var user = await UserController.GetUserByBotIdAsync(botId);
var latestCart = user.Cart.OrderByDescending(c => c.Id).FirstOrDefault();
if (latestCart != null && latestCart.Active)
{
latestCart.OrderLine.Add(orderLine);
}
else
{
var newCart = new Cart()
{
Active = true,
};
newCart.OrderLine.Add(orderLine);
user.Cart.Add(newCart);
}
await db.SaveChangesAsync();
}
}
Nothing is saving to the database once db.SaveChangesAsync() is called.
As #Caius Jard said in the comments it seems that user comes from another context. Try
if (latestCart != null && latestCart.Active)
{
orderLine.CartId = latestCart.Id;
db.OrderLines // I assume it is name of your orderlines DbSet
.Add(orderLine);
}
else
{
var newCart = new Cart()
{
Active = true,
UserId = user.Id,
};
newCart.OrderLine.Add(orderLine);
db.Carts // also assuming name of DbSet
.Add(newCart);
}
Also you can take a look at Attach method.
But I would say that in general you are doing something not good. Usually creating new scope is not needed, and db context should be injected in corresponding class via ctor. If you still need to create new scope it would make sense to resolve UserController also. Also is UserController an ASP controller?

In nested class, how can control that the duplicate information is not recorded in a table

I have a web service that users can send for that information like the following example.
The problem occurs when the information sent in a class is the same. Like a "sampleLine" in code.
How can I control it not to be stored in the duplicate information "sampleLine" table?
public class samplePerson
{
public int ID { get; set; }
public string Name { get; set; }
public sampleCopmany PersonCopmany { get; set; }
public sampleLine PersonLine { get; set; }
}
public class sampleCopmany
{
public int ID { get; set; }
public string Name { get; set; }
public sampleLine CopmanyLine { get; set; }
}
public class sampleLine
{
public int ID { get; set; }
public string Name { get; set; }
}
public class sampleDBContext
{
private MyDBContext dBContext;
public sampleDBContext()
{
dBContext = new MyDBContext();
}
public void Save()
{
samplePerson samplePerson = new samplePerson();
samplePerson.ID = -1;
samplePerson.Name = "Reza";
samplePerson.PersonCopmany = new sampleCopmany()
{
ID = -1,
Name = "Test",
CopmanyLine = new sampleLine()
{
ID = -1,
Name = "line"
}
};
samplePerson.PersonLine = new sampleLine()
{
ID = -1,
Name = "line"
};
dBContext.Add(samplePerson);
dBContext.SaveChangesAsync();
}
}
Is it possible to control this item?
There is not any automatic way in EF that handles it for you and you need to manually check for the existence of data before creating it.
If the record exists, then use the existing key and if not then create the record as you have done in your sample code.

How do I update a document in MongoDB & change all properties apart from_id in C#

public class UserObject
{
public ObjectId _id { get; set; }// Self generated
public ulong UserID { get; set; } // Self generated
public string Username { get; set; }
public string CharClass{ get; set; }
public int CharLevel { get; set; }
public int CharColour { get; set; }
}
I want to update just the Username, CharClass, CharLevel & CharColour.
I currently have this:
var client = new MongoClient(DBString);
var database = client.GetDatabase("UserLists");
var collection = database.GetCollection<UserObject>(Convert.ToString(GuildId));
var filter = Builders<UserObject>.Filter.Eq(s => s.Username, newUserName);
var UpdatedUserObject = new UserObject
{
UserID = UserId,
Username = newUserName,
CharClass = newCharClass,
CharLevel = newCharLevel,
CharColour = newCharColour
};
collection.ReplaceOneAsync(filter, UpdatedUserObject);
If you have any suggestions that would be much appreciated, i've got the add working i just can't seem to get the update, thanks!
I ended up doing:
var client = new MongoClient(DBString);
var database = client.GetDatabase("UserLists");
var collection = database.GetCollection<UserObject>(Convert.ToString(GuildId));
var filter = Builders<UserObject>.Filter.Eq(s => s.Username, newUserName);
var UpdatedUserObject = new UserObject
{
UserID = UserId,
Username = newUserName,
CharClass = newCharClass,
CharLevel = newCharLevel,
CharColour = newCharColour
};
collection.ReplaceOne(filter, UpdatedUserObject);
Which then gave me the problem that it was trying to update the _id so i used this stack overflow to put [BsonIgnoreIfDefault] on it so it didn't try and update:
Unable to use BsonIgnoreIfDefault for property of type long
public class UserObject
{
[BsonIgnoreIfDefault]
public ObjectId _id { get; set; }// Self generated
public ulong UserID { get; set; } // Self generated
public string Username { get; set; }
public string CharClass{ get; set; }
public int CharLevel { get; set; }
public int CharColour { get; set; }
}
After this was fixed i found that the problem was that I was passing through the wrong username so it never matched & therefore didn't find anything to update, just me being stupid really, Thanks for the help

EF6 Interceptor to set a value on Insert or Update

I am having troubles trying to figure out how to use the EF6 interceptors to set a value on Insert/Update.
What I wanted to do is to have an interceptor to automatically create a new instance of Audit like so:
public class FooContext : DbContext
{
public DbSet<Invoice> Invoices { get; set; }
public DbSet<Audit> Audits { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public string Name { get; set; }
public Audit AuditAndConcurrencyKey { get; set; }
}
public class InvoiceItem
{
public int Id { get; set; }
public Invoice Header { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
//For legacy reasons. I know this design is wrong :(
public Audit AuditAndConcurrencyKey { get; set; }
}
public class Audit
{
public int Id { get; set; }
public int InstanceId { get; set; }
public string Message { get; set; }
}
[Test]
public void WillCreateAudit()
{
using (var db = new FooContext())
{
var inv = new Invoice {Name = "Foo Invoice"};
var invLine = new InvoiceItem {Header = inv, Price = 1, Name = "Apple"};
db.Invoices.Add(inv);
db.SaveChanges();
//Inceptors should figure out that we are working with "Invoice" and "InvoiceLine"
//And automatically create an "Audit" instance
Assert.That(inv.AuditAndConcurrencyKey != null);
Assert.That(invLine.AuditAndConcurrencyKey != null);
Assert.That(inv.AuditAndConcurrencyKey == invLine.AuditAndConcurrencyKey)
}
}
The first thing I checked is this example for SoftDeleteInterceptor. I don't think this is what I want because it looks like at the point where we are already generating the expression tree, we are no longer aware of the type of object you are working with.
I checked this example as well, but again, it looks like we are injecting strings instead of setting object references.
Ideally I want something like this:
public class AuditInterceptor
{
public void Intercept(object obj)
{
if (!(obj is Invoice) && !(obj is InvoiceItem))
return; //not type we are looking for, by-pass
//Set the audit here
}
}

Saving an entity graph using entity framework

I am writing a Data Access Layer using EntityFramework 6. What I want is that when I invoke the SaveChanges() method on the DbContext, it will save the entity together with the set of relevant entities associated via navigation properties. Following is the simple code I am trying to do.
public class Customer
{
public long ID { get; set; }
public string Name { get; set; }
public virtual IEnumberable<PhoneNumber> { get; set; }
}
public class PhoneNumber
{
public long ID { get; set; }
public string Number { get; set; }
}
public class SampleContext : DbContext
{
public virtual DbSet<Customer> Customers { get; set; }
public virtual DbSet<PhoneNumber> PhoneNumbers { get; set; }
}
using(var context = new SampleContext())
{
var customer = new Customer { ID = 1, Name = "John" };
customer.PhoneNumbers = new PhoneNumbers[]
{
new PhoneNumber { ID = 1, Number = "1.111.1111111" },
new PhoneNumber { ID = 2, Number = "1.111.1111112" }
}
context.Customers.Add(customer);
context.SaveChanges();
}
The above code saves the customer in the customers table but saves nothing in the PhoneNumbers table.
Strange but found a solution. The above code need a little modification to make it work. Followings are the modifications:
//In Customer class, changed following line:
public virtual IEnumberable<PhoneNumber> { get; set; }
//To:
public virtual ICollection<PhoneNumber> { get; set; }
//Then in using block initialized entities as follows:
using(var context = new SampleContext())
{
var customer = new Customer { ID = 1, Name = "John", PhoneNumbers = new List<PhoneNumber>() };
customer.PhoneNumbers.Add(new PhoneNumber { ID = 1, Number = "1.111.1111111" });
context.Customers.Add(customer);
context.SaveChanges();
}

Categories