C# 将代码重构为泛型方法
我在EntityFramework支持的存储库中有一个重复出现的代码块,我希望以某种方式对其进行泛型并将其作为方法调用,以便重用代码而不是重复代码 当前代码块如下所示:C# 将代码重构为泛型方法,c#,entity-framework,C#,Entity Framework,我在EntityFramework支持的存储库中有一个重复出现的代码块,我希望以某种方式对其进行泛型并将其作为方法调用,以便重用代码而不是重复代码 当前代码块如下所示: // Archive deleted MyItems sections _t.MyItems.Where(x => x.ValidTo == null && !team.MyItems.Contains(x)).ToList().ForEach(x => x.Valid
// Archive deleted MyItems sections
_t.MyItems.Where(x => x.ValidTo == null && !team.MyItems.Contains(x)).ToList().ForEach(x => x.ValidTo = DateTime.Now);
// Add or update MyItems sections
foreach (var MyItemsSection in team.MyItems)
{
if (MyItemsSection.Id == default(int))
{
MyItemsSection.ValidFrom = DateTime.Now;
_t.MyItems.Add(MyItemsSection);
}
else
{
var _MyItemsSection = _t.MyItems.FirstOrDefault(x => x.Id == MyItemsSection.Id);
context.Entry(_MyItemsSection).CurrentValues.SetValues(MyItemsSection);
}
}
\u t
是EntityFramework连接的对象图,而team
是一种相同类型的对象图,已断开连接并可能在外部更新。这里的目标是同步两个对象图,以便保留更改
我需要传入_t.MyItems和team.MyItems,其中MyItems将被泛化,因此相同的方法适用于MyOtherItems和MySocks、myintrants等
这是可能的吗?您有两种选择:要么将对象约束到包含要在泛型方法中访问的属性和方法的已知基类型,要么使用谓词进行选择 限制条件:
// base type
interface IFoo {
int ID { get; set; }
}
// generic method
public List<T> Update<T>(List<T> graph1, List<T> graph2) where T : IFoo {
var update = graph1.Intersect(graph2, (g1, g2) => { g1.ID == g2.ID }).ToList();
return update;
}
//基类型
接口IFoo{
int ID{get;set;}
}
//通用方法
公共列表更新(列表图1,列表图2),其中T:IFoo{
var update=graph1.Intersect(graph2,(g1,g2)=>{g1.ID==g2.ID}).ToList();
返回更新;
}
谓词:
public void Update<T, U>(T _t, T team, Func<T, IList<U>> selector)
{
var _tItems = selector(_t);
var teamItems = selector(team);
// Archive deleted MyItems sections
_tItems.Where(x => x.ValidTo == null && !teamItems.Contains(x)).ToList().ForEach(x => x.ValidTo = DateTime.Now);
// Add or update MyItems sections
foreach (var MyItemsSection in teamItems)
{
if (MyItemsSection.Id == default(int))
{
MyItemsSection.ValidFrom = DateTime.Now;
_tItems.Add(MyItemsSection);
}
else
{
var _MyItemsSection = _tItems.FirstOrDefault(x => x.Id == MyItemsSection.Id);
context.Entry(_MyItemsSection).CurrentValues.SetValues(MyItemsSection);
}
}
}
//Usage:
Update(_t, team, (t => t.MyItems));
公共无效更新(T\U T、T团队、职能选择器)
{
var _tItems=选择器(_t);
var teamItems=选择器(团队);
//归档已删除的MyItems部分
_tItems.Where(x=>x.ValidTo==null&&!teamItems.Contains(x)).ToList().ForEach(x=>x.ValidTo=DateTime.Now);
//添加或更新MyItems部分
foreach(teamItems中的var MyItemsSection)
{
if(MyItemsSection.Id==默认值(int))
{
MyItemsSection.ValidFrom=DateTime.Now;
_添加(MyItemsSection);
}
其他的
{
var _MyItemsSection=_tItems.FirstOrDefault(x=>x.Id==MyItemsSection.Id);
context.Entry(_MyItemsSection).CurrentValues.SetValues(MyItemsSection);
}
}
}
//用法:
更新(_t,team,(t=>t.MyItems));
但话说回来,是什么阻止你编写一个以列表为参数的方法呢
正如在
public void Update(IList _tItems,IList teamItems)
中回答我自己的问题一样,这里有一个答案-我缺少的是,您可以要求传入类型实现特定接口,并且仍然可以按照所需的类型使用它
下面是我的想法:
public void UpdateEntities<TEntity>(ICollection<TEntity> pocoCollection, ICollection<TEntity> dbCollection)
where TEntity : class, IEntity
{
// Archive deleted entities
dbCollection.Where(x => x.ValidTo == null && !pocoCollection.Contains(x)).ToList().ForEach(x => x.ValidTo = DateTime.Now);
// Add or update entities
foreach (var entity in pocoCollection)
{
if (entity.Id == default(int))
{
entity.ValidFrom = DateTime.Now;
dbCollection.Add(entity);
}
else
{
var _entity = dbCollection.FirstOrDefault(x => x.Id == entity.Id);
context.Entry(_entity).CurrentValues.SetValues(entity);
}
}
}
这使得编译器不再抱怨使用了这些属性,而我仍然可以使用实际的类型,这样实体框架也会得到满足,并且不会对正在发生的事情感到困惑
希望这对其他人有所帮助。如果我们假设
MyItems
是T
的集合,那么所有其他T
是否都有属性Id
和有效?这似乎是使其更通用并适用于不同集合的症结所在。我认为您应该在codereview上发布这一点:是的,all具有Id和ValidTo,但context.Entry().CurrentValues.SetValues()仍然需要工作,因此仅基于这两个属性的接口将无法工作。虽然我不知道怎么做,但我很确定我也需要传递类型。约束到基类型将不起作用,因为它们将是不同的-我想要泛化的结构更多地是EF工作方式的产物,而不是我自己的工作方式,所以EF仍然必须能够完成它的工作。我不知道您是否注意到了,但我的代码示例有三件事正在进行——归档已删除的项目、添加新项目和更新现有项目。由于各种原因,EF使得仅仅合并对象图成了问题。@Moo-您可以将它们约束到一个基本接口,您可以通过修改T4模板或将其添加到带有partial
类对象另一部分的新文件中,将其添加到对象中。这根本不会影响EF,如果EF已经生成接口所需的属性,则不必执行任何其他操作。我理解这一点,但不会影响context.Entry().CurrentValues.SetValues()获得的类型不是它期望的类型(即实体)?由于您可以将实体同时传递给Entry()和SetValues(),因此我并不完全相信传递一个受约束的类型不会导致EF完全忽略这样一个事实,即我正在传递它知道的两个实体类型。这有意义吗?我明白你的意思,但我不完全确定答案。我认为真正的问题在于\u t.MyItems
——因为您不知道将是哪种类型的MyItem
,所以您不知道要查找哪个表/属性才能找到它。一旦你找到了它,你应该能够很好地使用它,但找到它会很困难。据我所知,至少-我没有对EF做过任何广泛的研究,所以我可能会弄错。谢谢你的回答,但我开始认为这是一个任何通用形式看起来更像加密色情而不是可读代码的时代,如果可能的话:)我会在传递MyItem时知道MyItem的类型,所以,如果我可以传递类型和对象,那么那里可能会发生什么。。。?
public interface IEntity
{
int Id { get; set;}
DateTime ValidFrom { get; set; }
DateTime? ValidTo { get; set; }
}