using a generic method when using interface - c#

Im stuck on a generics issue.
I have a method that returns DbSet from a DbContext object based on the object type.
Now I want to write I method where I pass in a object that implements a interface called IBaseData (that all entities implements). I want to use the existing method to return the correct DbSet based on the type of the IBaseData object, but I can't get it to work.
I've tried various versions of , typeof and gettype.
Existing method:
public GenericRepository<T> GetRepo<T>() where T : class
{
return new GenericRepository<T>(_context);
}
New method (this is just what Im trying to do):
public async Task SetDateLastInvoked<T>(IBaseData data, int id)
{
var tmp = GetRepo<typeof(data)>().Where(obj => obj.Id.Equals(id));
tmp.DateLastInvoked = DateTime.Now;
await SaveChangesAsync();
}

I assume you are doing something like:
public interface IBaseData
{
int Id { get; set; }
}
Then a method would looe like
public GenericRepository<T> GetRepo<T>()
where T : class
{
return new GenericRepository<T>(_context);
}
public async Task SetDateLastInvoked<T>(int id)
where T : class, IBaseData
{
var tmp = GetRepo<T>().FirstOrDefault(obj => obj.Id == id));
tmp.DateLastInvoked = DateTime.Now;
await SaveChangesAsync();
}
Usage:
public MyClass : IBaseData
{
public Id { get; set }
}
SetDateLastInvoked<MyClass>(1);

Ended up with this, based on previous answer:
public async Task SetLastInvoked<T>(int id) where T : class, IBaseData
{
var tmp = await GetRepo<T>().Single(obj => obj.Id.Equals(id));
tmp.DateLastInvoked = DateTime.Now;
await SaveChangesAsync();
}
I also created another method based on the same pattern:
public async Task DeleteItem<T>(int id, bool removeRow) where T : class, IBaseData
{
if (removeRow)
{
GetRepo<T>().Delete(await GetRepo<T>().Single(obj => obj.Id.Equals(id)));
}
else
{
var tmp = await GetRepo<T>().Single(obj => obj.Id.Equals(id));
tmp.Active = false;
}
await SaveChangesAsync();
}

Related

How to execute a default method before the interface implementation code C#?

I have an interface which implements only one method:
public interface IHandler<T> where T : Comand
{
Task<IResultComand> HandlerAsync(T comand);
}
And I use it in my classes as follows:
public class ProductHandler : IHandler<NewProductComand>,
IHandler<EditProductComand>,
IHandler<DeleteProductComand>
public async Task<IResultComand> HandlerAsync(NewProductComand comand)
{
ResultComand result = new();
comand.Validate();
if (!comand.Valid)
{
return result;
}
//Create new product
return result;
}
public async Task<IResultComand> HandlerAsync(EditProductComand comand)
{
ResultComand result = new();
comand.Validate();
if (!comand.Valid)
{
return result;
}
//Edit product
return result;
}
public async Task<IResultComand> HandlerAsync(DeleteProductComand comand)
{
ResultComand result = new();
comand.Validate();
if (!comand.Valid)
{
return result;
}
//Delete product
return result;
}
And my Comand has the following code:
public abstract class Comand
{
public bool Valid { get; set; }
public abstract void Validate();
}
How to make the following code run implicitly before executing the implementation code in HandlerAsync? (Some thing like a ActionFilter Web API .NET)
ResultComand result = new();
comand.Validate();
if (!comand.Valid)
{
return result;
}
You're looking for the Decorator Pattern.
Basically, you leave your 'base' CommandHandler as it is and let it do its job, but you add an additional class that implements the same interface:
ExtraStuffCommandHandlerDecorator<TComand> : IHandler<TComand>
where TComand : Comand
{
readonly IHandler<TComand> _decorated;
public ExtraStuffCommandHandlerDecorator(IHandler<TComand> decorated)
{
_decorated = decorated;
}
public async Task<IResultComand> HandlerAsync(TComand comand)
{
// Do stuff before base command execution
IResultComand result = await _decorated.HandlerAsync(comand);
// Do stuff after base command execution
return result;
}
}
Now your mediator that is currently instantiating and calling the base handler directly, should instead look up the decorator and call that instead, passing the base handler in as a constructor argument.
Using decorators is well supported by Autofac's RegisterGenericDecorator method. Implementation with Asp Core DI is a little more complex, but here's a guide.
https://andrewlock.net/adding-decorated-classes-to-the-asp.net-core-di-container-using-scrutor/#manually-creating-decorators-with-the-asp-net-core-di-container

Reflection, set.GetValue(context, null) always return null

I'm trying to get a property from an object by reflection.
public class CosmosDbSet<TEntity> : DbSet<TEntity> where TEntity : class, IEntity<string>
{
public string Name { get; }
//...
);
}
public class SKCosmosDbContext : CosmosDBContext
{
public CosmosDbSet<Item> Items { get; }
public SKCosmosDbContext ()
{
Items = new CosmosDbSet<Item>(
this,
"Items"
);
}
//...
}
public abstract class CosmosDBContext : DbContext
{
public async Task EnsureContainersExistAsync()
{
var sets = GetType().GetProperties()
.Where(pi => pi.PropertyType.IsGenericType
&& pi.PropertyType.GetGenericTypeDefinition().Equals(typeof(CosmosDbSet<>))
);
foreach (var set in sets)
{
var value = set.GetValue(this, null); // => value is always null
//...
}
}
}
public static class DbInitializer
{
public async static Task InitializeAsync(IServiceProvider services, ILogger logger)
{
var dbContext = services.GetRequiredService<SKCosmosDbContext>();
await dbContext.EnsureContainersExistAsync();
}
}
As you can see, the property Items from SKCosmosDbContext has been found but, I can't have access to it.
How to have access to the property using reflection?
So basically I see problem in using .GetGenericTypeDefinition() call.
If you do more detailed debug you could see that it returns enumerable with next content:
To get what you want you could use pi.PropertyType.GetGenericArguments()[0] and than use its return value to equal it in your linq query.
ex.
I used dummy types just for sake of example
Your problem could be related also with this one: Get type of generic list
TL;DR Example works after changing query to:
var sets = db.GetType().GetProperties()
.Where(pi => pi.PropertyType.IsGenericType
&& pi.PropertyType.GetGenericArguments()[0].Equals(typeof(...))
);

Async ObservableCollection from Interface in C# (Xamarin)

I've got an ObservableCollection in an interface and I'm using it with a RestApi-Request. It has some Await functions and it must be async. But there's an error.
Here's the interface:
public interface IDbConnection
{
ObservableCollection<Einkauf> GetEinkauf();
}
Here's the part from the class it is using:
public partial class RestView : IDbConnection
{
private ObservableCollection<Einkauf> _einkauf;
private const string Url = "http://localhost:3003/einkauf";
private HttpClient _client = new HttpClient();
public RestView ()
{
InitializeComponent ();
}
public async ObservableCollection<Einkauf> GetEinkauf()
{
var content = await _client.GetStringAsync(Url);
var einkauf = JsonConvert.DeserializeObject<List<Einkauf>>(content);
_einkauf = new ObservableCollection<Einkauf>(einkauf);
speisenListe.ItemsSource = _einkauf;
return _einkauf;
}
}
The GetEinkauf is underlined and it says:
CS1983 C# The return type of an async method must be void, Task or Task<T>
Does anybody know how to fix this?
public interface IDbConnection
{
Task<ObservableCollection<Einkauf>> GetEinkauf();
}
public async Task<ObservableCollection<Einkauf>> GetEinkauf()
{
...
}
If GetEinkauf is supposed to be implemented as an asynchronous method, you should change it return type to Task<ObservableCollection<Einkauf>> and also change its name to GetEinkaufAsync to follow the naming convention for asynchronous methods:
public interface IDbConnection
{
Task<ObservableCollection<Einkauf>> GetEinkaufAsync();
}
public async Task<ObservableCollection<Einkauf>> GetEinkaufAsync()
{
var content = await _client.GetStringAsync(Url);
var einkauf = JsonConvert.DeserializeObject<List<Einkauf>>(content);
_einkauf = new ObservableCollection<Einkauf>(einkauf);
speisenListe.ItemsSource = _einkauf;
return _einkauf;
}
You could then await the method from any method marked as async:
var collection = await GetEinkaufAsync();
If another class implements the IDbConnection interface in a synchronous fashion for some reason, it could use the Task.FromResult method to return a Task<ObservableCollection<Einkauf>>:
public class SomeOtherClass : IDbConnection
{
public Task<ObservableCollection<Einkauf>> GetEinkaufAsync()
{
return Task.FromResult(new ObservableCollection<Einkauf>());
}
}

Simple Injector with Generic interfaces and composition - Not registering interfaces

I am moving from inheritance to compositon, as you can see from here https://stackoverflow.com/questions/29653153/composition-migrating-from-inheritance
Now i have got it all working, but simple injector wants me to manually register each interface with each type being passed in. Here is the striped down code.
I have IBaseEntityService, which BaseEntityService implements, like so
public interface IEntityBaseService<T> where T : class, IEntityBase
{
IDataContext Context { get; }
Task<ICollection<T>> GetAllAsync();
Task<T> GetAsync(long id);
}
public class EntityBaseService<T> : IEntityBaseService<T>
where T : class, IEntityBase
{
protected IDataContext _context;
public IDataContext Context
{
get
{
return _context;
}
}
public EntityBaseService(IDataContext context)
{
_context = context;
}
public async Task<ICollection<T>> GetAllAsync()
{
return await _context.Set<T>().ToListAsync();
}
public Task<T> GetAsync(long id)
{
return _context.Set<T>().Where(e => e.Id == id).FirstOrDefaultAsync();
}
}
Now i have a IValidationService and ValidationService implements that, like so
public interface IValidationService<T>
where T : class, IEntityBase
{
Task<ValidationResult> ValidateAsync(T entity);
Task<int> AddAsync(T entity);
Task<int> UpdateAsync(T entity);
}
public class ValidationService<T> : IValidationService<T>
where T : class, IEntityBase
{
private IEntityBaseService<T> _service;
private IValidator<T> _validator = null;
public IDataContext Context
{
get
{
return _service.Context;
}
}
public ValidationService(IEntityBaseService<T> service, IValidator<T> validator)
{
_service = service;
_validator = validator;
}
public Task<ValidationResult> ValidateAsync(T entity)
{
if (_validator == null) throw new MissingFieldException("Validator does not exist for class " + entity.GetType().ToString() + ". override method if no validation needed");
return _validator.ValidateAsync(entity);
}
public async Task<int> AddAsync(T entity)
{
var results = await ValidateAsync(entity);
if (!results.IsValid)
{
throw new ValidationException(results.Errors);
}
return await _service.AddAsync(entity);
}
public async Task<int> UpdateAsync(T entity)
{
var results = await ValidateAsync(entity);
if (!results.IsValid)
{
throw new ValidationException(results.Errors);
}
return await _service.UpdateAsync(entity);
}
}
Now i can use services, and pass in each interface. So i have a IContentService and ContentService which implements that. This service uses both IEntityBaseService and IValidationService
public interface IContentService
{
Task<Content> GetAsync(long id);
Task<int> AddAsync(Content entity);
Task<int> UpdateAsync(Content entity);
}
public class ContentService : IContentService
{
private IEntityBaseService<Content> _service;
private IValidationService<Content> _validation;
public ContentService(IEntityBaseService<Content> service, IValidationService<Content> validation)
{
_service = service;
_validation = validation;
}
public async Task<Content> GetAsync(long id)
{
var content = await _service.Context.Contents
.Where(e => e.Id == id)
.WhereNotDeleted()
.FirstOrDefaultAsync();
if (content != null)
{
content.Attachments = await _service.Context.Attachments
.Where(e => e.ContentId == id)
.WhereNotDeleted()
.ToListAsync();
}
return content;
}
public Task<int> AddAsync(Content entity)
{
return _validation.AddAsync(entity);
}
public Task<int> UpdateAsync(Content entity)
{
return _validation.UpdateAsync(entity);
}
}
Now in my config file, i have this, which works, but if i have another service, and i have about 20 of them, i do not want to type this out for each one.
container.Register<IEntityBaseService<Content>, EntityBaseService<Content>>();
container.Register<IValidationService<Content>, ValidationService<Content>>();
container.Register<IValidator<Content>, ContentValidator>();
container.Register<IContentService, ContentService>();
So i looked online and i think i can use the RegisterManyForOpenGeneric method instead of registering IEntityBaseService and IValidatioNService, but i cannot get it too work. I have wrote
container.RegisterManyForOpenGeneric(typeof(IEntityBaseService<>), typeof(IEntityBaseService<>).Assembly);
container.RegisterManyForOpenGeneric(typeof(IValidationService<>), typeof(IValidationService<>).Assembly);
And i get an error saying
The constructor of type ContentService contains the parameter of type
IEntityBaseService with name 'service' that is not
registered. Please ensure IEntityBaseService is registered in
the container, or change the constructor of ContentService.
Maybe i have set up my entities incorrectly, so just encase, here are the basic entities for this example
public interface IEntityBase
{
long Id { get; set; }
}
public abstract class EntityBase : IEntityBase
{
public long Id { get; set; }
}
public class Content : EntityBase, IAudit
{
public short Position { get; set; }
public Content()
{
Position = 1;
}
}
Any help much appreciated on both simple injector and the code in general
You're using the wrong registration method.
RegisterManyForOpenGeneric exists to support the following scenario:-
container.Register<IValidate<Customer>, CustomerValidator>();
container.Register<IValidate<Employee>, EmployeeValidator>();
container.Register<IValidate<Order>, OrderValidator>();
container.Register<IValidate<Product>, ProductValidator>();
// can replace the above with the following:-
container.RegisterManyForOpenGeneric(
typeof(IValidate<>),
typeof(IValidate<>).Assembly);
By default RegisterManyForOpenGeneric searches the supplied assembly for all types that implement the [specified open generic] interface and registers each type by their specific (closed generic) interface. [Emphasis mine]
Note how the concrete types aren't themselves generic (i.e. CustomerValidator implements the closed generic interface IValidate<Customer>). This works fine for your ContentValidator but is no good for open generic classes implementing open generic interfaces like your EntityBaseService<Content> and ValidationService<Content>.
You're looking for:-
container.RegisterOpenGeneric(
typeof(IEntityBaseService<>),
typeof(EntityBaseService<>));
RegisterManyForOpenGeneric has been marked obsolete now.
container.Register(typeof(IValidator<>), new[] { typeof(IValidator<>).Assembly });
Is the way to go now.
http://simpleinjector.readthedocs.org/en/latest/advanced.html

Cannot return IObservable<T> from a method marked async

This contrived example is roughly how my code is structured:
public abstract class SuperHeroBase
{
protected SuperHeroBase() { }
public async Task<CrimeFightingResult> FightCrimeAsync()
{
var result = new CrimeFightingResult();
result.State = CrimeFightingStates.Fighting;
try
{
await FightCrimeOverride(results);
}
catch
{
SetError(results);
}
if (result.State == CrimeFightingStates.Fighting)
result.State = CrimeFightingStates.GoodGuyWon;
return result;
}
protected SetError(CrimeFightingResult results)
{
result.State = CrimeFightingStates.BadGuyWon;
}
protected abstract Task FightCrimeOverride(CrimeFightingResult results);
}
public enum CrimeFightingStates
{
NotStarted,
Fighting,
GoodGuyWon, // success state
BadGuyWon // error state
}
public class CrimeFightingResult
{
internal class CrimeFightingResult() { }
public CrimeFightingStates State { get; internal set; }
}
Now I'm trying to build a collection that would hold multiple SuperHero objects and offer a AllHerosFightCrime method. The hero's should not all fight at once (the next one starts when the first is finished).
public class SuperHeroCollection : ObservableCollection<SuperHeroBase>
{
public SuperHeroCollection() { }
// I mark the method async...
public async IObservable<CrimeFightingResult> AllHerosFightCrime()
{
var heros = new List<SuperHeroBase>(this);
var results = new ReplaySubject<CrimeFightingResult>();
foreach (var hero in heros)
{
// ... so I can await on FightCrimeAsync and push
// the result to the subject when done
var result = await hero.FightCrimeAsync();
results.OnNext(result);
}
results.OnCompleted();
// I can't return the IObservable here because the method is marked Async.
// It expects a return type of CrimeFightingResult
return results;
}
}
How can I return the IObservable<CrimeFightingResults> and still have the call to FightCrimeAsync awaited?
You could turn your task into an observable and combine them using Merge:
public IObservable<CrimeFightingResult> AllHerosFightCrime()
{
var heros = new List<SuperHeroBase>(this);
return heros.Select(h => h.FightCrimeAsync().ToObservable())
.Merge();
}
If you want to maintain the order your events are received you can use Concat instead of Merge.

Categories

Resources