Code refactoring in a generic method

In my backup EntityFramework repository, which I would like to generalize in some way, and called as a method, I have code to re-occupy the code in order to reuse the code and not repeat it.

The current code block is as follows:

// 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); } } 

_t is an object graph bound to EntityFramework, and team is an identical type of object graph that has been disabled and possibly updated externally. The goal here is to synchronize two object plots so that changes are saved.

I need to pass _t.MyItems and team.MyItems where MyItems should be generalized, so the same method works for MyOtherItems and MySocks, MyUnderPants, etc.

Is it possible?

+4
source share
2 answers

In answer to my own question, here's the answer - what I was missing was that you can require the incoming type to execute a specific interface and still have it available as the necessary type.

So here is what I came up with:

 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); } } } 

The part I was looking for was where TEntity : class, IEntity

In this solution I have to make sure that my objects implement the IEntity interface, which is simple:

 public interface IEntity { int Id { get; set;} DateTime ValidFrom { get; set; } DateTime? ValidTo { get; set; } } 

This allows the compiler to stop complaining about the use of these properties, while I can still use the actual type, so the Entity Framework is also satisfied and left less confused as to what is happening.

Hope this helps someone else.

+1
source

You have two options: either you restrict your objects to a known base type that contains the properties and methods that you want to access in the general method, or you use predicates to select.

Limitations:

 // 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; } 

Predicates:

 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)); 

But then again, what prevents you from writing a method that accepts lists as parameters?

As in public void Update<T>(IList<T> _tItems, IList<T> teamItems)

+1
source

Source: https://habr.com/ru/post/1447983/


All Articles