Reputation: 603
I have an interface and existing code which implements this interface:
public interface IBusinessService<T> where T : class
{
Task Add(T category);
Task Delete(T category);
Task Update(T category);
Task<IEnumerable<T>> GetAll();
Task<T> GetById(int id);
}
Now some id
has type of Guid
. So I cannot use this method to send Guid
id:
var id = Guid.NewGuid()
var result = GetById(id);
So we need that id
parameter can be type of Guid
. It would be ideal if it is possible:
public interface IBusinessService<T> where T : class
{
/* ... the other code is omitted for the brevity */
Task<T> GetById(Guid or int id );
}
What I thought to implement is to create a new method with parameter type of Guid
:
public interface IBusinessService<T> where T : class
{
Task Add(T category);
Task Delete(T category);
Task Update(T category);
Task<IEnumerable<T>> GetAll();
Task<T> GetById(int id);
Task<T> GetById(Guid id);
}
But if I will do this, then I need to edit so many code. So it looks like it is not a good solution.
Is there a way to add another type of id
to the interface method GetById
without breaking changes?
Upvotes: 1
Views: 135
Reputation: 19394
I really don't see the problem as big as you describe -- "But if I will do this, then I need to edit so many code"
How many implementations do you have. 20? Key here, implementation vs usages. Usages can be many or very many.
Looks like your goal is to keep usage intact as
var id = Guid.NewGuid();
var result = someInst.GetById(id);
// or
var id = 5;
var result = someInst.GetById(id);
No problem, you need minimal changes. This happens all the time
public interface IBusinessService<T> where T : class
{
Task<T> GetById(object id); // NOTE - change to 'object'
}
Now, you just need to change only few of your implementations that you have to distinct Guid
from int
public Task<T> GetById(object id)
{
if (id is int i)
return ProcessIdAsInt<T>(i);
else if (id is Guid g)
return ProcessIdAsGuid<T>(g);
else
throw new InvalidOperationException("Supplied data type not supported");
}
So, the changes are minimal. Usages will remain intact. Nothing unusual.
Upvotes: 1
Reputation: 29028
This seems to be a design problem (interface/API design) in the first place. It should be clear from the beginning whether IDs are represented by a Guid
or a raw int
value. But not both. Usually you try to use a protocol to control the client-server communication or at least by defining a server API. This protocol would define what values the API expects and the client must take care to input them in the correct format.
Consider to stay with the int
ID an use a custom conversion from Guid
to int
where required. Alternatively introduce a table (Dictionary
) where you store pairs of Guid
and int
values. So for each Guid
that enters your application you internally generate a integer which will replace the Guid
. But there must be some constraints or algorithm to guarantee the uniqueness of the IDs especially when using int
values. But I assume when you are using int
for unique IDs you already have an algorithm to generate those IDs.
Adding members to an interface is always a breaking change.
Since you are not using C# 8 you can't make use of the new Default Interface Method feature.
So, if you can accept breaking changes then you could refactor the interface to take one more generic parameter for the id
parameter.
You could also change the type of the id
parameter from int
to object
. But this would also introduce the costs of boxing/unboxing (in order to convert value types like int
to object
). I personally think using either object
or string
would be the best. I don't know your web interface but string
could be the preferred ID type.
So the best solution, in case you can't accept those breaking changes, would be to implement a new interface e.g., IGuidIdBusinessService<T>
:
public interface IGuidIdBusinessService<T> : IBusinessService<T> where T : class
{
Task<T> GetByGuid(Guid id);
}
Implementing the interfaces:
public class MyClass : IGuidIdBusinessService<object>
{
#region Implementation of IBusinessService<object>
...
// Either throw NotSupportedException or implement conversion
public async Task<object> GetById(int id)
{
Guid guid = ConvertIntToGuid(id);
return GetByGuid(guid);
}
#endregion
#region Implementation of IGuidIdBusinessService<object>
public async Task<object> GetByGuid(Guid id) => throw new NotImplementedException();
#endregion
}
Upvotes: 1