MongoDB – ViewModel
Po pierwszych testach projektu zauważyłem, że wywołania REST API zwracają za dużo danych. Było to spowodowane zwracaniem modelu domenowego, który nie jest odpowiedni do tego typu operacji. Dlatego częstym zabiegiem jest wprowadzenie tzw. view model, który odpowiada danym zwracanym do interfejsu użytkownika czy wywołań API. Zazwyczaj jest on „szczuplejszy” i prostszy od domenowego.
W tym poście chciałbym pokazać rozwiązanie oparte o bazę danych MongoDB i metody generyczne.
Dogevents – domain model |
IEvent – główny model domenowy. Ten interfejs zawiera wszystkie informacje o wydarzeniu. Jest złożoną strukturą, która odzwierciedla wydarzenia pobrane z Facebook przy użyciu Graph Api.
public class Event
{
[BsonId]
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[JsonProperty("start_time")]
public DateTime StartTime { get; set; }
[JsonProperty("end_time")]
public DateTime EndTime { get; set; }
[BsonIgnore]
public string Url { get => $"https://www.facebook.com/events/{Id}/"; }
public string CoverUrl { get => Cover.source; }
[JsonProperty("is_canceled")]
public bool IsCanceled { get; set; }
public Cover Cover { get; set; }
public Owner Owner { get; set; }
public Place Place { get; set; }
public IEnumerable<string> Categories { get; set; }
}
IViewEventModel – Podstawowy interfejs na potrzeby widoków. Stworzony do oznaczenia metod generycznych – tak aby móc wykorzystywać w nich pewne właściwości niezbędne do zapytań bazodanowych.
public interface IViewEventModel
{
long Id { get; set; }
string Name { get; set; }
DateTime StartTime { get; set; }
}
View service
public interface IViewEventsService
{
Task<List<T>> GetPopular<T>() where T : IViewEventModel;
Task<List<T>> GetIncoming<T>() where T : IViewEventModel;
Task<List<T>> GetJustAdded<T>() where T : IViewEventModel;
}
View models
public class EventCardHeaderViewModel : IViewEventModel
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime StartTime { get; set; }
public string Url { get => $"https://www.facebook.com/events/{Id}/"; }
}
Przykład użycia
[HttpGet]
[Route("GetPopular")]
public async Task<IEnumerable<EventCardViewModel>> GetPopular()
{
return await _viewEventService.GetPopular<EventCardViewModel>();
}
Powyżej przykład wywołania metody serwisowej ze wskazaniem na jaki model mają zostać zmapowane dane. Jest tylko jedno ale. Nie została wykonana pewna konfiguracja czy też zastosowana konwencja jak ma się zachowywać MongoDB podczas deserializacji danych na nasz model. Domyślnie, jeśli zabraknie definicji któregoś z pól otrzymamy wyjątek typu FormatException:
MongoDB konwencje
private static void RegisterConventions()
{
ConventionRegistry.Register("DogeventsConvetion", new MongoConvention(), x => true);
}
private class MongoConvention : IConventionPack
{
public IEnumerable<IConvention> Conventions => new List<IConvention>
{
new IgnoreExtraElementsConvention(true),
};
}