Reputation: 1794
For work, we have specific types of records that come in, but each project has its own implementation. The columns and the like are different, but in general the process is the same (records are broken into batches, batches are assigned, batches are completed, batches are returned, batches are sent out, etc.). Many of the columns are common, too, but sometimes there are name changes (BatchId in one vs Id in another. [Column("name")] takes care of this issue).
Currently this is what I have for the implementation of the batch assignment functionality with the common components given in the interface:
public interface IAssignment
{
// properties
...
// methods
T GetAssignmentRecord<T>(int UserId, int BatchId) where T : IAssignment;
List<T> GetAssignmentRecords<T>(int UserId) where T : IAssignment;
}
Now I currently have two projects that have batch assignment. Due to these being done in EntityFramework, Assignment in Namespace1 and Assignment in Namespace2 are completely different things but are bound by certain common components (an ID, an assigned user, checked in, etc.) which drive all of the methods for returning them.
I think my main question is if I'm doing this incorrectly and if there is a better way to achieve this such that I can pipe data into my Controllers and have the controllers look somewhat similar project to project while having as much of the method work being handled automatically (primarily so that a "fix one, fix all" scenario occurs when I need to do updates).
Here's an example of how I'm doing the implementation for namespace1:
public class Assignment
{
...
public T GetAssignmentRecord<T>(int UserId, int BatchId) where T : IAssignment
{
var db = new Database1Context();
return (T) Convert.ChangeType(db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned).First(), typeof(T));
}
}
In the Controller:
Assignment assignment = new Assignment();
var record = assignment.GetAssignmentRecord<Assignment>(userid, batchid);
// do stuff
The controller code is actually how I'm assuming it would work. I've completed through the Assignment class and now I'm perplexed if I'm doing it the proper way. The reason I feel this may be incorrect is I'm basically saying "The interface is looking for a generic, I'm getting a strong typed object from the database using entity framework, I'm casting it to a generic, and when I'm making the request, I'm asking for the same strong typed object that I converted to generic initially."
Is there a better way of doing this? Or a completely different direction I should be going?
Upvotes: 2
Views: 2381
Reputation: 5101
Think about an adapter layer. That layer should transform the incoming data to a common structure/class and then can be handled consistently, generics notwithstanding. Of course it also re-transforms on the "outbound" side to that expected by the particular databases. This assumes that no datasource has data that is undefined in the others, or that you can define valid default values for said missing data.
I imagine you need different adapters for the different projects. Perhaps this is a job for dependency injection. Basically at runtime you fetch the particular code (adapter class) needed.
Upvotes: 1
Reputation: 4643
Assuming that the 2 Assignment has different properties (maybe some additional), but some of the property is same, and they are from different database, there are many ways to doing it. But "the best" (for me) is by doing dependency injection.
Your activities (methods) in Assignment class, should be moved to a separated "service" class. This increases the modularity of Assignment, as it only became a POCO.
For data access, create a separated class (repository) to retrieve/insert/update/delete your data. Example will be like:
public AssignmentRepository: IAssignmentRepository{
public Assignment GetAssignmentRecord(int userId, int batchId){
}
}
public BatchAssignmentRepository: IAssignmentRepository{
public Assignment GetAssignmentRecord(int userId, int batchId){
}
}
If you ask why there are 2 repository instead of 1, will it make the code redundant? Yes it is, but you also must consider it will increase the modularity. If you change something in BatchAssignment (maybe change the column name, add additional column, etc) then you do not need to apply the same in Assignment, and avoiding you of "if batchAssignment else" logic inside.
The use from the caller will be like this:
IAssignmentService service = new AssignmentService();
IAssignmentRepository repository = new AssignmentRepository();
Assignment a = repository.GetAssignmentRecord(userId, batchId);
service.DoSomething(a);
Upvotes: 1
Reputation: 14302
Providing I understood correctly what your goal is, I'd do it e.g. this way...
interface IAssignment
{
}
interface IRepo<out T> where T : IAssignment
{
T GetAssignmentRecord(int UserId, int BatchId);
IEnumerable<T> GetAssignmentRecords(int UserId);
}
class AssignmentRecord : IAssignment
{
}
class AssignmentWeb : IAssignment
{
}
class RepoDb : IRepo<AssignmentRecord>
{
public AssignmentRecord GetAssignmentRecord(int UserId, int BatchId)
{
//using(var db = new MyDbContext())
//{
// return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned).First();
//}
return new AssignmentRecord();
}
public IEnumerable<AssignmentRecord> GetAssignmentRecords(int UserId)
{
//using(var db = new MyDbContext())
//{
// return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned);
//}
return new List<AssignmentRecord>
{
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
};
}
}
class RepoWeb : IRepo<AssignmentWeb>
{
public AssignmentWeb GetAssignmentRecord(int UserId, int BatchId)
{
// fetch it from some web service...
return new AssignmentWeb();
}
public IEnumerable<AssignmentWeb> GetAssignmentRecords(int UserId)
{
//using(var db = new MyDbContext())
//{
// return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned);
//}
return new List<AssignmentWeb>
{
new AssignmentWeb(),
new AssignmentWeb(),
new AssignmentWeb(),
};
}
}
class MYController
{
public IRepo<IAssignment> Repository { get; set; } // you can inject this e.g. DI
public IAssignment GetAssignment(int userid, int batchid)
{
return Repository.GetAssignmentRecord(userid, batchid);
}
public IEnumerable<IAssignment> GetAllAssignments(int userid)
{
return Repository.GetAssignmentRecords(userid);
}
}
class ProgramAssignment
{
static void Main(string[] args)
{
try
{
var controller = new MYController();
controller.Repository = new RepoDb();
IAssignment assignment = controller.GetAssignment(0, 0);
IEnumerable<IAssignment> all = controller.GetAllAssignments(0);
controller.Repository = new RepoWeb();
assignment = controller.GetAssignment(0, 0);
all = controller.GetAllAssignments(0);
}
catch
{
Console.WriteLine("");
}
}
}
As to why the out
- here is some more in my other post...
How to make generic class that contains a Set of only its own type or subtypes as Children?
Upvotes: 3