I am inserting 5000 transactions with about 6 banks, but I get 5000 bank rows in the database with duplicate names.
public class Transaction
{
[Key]
public int Id { get; set; }
public DateTime TransDate { get; set; }
public decimal Value { get; set; }
public string Description { get; set; }
public Bank Bank { get; set; }
}
public class Bank
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class FinancialRecordContext : DbContext
{
public FinancialRecordContext() : base("FinancialRecordDatabase") { }
public DbSet<Transaction> Transactions { get; set; }
public DbSet<Bank> Banks { get; set; }
public Bank FindOrInsertBank(string bankName)
{
var bank = Banks.SingleOrDefault(b => b.Name == bankName);
if (bank == null)
{
bank = new Bank { Name = bankName };
Banks.Add(bank);
}
return bank;
}
}
Then to insert I am looping through some data and inserting thusly:
using (var context = new FinancialRecordContext())
{
foreach (var t in data)
{
var tran = new Transaction
{
Description = t.Description,
Value = t.Value,
TransDate = t.TransDate,
Bank = context.FindOrInsertBank(t.BankName)
};
context.Transactions.Add(tran);
}
context.SaveChanges();
}
It would appear that the FindOrInsertBank
method is going to the database all the time and not looking locally at it's recently added, but not committed banks. How can/should I be doing this?
Should I SaveChanges
after each bank insert? Not really what I want to do I want this to be all one transaction.
You can try to query the change tracker (which is an in-memory query, not a database query):
using (var context = new FinancialRecordContext())
{
foreach (var t in data)
{
Bank bank = context.ChangeTracker.Entries()
.Where(e => e.Entity is Bank && e.State != EntityState.Deleted)
.Select(e => e.Entity as Bank)
.SingleOrDefault(b => b.Name == t.BankName);
if (bank == null)
bank = context.FindOrInsertBank(t.BankName);
var tran = new Transaction
{
Description = t.Description,
Value = t.Value,
TransDate = t.TransDate,
Bank = bank
};
context.Transactions.Add(tran);
}
context.SaveChanges();
}
Edit
Using the change tracker here could be bad for performance because Bank.Name
is not the key of the entity and I guess the query would be a linear search through the entries. In this case using a handwritten dictionary might be the better solution:
using (var context = new FinancialRecordContext())
{
var dict = new Dictionary<string, Bank>();
foreach (var t in data)
{
Bank bank;
if (!dict.TryGetValue(t.BankName, out bank))
{
bank = context.FindOrInsertBank(t.BankName);
dict.Add(t.BankName, bank);
}
var tran = new Transaction
{
Description = t.Description,
Value = t.Value,
TransDate = t.TransDate,
Bank = bank
};
context.Transactions.Add(tran);
}
context.SaveChanges();
}
A couple of suggestions to try:
1) Check the Local
collection of DbSet<Bank>
first.
public Bank FindOrInsertBank(string bankName)
{
var bank = Banks.Local.SingleOrDefault(b => b.Name == bankName);
if (bank == null)
{
var bank = Banks.SingleOrDefault(b => b.Name == bankName);
if (bank == null)
{
bank = new Bank { Name = bankName };
Banks.Add(bank);
}
}
return bank;
}
2) Force a call to DetectChanges()
after each update
context.Transactions.Add(tran);
context.ChangeTracker.DetectChanges();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.