I have an entity Order with a Status property. Status will be Enum’s like New, Processed, Disable..etc. I have set of Actions like, DeleteOrder, DuplicateOrder..etc.
Actions will be available based on status. I need to get list of Actions when I pass Instance of Order to a method. How can I achieve this. I have following pattern.
public class Order (entity)
{
public int Id { get; set; }
public OrderStatus Status { get; set; }
}
public enum OrderStatus
{
New,
Processed,
Disable
}
public class OrderActionModel
{
public int Id { get; set; }
public OrderAction Action { get; set; }
public string Comments { get; set; }
}
public enum OrderAction
{
DeleteOrder,
DuplicateOrder
}
public interface IAction
{
void Process();
}
public interface IActionFactory
{
IAction GetAction(OrderActionModel model);
List<string> GetActions(Order order);
}
public class ActionFactory : IActionFactory
{
private readonly IMailService _mailService;
public ActionFactory(IMailService mailService)
{
_mailService = mailService;
}
public IAction GetAction(OrderActionModel model)
{
switch (model.Action)
{
case DeleteOrder:
return new DeleteOrderAction(model, _mailService);
case DuplicateOrder:
return new DuplicateOrderAction(model, _mailService);
default:
return null;
}
}
public List<string> GetActions(Order order)
{
//How to call isValidAction from here for each list of actions
}
}
public class DeleteOrderAction : IAction
{
private OrderActionModel _model;
private readonly IMailService _mailService;
public DeleteOrderAction(OrderActionModel model, IMailService mailService)
{
_model = model;
_repository = repository;
}
public void Process()
{
AddAudit(_model.comments);
SendEMail();
}
public bool IsValidAction(Order order)
{
return order.Status == OrderStatus.New;
}
}
public IHttpActionResult Action(OrderActionModel model)
{
IAction action = _actionFactory.GetAction(model);
action.Process();
return Ok();
}
public IHttpActionResult GetActions(1)
{
//get order by id
var order = repo.getOrder(1)
//Some method to get Actions
var actions = SampleMethod(order)
return Ok(actions);
}
So if you want to stick with this model, the simplest way to achieve what you want would be
Dictionary<string, Func<Order, bool>> actionNameToViableCheck;
You put your actions along with their related validation functions during app initialization, so
actionNameToViableCheck.Add("Delete Order", o => DeleteOrderAction.IsValidOrder(o));
This requires the IsValidOrder method to be static.
Then your GetActions could look something like this:
public List<string> GetActions(Order order) =>
actionNameToViableCheck.Where(kv => kv.Value(order)).Select(kv => kv.Key);
Note that this is not the most effective solution if there are many different actions, because you process all of them every time. But since you define validity of an Action by its IsValidOrder method output, I don't see a much better simple way.
Related
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;
}
}
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.
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() }
};
}
}
I'm creating a static class with static methods for helping the controllers to do their job. When build the application I get the following error:
Error 40 'System.Web.Mvc.Controller.Content(string)' is inaccessible due to its protection level"
Any idea how to solve this problem?
Notes:
It's a c# mvc aplication
public static ActionResult GetAlbumJSON(AlbumVO album)
{
return Controller.Content(
JsonConvert.SerializeObject(new
{
max_car = #ABookClient.maxCharsProjecName,
trans_img = #ABookClient.Transparent_Image,
show_description = #ABookClient.Show_Product_Description,
product_type = "Album",
obj = CreateObjAlbumVO(album),
})
);
}
Content method is protected internal, so you can't use it outside of controller.
Controller.Content Method. Most probably your static class violates SRP principle. Let him do his job (initializing, serializing,...) and controller - controller's job - return result to the client.
protected internal ContentResult Content(string content)
It would look smth like:
public static class MyHelper
{
public static object GetAlbum(AlbumVO album)
{
return new
{
max_car = #ABookClient.maxCharsProjecName,
trans_img = #ABookClient.Transparent_Image,
show_description = #ABookClient.Show_Product_Description,
product_type = "Album",
obj = CreateObjAlbumVO(album),
};
}
}
public class AlbumController : Controller
{
public ActionResult GetAlbums(int id)
{
var album = Context.GetAlbum(id);
var convertedResult = MyHelper.GetAlbum(album);
return Json(convertedResult);
}
}
Also I'd advice to take a look at AutoMapper for creating client response objects
I think this is valid case for a view-model for a JSON result since you do want a separation between the Domain model and the data sent back to the client. Using a view model also gives you a proper place to put this mapping between the domain model and the view (the JSON) so you don't need to delegate to a helper class.
public class AlbumModel
{
[JsonProperty(PropertyName = "max_car")]
public int MaxChars { get; private set; }
[JsonProperty(PropertyName = "trans_img")]
public string TransparentImage { get; private set; }
[JsonProperty(PropertyName = "product_type")]
public string ProductType { get; private set; }
[JsonProperty(PropertyName = "obj")]
public AlbumInfo Object { get; private set; }
[JsonProperty(PropertyName = "show_description")]
public bool ShowProductDescription { get; private set; }
public AlbumModel(AlbumVO album)
{
MaxChars = album.maxCharsProjecName;
TransparentImage = album.Transparent_Image;
ShowProductDescription = album.Show_Product_Description;
ProductType = "Album";
Object = new AlbumInfo(album);
}
}
The AlbumInfo class provides additional mappings for your JSON result, which becomes the "obj" property sent back to the client.
public class AlbumInfo
{
// ... define properties here
public AlbumInfo(AlbumVO album)
{
// ... map properties here
}
}
And your controller becomes nice and clean:
public class AlbumController : Conrtoller
{
public ActionResult GetAlbums(int id)
{
var album = Context.GetAlbum(id);
var model = new AlbumModel(album);
return Json(model);
}
}
How can i expose the return of my method in to a class property?
public class BIContactLib: ContactLib
{
//Expose it here.. to replace the code below
public IEnumerable<BIContactLib> GetContactLibs
{
get { return (BIContactLib) GetAll}
set { ; }
}
}
public class BIContactLibService : IBIRequirement, IDisposable
{
private ClientContext context = new ClientContext();
//The return of the method here is the one I would like to expose
public IEnumerable<BIContactLib> GetAll()
{
var contactslib = context.ContactLibs;
return contactslib.ToList();
}
}
The reason behind this, is I want to create a view model with have the list of contacts library... heres my view model by the way..
public class PersonInformation
{
public BIPerson Person { get; set; }
public BIAddress Address { get; set; }
//This is where i want to use it
public IEnumerable<BIContactLib> GetAll { get; set; }
}
Or any other way to do this?
Best regards,
How about something like this
public IEnumerable<BIContactLib> GetContactLibs
{
get {
BiContractLib lib = new BiContractLib();
return lib.GetAll();
}
}