EF Repository - Custom Remove Method - c#

I've created a generic EF repository. For every deletions I need to check if my entity has a specific interface and do some changes to some other entities before remove it. How would I do that?
I tried to crate a foreach like this but it's not working.
var entitiesToRemove = context.Set<TEntity>().Where(predicate).ToList();
foreach (var entityToRemove in entitiesToRemove)
{
///
}
My current remove method
public void Remove(Func<TEntity, bool> predicate)
{
context.Set<TEntity>()
.Where(predicate).ToList()
.ForEach(del => context.Set<TEntity>().Remove(del));
}

I believe you can tackle the problem via the Strategy pattern
What follows is just one possible approach and example. You'd have to take dependency injection into consideration as well (but you can adapt this idea)
/* Your Repository implementation would probably look like this */
public class GenericRepository<TEntity>
{
private readonly DbContext context;
private readonly RemoveStrategyFactory removeStrategyFactory;
public GenericRepository(DbContext context, RemoveStrategyFactory removeStrategyFactory)
{
this.context = context;
this.removeStrategyFactory = removeStrategyFactory;
}
public void Remove(Func<TEntity, bool> predicate)
{
var entitiesToRemove = context.Set<TEntity>()
.Where(predicate).ToList();
var removeStrategy = removeStrategyFactory.GetStrategy<TEntity>();
foreach (var entity in entitiesToRemove)
{
removeStrategy.BeforeRemove(entity);
context.Set<TEntity>().Remove(entity);
}
}
}
/* SAMPLE ENTITIES */
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public Customer Customer { get; set; }
public DateTime CreatedOn { get; set; }
public bool IsArchived { get; set; }
}
/* SAMPLE STRATEGIES and FACTORY */
public abstract class RemoveStrategy<TEntity>
{
public abstract void BeforeRemove(TEntity entity);
}
public sealed class DoNothingRemoveStrategy<TEntity> : RemoveStrategy<TEntity>
{
public override void BeforeRemove(TEntity entity)
{
// Do nothing
}
}
public sealed class CustomerRemoveStrategy : RemoveStrategy<Customer>
{
public override void BeforeRemove(Customer customer)
{
// Mark all orders as archived
foreach (var order in customer.Orders)
{
order.IsArchived = true;
}
}
}
public class RemoveStrategyFactory
{
private readonly Lazy<Dictionary<Type, object>> _lazyStrategyMap;
public RemoveStrategyFactory()
{
_lazyStrategyMap = new Lazy<Dictionary<Type, object>>(InitializeStrategyMap);
}
public RemoveStrategy<TEntity> GetStrategy<TEntity>()
{
var strategyMap = _lazyStrategyMap.Value;
object strategy;
if (strategyMap.TryGetValue(typeof(TEntity), out strategy))
{
return (RemoveStrategy<TEntity>) strategy;
}
return new DoNothingRemoveStrategy<TEntity>();
}
public Dictionary<Type, object> InitializeStrategyMap()
{
return new Dictionary<Type, object>
{
// CAREFUL: for Customer type, it must be a RemoveStrategy<Customer> or derived instance
{ typeof (Customer), new CustomerRemoveStrategy() }
};
}
}

Related

return a list of generic C#

I have a singleton class, holding several list of data. I want to have a function to return one of the list based on the requested data type
public interface IRentable
{
long Id { get; set; }
}
public class Book : IRentable
{
private long _id;
public long Id
{
get { return _id; }
set { _id = value; }
}
public string Name { get; set; }
public int Pages { get; set; }
}
public class DVD : IRentable
{
private long _id;
public long Id
{
get { return _id; }
set { _id = value; }
}
public string Name { get; set; }
public TimeSpan Length { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public IEnumerable<Entity> GetEntities<Entity>() where Entity : IRentable
{
switch(typeof(Entity).Name)
{
case nameof(Book):
return _bookList;
case nameof(DVD):
return _dvdList;
}
return null;
}
This is not working, since a conversion is needed. I wonder if there is any elegant solution (without serialize all elements or implement IConvertible)?
Background: I was looking into the implementation of Repository pattern implementation from Microsoft document website and a couple of YouTube videos. Their base class implementations are bounded with DBContext,
[from Microsoft doc]
public class GenericRepository<TEntity> where TEntity : class
{
internal SchoolContext context;
internal DbSet<TEntity> dbSet;
[from youtube]
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
So, this means this base class cannot be used for databases without EntityFramework. I'm trying to remove DBContext in my base class
public class InMemoryRepository<TEntity> : IRepository<TEntity> where TEntity : IRentable
{
private IEnumerable<TEntity> _entities;
public IEnumerable<TEntity> GetAll()
{
if (_entities == null)
{
_entities = DBReferenceSingleton.GetInstance.GetEntities<TEntity>();
}
return _entities;
}
public TEntity Get(long id)
{
GetAll();
if (_entities == null)
{
return default(TEntity);
}
return _entities.FirstOrDefault(t => t.Id == id);
}
(I was wrong in my previous code, as I was using "class" instead of "IRentable" as I thought it will be generic for all classes. After implementing the Get(long id) function. I found I have to have a IRentable interface so I made the changes. And the answer from The Lemon work perfectly.
Thanks
It doesn't look like generics seem the right tool here; not least because Book and DVD don't have a common base class.
I think you should have two methods:
public List<Book> GetBooks() => _bookList;
public List<DVD> GetDvds() => _dvdList;
Have you tried doing a Cast?
public class Book
{
public string Name { get; set; }
public int Pages { get; set; }
}
public class DVD
{
public string Name { get; set; }
public TimeSpan Length { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public IEnumerable<Entity> GetEntities<Entity>() where Entity : class
{
switch(typeof(Entity).Name)
{
case nameof(Book):
return _bookList as List<Entity>;
case nameof(DVD):
return _dvdList as List<Entity>;
}
return null;
}
}
the "as List< Entity >" will return null if the object is the wrong type, or the object as the type if it's the right type. The A = B as C pattern works for inherited types as well, just remember to check for null returned values for cases where your type isn't as well known as in this situation
A side comment on the usefulness of having a generic method in this case: In this method you're forced to set the type of entity each time explicitly, which means your method is functionally non-generic - so you might as well make two explicit methods.
One case where a generic method like the one you have might be more useful, is if book and dvd both inherited from a base class, and you had some follow up methods that needed to operate off a list of . For example, you might end up wanting to do something like this instead in your code:
public class Book : Rentable
{
public int Pages { get; set; }
}
public class DVD : Rentable
{
public TimeSpan Length { get; set; }
}
public class Rentable
{
public string Name { get; set; }
public string borrowedBy { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public enum RentableType { Book, DVD }
public IEnumerable<Rentable> GetEntities(RentableType entityType)
{
switch (entityType)
{
case RentableType.Book:
return _bookList.ToList<Rentable>();
case RentableType.DVD:
return _dvdList.ToList<Rentable>();
default:
throw new NotImplementedException($"Entity {entityType} not supported");
}
return null;
}
}

Apply patterns on repeating operations

What pattern/s can I apply on my code? I have been reading on the patterns Bridge, Command, and Builder, and it looks like I can apply them on the code block below, but I struggle at how I can apply them to my code. I've also tried adding a generic method, but I struggle with consuming it with my repository.
This is a webform backend code. I am using .Net 3.5 (can't upgrade), and C#7. Here is the code:
Entities
Note: I'm using data transfer objects because these classes have a lot of properties. Also, SpecialRequestDTO inherits StandardRequestDTO.
public class StandardRequest
{
public int RequestType { get; protected set; }
public string Name { get; protected set; }
private StandardRequest(StandardRequestDTO dto) { Name = dto.Name; }
public static StandardRequest Create(StandardRequestDTO dto) => new StandardRequest(dto);
}
public class SpecialRequest : StandardRequest
{
public string Desc { get; protected set; }
private SpecialRequest(SpecialRequestDTO dto) : base((StandardRequestDTO) dto) { Desc = dto.Desc; }
public static SpecialRequest Create(SpecialRequestDTO dto) => new SpecialRequestDTO(dto);
}
Repository
public class Repository
{
public void SaveStandardRequest(StandardRequest request)
{
var query = $"INSERT INTO Requests (Name, RequestType) Values(#{nameof(request.Name)}, #{nameof(request.RequestType)})";
// sqlcommand code etc
}
public void SaveSpecialRequest(SpecialRequest request)
{
var query = $"INSERT INTO Requests (Name, RequestType, Desc) VALUES(#{nameof(request.Name)}, #{nameof(request.Name)}, #{nameof(request.Desc)})";
// sqlcommand code etc
}
}
Index.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
string requestTypeStr = Request.Form[nameof(requestTypeStr)];
if (string.IsNullOrEmpty(requestTypeStr))
return;
}
if (requestTypeStr == 0)
{
ValidateStandardRequestFields();
var dto = CreateStandardRequestDTO();
_repository.SaveStandardRequest(dto);
}
if (requestTypeStr == 1)
{
ValidateSpecialRequestFields();
var dto = CreateSpecialRequestDTO();
_repository.SaveSpecialRequest(dto);
}
}
I took most of what I could from your post and this is what I could come up with.
Your DTO objects don't seem very clear to me, so I've left it out, but they have a role to play when it comes to saving the objects in the repository and I've left that part out.
Interface
public interface IRequest
{
int RequestType { get; set; }
string Name { get; set; }
void ValidateFields();
}
Two types of Request
public class StandardRequest : IRequest
{
public int RequestType { get ; set ; }
public string Name { get ; set; }
public void ValidateFields()
{
//validation logic
}
}
public class SpecialRequest: IRequest
{
public string Desc { get; set; }
public int RequestType { get; set; }
public string Name { get; set; }
public void ValidateFields()
{
//validation logic
}
}
Factory to create the Request objects
public class RequestFactory
{
public static IRequest CreateRequest(string requestTypeStr)
{
switch (requestTypeStr)
{
case "0": return new SpecialRequest();
default: return new StandardRequest();
}
}
}
Class to handle the interactions of the IRequest object, aptly named RequestInteractions, I know a poor name choice!
This class is what validates and saves the requests.
public class RequestInteractions
{
private IRequest _requestObj;
private Repository _repository;
public RequestInteractions(IRequest requestObj, Repository repository)
{
_requestObj = requestObj;
_repository = repository;
}
public bool ValidateAndSave()
{
try
{
_requestObj.ValidateFields();
_repository.SaveRequest(_requestObj);
return true;
}
catch (Exception e)
{
throw;
}
}
}
Repository - like I said, this needs to be fleshed out. The IRequest (through the DTO) should be able to tell you how it needs to be persisted. You'll have to fill this in.
public class Repository
{
public void SaveRequest(IRequest requestObject)
{
//The respective DTO should help you figure out what to save based on the type of IRequest
}
}
Tying it all together
var repository = new Repository();
var requestObject = RequestFactory.CreateRequest("");
var requestInteractions = new RequestInteractions(requestObject,repository);
requestInteractions.ValidateAndSave();
Benefit of this approach - You need to create a new Request class (and
a DTO) when you get a new Request to add to the system, the rest of
the plumbing need not be touched.
Downside - Well, a lot of code compared to what you have.

Entity Framework 6 - Dependency Injection with Unity - Repository pattern - Add or Update exception for many to many relationship

I have a problem when adding new values with a many to many mapping in Entity Framework. I know about the unit of work pattern but in our solution we would like to keep a simple repository pattern and not a unit of work class that contains everything. Is this possible or should I just implement Unit of Work right away?
If I don't use iSupplierRepository below a supplier will be added, but it will always add a new one even though there already exists one with that name.
Error:
The relationship between the two objects cannot be defined because
they are attached to different ObjectContext objects.
Repository example:
public class SupplierRepository : IntEntityRepository<Supplier, DbContext>, ISupplierRepository
{
public SupplierRepository(DbContext context) : base(context, context.Suppliers)
{
}
}
Inherited repositories:
public class IntEntityRepository<TEntity, TContext> : EntityRepository<TEntity, TContext, int>
where TEntity : class, IEntity<int>
where TContext : BaseIdentityDbContext
{
public IntEntityRepository(TContext context, IDbSet<TEntity> set) : base(context, set)
{
}
public override async Task<TEntity> GetAsync(int id)
{
return (await GetAsync(entity => entity.Id == id)).SingleOrDefault();
}
...
public abstract class EntityRepository<TEntity, TContext, TId> : IEntityRepository<TEntity, TId>
where TEntity : class, IEntity<TId>
where TContext : BaseIdentityDbContext
{
protected TContext Context { get; }
protected IDbSet<TEntity> Set { get; }
protected EntityRepository(TContext context, IDbSet<TEntity> set)
{
Context = context;
Set = set;
}
public abstract Task<TEntity> GetAsync(TId id);
...
Unity:
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();
Controller:
private readonly IContactRepository iContactRepository;
private readonly ISupplierRepository iSupplierRepository;
public ContactsController(IContactRepository iContactRepository, ISupplierRepository iSupplierRepository)
{
this.iContactRepository = iContactRepository;
this.iSupplierRepository = iSupplierRepository;
}
[HttpPut]
[Route("UpdateContact/{id}")]
public async Task<IHttpActionResult> UpdateContact(ContactViewModel contactVm, int id)
{
try
{
var supplierList = new List<Supplier>();
foreach (var contactVmSupplier in contactVm.Suppliers)
{
var supplier = await iSupplierRepository.GetAsync(contactVmSupplier.Id);
supplierList.Add(supplier);
}
var contactOriginal = await iContactRepository.GetAsync(id);
var updatedContact = Mapper.Map<ContactViewModel, Contact>(contactVm, contactOriginal);
updatedContact.Suppliers = supplierList;
await iContactRepository.UpdateAsync(updatedContact);
return Ok();
}
catch (Exception e)
{
throw new Exception("Could not update a contact", e);
}
}
Viewmodels:
public class ContactViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<SupplierViewModel> Suppliers { get; set; }
}
public class SupplierViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Models:
public class Contact : IEntity<int>
{
public Contact()
{
Suppliers = new List<Supplier>();
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string Name { get; set; }
public ICollection<Supplier> Suppliers { get; set; }
}
public class Supplier: IEntity<int>
{
public Supplier()
{
Contacts = new List<Contact>();
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
If you install the Unity bootstrapper for ASP.NET Web API package, a UnityHierarchicalDependencyResolver is available which will use a new child container for each IHttpController resolution effectively making all registrations with a HierarchicalLifetimeManager resolved per request so that all repository instances in a controller will use the same DbContext.
The NuGet package will also install some bootstrapping code in App_Start which uses WebActivatorEx. You can either use this approach or change to align with what you are using right now. Based on your posted code it would look something like:
public static void ConfigureUnity(HttpConfiguration config)
{
var container = new UnityContainer();
container.RegisterType<DbContext>(new HierarchicalLifetimeManager());
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();
config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
}
Solved it like this, dependency injection is from the tutorial Dependency Injection in ASP.NET Web API 2.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection
App_Start -> WebApiConfig
public static void Register(HttpConfiguration config)
{
UnityConfig.ConfigureUnity(config);
...
UnityConfig:
public static void ConfigureUnity(HttpConfiguration config)
{
var context = new DbContext();
var container = new UnityContainer();
container.RegisterType<ISupplierRepository, SupplierRepository>(new InjectionConstructor(context));
container.RegisterType<IContactRepository, ContactRepository>(new InjectionConstructor(context));
config.DependencyResolver = new UnityResolver(container);
}
Update: Use Randy Levy's answer instead.
My recommendation here is not to use Repository or UoW at all. EF already has them implemented. You'll encounter a lot of issues trying to re-implement them.
As to specific issue you encounter with exception: you have to use the same DbContext for your entities. At the same time, you wouldn't like to use DbContext as Singleton and use it per-request instead. A possible solution for it might be found here.
Application_BeginRequest(...)
{
var childContainer = _container.CreateChildContainer();
HttpContext.Items["container"] = childContainer;
childContainer.RegisterType<ObjectContext, MyContext>
(new ContainerControlledLifetimeManager());
}
Application_EndRequest(...)
{
var container = HttpContext.Items["container"] as IUnityContainer
if(container != null)
container.Dispose();
}

Map Private Property

I have an Asp.Net project with Entity Framework 7 an i have a Email class with a list of attachments.
I don't want to leave that anyone add a item to my list, them i have
private List<Attachment> Resources { get; set; }
public IEnumerable<Attachment> Attachments { get; set; }
Now, I want to map to database the relationship with the property Resources instead Attachments.
Entity Framework 7 rise an Exception...
How i can do this.
Separate the this to two different model, one internal that maps to the database and another one that's available to users.
It's also the correct way of passing data between layers.
Hope it helps!
I agree with Itay.
Maybe this code example could help you.
Make entities that map to db tables.
public class EmailState
{
public int Id { get; private set; }
public List<AttachmentState> Resources { get; set; }
public static Email ToEmail(EmailState state)
{
return new Email(state);
}
}
public class AttachmentState
{
public static Attachment ToAttachment(AttachmentState state)
{
return new Attachment(state);
}
public Attachment ToAttachment()
{
return new Attachment(this);
}
}
Make classes that are available to users
public class Email
{
public Email()
{
this.State = new EmailState();
}
internal Email(EmailState state)
{
this.State = state;
}
internal EmailState State { get; set; }
public int Id { get; private set; }
public IEnumerable<Attachment> Attachments()
{
return this.State.Resources.Select(x => x.ToAttachment());
}
public void AddAttachment(Attachment attachment)
{
this.State.Resources.Add(attachment.State);
}
}
public class Attachment
{
public Attachment()
{
this.State = new AttachmentState();
}
internal Attachment(AttachmentState state)
{
this.State = state;
}
internal AttachmentState State { get; set; }
}
Define DbContext
public class EmailDbContext : DbContext
{
public DbSet<EmailState> Emails { get; set; }
public DbSet<AttachmentState> Attachments { get; set; }
}
Make repository
public interface IEmailRepository
{
void Add(Email email);
Email GetById(int emailId);
}
public class EmailRepository : IEmailRepository
{
private EmailDbContext _context;
public EmailRepository(EmailDbContext context)
{
_context = context;
}
public void Add(Email email)
{
_context.Emails.Add(email.State);
}
public Email GetById(int emailId)
{
EmailState emailState = _context.Emails.Single(x => x.Id == emailId);
return new Email(emailState);
}
}
Use it like this
using (var context = new EmailDbContext())
{
IEmailRepository repository = new EmailRepository(context);
var email = new Email();
repository.Add(email);
context.SaveChanges();
var emailFoundById = repository.GetById(email.Id);
}

How to use the same foreach code for 2 collections?

I have 2 collections of 2 different types but have almost the same set of fields.
in one function, I need to iterate through one of the collections depending on one condition.
I want to write only one code block that will cover both cases.
Example:
I have the following code:
if (condition1)
{
foreach(var type1var in Type1Collection)
{
// Do some code here
type1var.Notes = "note";
type1var.Price = 1;
}
}
else
{
foreach(var type2var in Type2Collection)
{
// the same code logic is used here
type2var.Notes = "note";
type2var.Price = 1;
}
}
Now: I want to simplify this code to use the same logic only once ( as they are identical ), something like the following ( P.S : I know the following code is not correct, I am just explaining what I want to do ):
var typecollection = Condition1 ? Type1Collection : Type2Collection;
foreach(var typevar in TypeCollection)
{
// the same code logic is used here
typevar.Notes = "note";
typevar.Price = 1;
}
The definition of Type1 & Type2 is similar to the following code ( Actually they are Entity objects):
public class Type1 : EntityObject
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public class Type2 : EntityObject
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
Update 1:
I have included some sample code I am using inside foreach block ( I am accessing a public properties of the 2 types).
Update 2:
I have included sample Type1 & Type2 definitions, as you can see I have 2 common Public Properties in both classes which I want to update in foreach block.
Update 3:
I am sorry for the confusion, Type1 & Type2 are derived from EntityObject ( They are both part of my entity model, and the Type1Collection & Type2Collection are actually EntityCollection of these 2 entities.
You could use dynamic. Note you will lose type safety.
var list1 = new List<bool>(){true,false};
var list2 = new List<int>(){1,2};
var typecollection = condition1 ? list1.Cast<dynamic>() : list2.Cast<dynamic>();
foreach (var value in typecollection)
{
//then you can call a method you know they both have
Debug.WriteLine(value.ToString());
}
Or if they share a common interface you can cast directly to that. You will maintain type safety
var list1 = new List<bool>(){true,false};
var list2 = new List<int>(){1,2};
var typecollection = condition1 ? list1.Cast<IConvertible>() : list2.Cast<IConvertible>();
foreach (IConvertible convertible in typecollection)
{
//we now know they have a common interface so we can call a common method
Debug.WriteLine(convertible.ToString());
}
Given Jon Skeet's hint of using LINQ's Concat method and the OP's statement that the classes involved are EntityObjects, here's another possible solution. This assumes that the EntityObject subclasses are defined as partial classes:
public partial class Type1 : EntityObject
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public partial class Type2 : EntityObject
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
This allows the OP to declare an interface with the common properties, and have his EntityObject subclasses implement that interface:
public interface IMyType
{
String Notes { get; set; }
decimal Price { get; set; }
}
public partial class Type1 : IMyType {}
public partial class Type2 : IMyType {}
And the original code becomes:
var query = (
from type1var in type1Collection
where condition1
select (IMyType)type1var
).Concat(
from type2var in type2Collection
where !condition1
select (IMyType)type2var
);
foreach(var myType in query)
{
myType.Notes = "note";
myType.Price = 1;
}
You could create a base type for type1 and type2 that groups the common properties between the two classes:
class MyBaseType {
// Common properties
}
class Type1 : MyBaseType {
// Specific properties
}
class Type2 : MyBaseType {
// Specific properties
}
Then, you could do something like this:
IEnumerable<MyBaseType> collection;
if(condition1)
collection = type1Collection;
else
collection = type2Collection;
foreach(MyBaseType element in collection) {
// Common logic
}
EDIT:
As Simon points out in the comments, you should use an interface instead of a base type if it's enough (i.e you don't need a specific implementation for both types).
This is not a very nice way to do it, but it would atleast work.
var type1Collection = new Collection<Type1>();
var type2Collection = new Collection<Type2>();
var condition1 = new Random().Next(0, 2) != 0;
dynamic selectedCollection;
if (condition1)
selectedCollection = type1Collection;
else
selectedCollection = type2Collection;
foreach (var typeVar in selectedCollection)
{
typeVar.Notes = "note";
typeVar.Price = 1;
}
I'm surprised nobody else has suggested an extension method yet:
public interface IMyType
{
String Notes { get; set; }
decimal Price { get; set; }
}
public static class MyTypeExtensions
{
public static void MyLogic(this IMyType myType)
{
// whatever other logic is needed
myType.Notes = "notes";
myType.Price = 1;
}
}
Now, your original types just need to implement IMyType:
public class Type1 : IMyType
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public class Type2 : IMyType
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
Then the original code becomes:
if (condition1)
{
foreach (var type1 in type1Collection)
{
type1.MyLogic();
}
}
else
{
foreach (var type2 in type2Collection)
{
type2.MyLogic();
}
}
You can do it with Predicate and Action stored in a Dictionary. I am suggesting Action here since the code snippet doesn't seems to return anything
public class IterationExample
{
private readonly Dictionary<bool, Action> dictionary;
public IterationExample()
{
dictionary = new Dictionary<bool, Action> { { true, CollectionOneIterator }, { false, CollectionTwoIterator } };
}
public void PublicMethod()
{
dictionary[condition]();
}
private void CollectionOneIterator()
{
foreach (var loopVariable in Type1Collection)
{
//Your code here
}
}
private void CollectionTwoIterator()
{
foreach (var loopVariable in Type2Collection)
{
//Your code here
}
}
}
With this way the readbility and testability of your code improves and also avoids long methods.
Edit:
public class Entity
{
public IList<string> Type1Collection { get; set; }
public IList<string> Type2Collection { get; set; }
}
public class ConsumingClass
{
public void Example()
{
var entity = new Entity();
entity.PublicMethod();
}
}
public static class IterationExample
{
private static readonly Dictionary<bool, Action<Entity>> dictionary;
static IterationExample()
{
dictionary = new Dictionary<bool, Action<Entity>> { { true, CollectionOneIterator }, { false, CollectionTwoIterator } };
}
public static void PublicMethod(this Entity entity)
{
dictionary[condition]();
}
private static void CollectionOneIterator(Entity entity)
{
foreach (var loopVariable in entity.Type1Collection)
{
//Your code here
}
}
private static void CollectionTwoIterator(Entity entity)
{
foreach (var loopVariable in entity.Type2Collection)
{
//Your code here
}
}
}

Categories

Resources