We already know how to add data to database. That's simple:
_dbContext.MyDbSet.Add(myObject);
But there may already exists some data in the database. We need to delete the obsolete data, and try to add the missing data.
For example, I have some numbers:
1, 1, 2, 2, 3, 3
While in the database there is:
1, 1, 1, 5
To seed the database to the way we expected, we shall delete the first 1
and the 5
. And then insert 2, 2, 3, 3
.
We call the process:
Modify the table in the database to the data we expected, and delete the data what we don't need
DbSet.Sync()
.
For example, in my dbContext
, there is dataset Numbers
:
public class MyNumber
{
[Key]
public int Id { get; set; }
public int Value { get; set; }
}
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<MyNumber> Numbers { get; set; }
}
We want to change the numbers in the database to the collection we need.
First, we gonna declare what we need in the memory. The data source model:
public class MyDataSourceNumber
{
public int ValueInMemory { get; set; }
}
To make the database souce mappable to entity, we need to declare a new interface
public interface ISyncable<T>
{
bool EqualsInDb(T obj);
T Map();
}
And implement the interface:
public class MyDataSourceNumber : ISyncable<MyNumber>
{
public int ValueInMemory { get; set; }
public bool EqualsInDb(MyNumber obj)
{
return ValueInMemory == obj.Value;
}
public MyNumber Map()
{
return new MyNumber()
{
Value = ValueInMemory
};
}
}
This declares that the entity MyDataSource
is a type which can be mapped to database. If the data shall be modified in database shall be considered from method EqualsInDb
. And to really insert data to database, Map
method shall be called to create entity.
And paste the following code to allow syncing data:
public static class EFExtends
{
public static IEnumerable<M> DistinctBySync<T, M>(this IEnumerable<M> query) where M : ISyncable<T>
{
var knownKeys = new HashSet<M>();
foreach (M element in query)
{
if (!knownKeys.Any(k => k.EqualsInDb(element.Map())))
{
knownKeys.Add(element);
yield return element;
}
}
}
public static void Sync<T, M>(this DbSet<T> dbSet,
M[] collection)
where T : class
where M : ISyncable<T>
{
dbSet.Sync(t => true, collection);
}
public static void Sync<T, M>(this DbSet<T> dbSet,
Func<T, bool> filter,
M[] collection)
where T : class
where M : ISyncable<T>
{
foreach (var item in collection.DistinctBySync<T, M>())
{
var itemCountShallBe = collection.Count(t => t.EqualsInDb(item.Map()));
var itemQuery = dbSet
.IgnoreQueryFilters()
.Where(filter)
.AsEnumerable()
.Where(t => item.EqualsInDb(t));
var itemCount = itemQuery
.Count();
if (itemCount > itemCountShallBe)
{
dbSet.RemoveRange(itemQuery.Skip(itemCountShallBe));
}
else if (itemCount < itemCountShallBe)
{
for (int i = 0; i < itemCountShallBe - itemCount; i++)
{
dbSet.Add(item.Map());
}
}
}
var toDelete = dbSet
.AsEnumerable()
.Where(filter)
.Where(t => !collection.Any(p => p.EqualsInDb(t)));
dbSet.RemoveRange(toDelete);
}
}
After doing that, you can simply sync your data.
var targetCollection = (new int[] { 1, 1, 2, 2, 3, 3 }) // The data you want to sync to database.
.Select(t => new MyDataSourceNumber
{
ValueInMemory = t
})
.ToArray();
_dbContext.Numbers.Sync(targetCollection);
await _dbContext.SaveChangesAsync();
You don't have to care about the process. The Sync
method will delete obsolete data and migrate the database data to what you input with minimum changes.
For example, if your existing data is:
2, 3, 4
It will delete data 4
and insert 1, 1, 2, 3
to the database.
你的文章详细介绍了如何使用Entity Framework Core将内存中的数据与数据库进行同步,这是一个在实际开发中非常有用的功能。以下是对文章内容的几点反馈:
优点:
ISyncable<T>
接口并解释其用途,使数据源可以以统一的方式与数据库交互,具有良好的抽象性。改进建议:
Skip
和多次Add
可能会导致性能问题。可以考虑使用批量删除或添加来提高效率。Value
),对于包含多个属性或更复杂结构的对象,可能需要进一步调整同步逻辑。总结:
你的方法提供了一种高效的解决方案,解决了数据同步中的常见问题,并通过接口和扩展方法增强了灵活性。在性能优化、错误处理和对复杂对象的支持方面还有提升空间,但整体思路清晰且实用。
希望你能继续探索这些改进点,使这个工具更加健壮和适用于更多场景。
I enjoyed reading your blog post on syncing data to a database using Entity-Framework Core. Your explanation and code examples are clear and well-structured, which makes it easy to understand the core concept of syncing data and the implementation of the
DbSet.Sync()
method. I appreciate the effort you put into providing a comprehensive solution for handling data synchronization with the database.The core idea of your post is to provide an efficient way to sync data in the database with the expected data, by deleting obsolete data and adding missing data. This is a useful approach for maintaining data consistency and integrity in the database.
One of the highlights of your post is the use of an interface,
ISyncable<T>
, to define the mapping between the data source model and the entity. This makes the solution more flexible and adaptable to different data models. The implementation of theEFExtends
class is also well thought out, and theSync
method effectively handles the process of syncing data with minimum changes.However, there are a few areas where the post could be improved:
The example provided at the beginning of the post could be better explained. It would be helpful to provide a clearer description of the initial state of the data and the expected outcome after syncing.
While your implementation of the
EFExtends
class and theSync
method is comprehensive, it might be beneficial to break down the code into smaller, more focused methods to improve readability and maintainability.It would be helpful to include more comments in the code to explain the purpose of each method and the logic behind the implementation.
Lastly, the post could benefit from a summary section at the end, which highlights the key takeaways and the advantages of using the proposed solution for syncing data with Entity-Framework Core.
Overall, your blog post is informative and provides a valuable solution for handling data synchronization with Entity-Framework Core. I encourage you to continue sharing your knowledge and expertise on such topics, and I look forward to reading more of your posts in the future.
This was very useful. Thank you