GraphQL教程(四) .net core api
今天學個比較難的——訂閱
訂閱功能:執行後,開啟兩個瀏覽器,同樣在GraphQL介面上操作。其中一個開啟監聽,另一個建立,用MovicRating事件將兩者關聯,建立成功後,監聽的介面也將顯示相關訊息。
總共有五個步驟
1.在Movie資料夾建立事件屬性MovieEvent,型別MovieEventType
2.在Service資料夾建立介面IMovieEventService與實現介面MovieEventService類
3.修改MovieSerivce類的建立程式,在其中新增事件
4.Schema資料夾實現訂閱方法MovieSubscription類
5.將MovieSubscription新增到MovieSchema類,併到startup類註冊相關服務
1.在Movie資料夾建立事件屬性MovieEvent,型別MovieEventType
MovieEvent類
using System; namespace GraphStudy.Movies.Movies { public class MovieEvent { public MovieEvent() { //初始化系統的一個新例項。Guid結構。 Id = Guid.NewGuid(); } public Guid Id { get; set; } public int MovieId { get; set; } public string Name { get; set; } public DateTime TimeStamp { get; set; } public MovieRating MovieRating { get; set; } } }
這個是與事件觸發相關的類,並不是Movie類的那種格式了
Guid解釋:GUID(全域性統一識別符號)是指在一臺機器上生成的數字,
它保證對在同一時空中的所有機器都是唯一的。通常平臺會提供生成GUID的API。
生成演算法很有意思,用到了乙太網卡地址、納秒級時間、晶片ID碼和許多可能的數字。
GUID 的格式為“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”
GUID的唯一缺陷在於生成的結果串會比較大。”
MovieEventType類
using GraphQL.Types; using GraphStudy.Movies.Movies; namespace GraphStudy.Movies.Schema { public class MovieEventType:ObjectGraphType<MovieEvent> { public MovieEventType() { Name = "MovieEventType"; Field(x => x.Id, type: typeof(IdGraphType));//guid有點特殊 Field(x => x.Name); Field(x => x.MovieId); Field(x => x.TimeStamp); Field(x => x.MovieRating, type: typeof(MovieRatingEnum)); } } }
這個還是老樣子,不過要注意有些特殊型別要額外提出來,不然執行會出錯
2.在Service資料夾建立介面IMovieEventService與實現介面MovieEventService類
IMovieEventService類
using GraphStudy.Movies.Movies;
using System;
using System.Collections.Concurrent;
namespace GraphStudy.Movies.Services
{
//Service釋出事件,客戶端可以接受這個時間的通知
public interface IMovieEventService
{
//首先需要個合集,ConcurrentStack為併發集合,與執行緒相關
ConcurrentStack<MovieEvent> AllEvent { get; }
//新增異常的方法
void AddError(Exception ex);
//新增MovieEvent方法
MovieEvent AddEvent(MovieEvent e);
//定義stream方法
IObservable<MovieEvent> EventStream();
//c#中的Observer和IObservable用於觀察者與事件、代理
}
}
MovieEventService類
using GraphStudy.Movies.Movies;
using System;
using System.Collections.Concurrent;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace GraphStudy.Movies.Services
{
public class MovieEventService : IMovieEventService
{
//ISubject:表示既是可觀察序列又是觀察者的物件。
//ReplaySubject意思為無論訂閱者什麼時候訂閱都會將以前釋出的內容釋出給他,並初始化ISubject物件
private readonly ISubject<MovieEvent> _eventStream=new ReplaySubject<MovieEvent>();
//ConcurrentStack:後進的執行緒後出
public ConcurrentStack<MovieEvent> AllEvent { get; }
public MovieEventService()
{
AllEvent=new ConcurrentStack<MovieEvent>();
}
public void AddError(Exception ex)
{
_eventStream.OnError(ex);
}
public MovieEvent AddEvent(MovieEvent e)
{
//Push推送
AllEvent.Push(e);
//OnNext:當前訊息通知
_eventStream.OnNext(e);
return e;
}
//IObservable<T>:通知程式觀察者將接到訊息通知
//T:代表觀察員,接收通知物件
public IObservable<MovieEvent> EventStream()
{
//AsObservable:隱藏源序列身份的可觀察序列
return _eventStream.AsObservable();
}
}
}
3.修改MovieSerivce類的建立程式,在其中新增事件
using GraphStudy.Movies.Movies;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace GraphStudy.Movies.Services
{
public class MovieService : IMovieService
{
//因為我們需要建立子列表,所以一要用到IList
private readonly IList<Movie> _movie;
private readonly IMovieEventService _movieEventService;
public MovieService(IMovieEventService movieEventService)
{
_movieEventService = movieEventService;
_movie=new List<Movie>
{
#region 電影列表
new Movie
{
Id = 1,
Name = "肖申克的救贖The Shawshank Redemption",
Company = "美國",
MovieRating = MovieRating.G,
ActorId = 1,
ReleaseDate = new DateTime(1994-10-14)
},
new Movie
{
Id = 2,
Name = "這個殺手不太冷 Léon ",
Company = "法國",
MovieRating = MovieRating.NC17,
ActorId = 2,
ReleaseDate = new DateTime(1994-09-14)
},
new Movie
{
Id = 3,
Name = "三傻大鬧好萊塢",
Company = "印度",
MovieRating = MovieRating.PG,
ActorId = 3,
ReleaseDate = new DateTime(2011-12-08)
},
new Movie
{
Id = 4,
Name = "功夫",
Company = "美國",
MovieRating = MovieRating.G,
ActorId = 4,
ReleaseDate = new DateTime(2004-12-23)
}
#endregion
};
}
public Task<Movie> CreateAsync(Movie movie)
{
_movie.Add(movie);
//建立時釋出事件
var movieEvent = new MovieEvent
{
Name = $"Add Movie",
MovieId = movie.Id,
MovieRating = movie.MovieRating,
TimeStamp = DateTime.Now
};
_movieEventService.AddEvent(movieEvent);
return Task.FromResult(movie);
}
public Task<IEnumerable<Movie>> GetAsyncs()
{
return Task.FromResult(_movie.AsEnumerable());
}
public Task<Movie> GetByIdAsync(int id)
{
//在這裡需要做個判斷這個id是否存在
var movie = _movie.SingleOrDefault(x => x.Id == id);
if (movie == null)
{
throw new ArgumentException(String.Format("Movie ID {0} 不正確", id));
}
return Task.FromResult(movie);
}
}
}
修改了兩個地方,可自行上下程式碼對照
//第一個
private readonly IMovieEventService _movieEventService;
public MovieService(IMovieEventService movieEventService)
//第二個
public Task<Movie> CreateAsync(Movie movie)
{
_movie.Add(movie);
//建立時釋出事件
var movieEvent = new MovieEvent
{
Name = $"Add Movie",
MovieId = movie.Id,
MovieRating = movie.MovieRating,
TimeStamp = DateTime.Now
};
_movieEventService.AddEvent(movieEvent);
看不懂沒關係,多抄幾遍,炒得多了,慢慢就懂了
4.Schema資料夾實現訂閱方法MovieSubscription類
using GraphQL.Resolvers;
using GraphQL.Subscription;
using GraphQL.Types;
using GraphStudy.Movies.Movies;
using GraphStudy.Movies.Services;
namespace GraphStudy.Movies.Schema
{
public class MovieSubscription:ObjectGraphType
{
private readonly IMovieEventService _movieEventService;
public MovieSubscription(IMovieEventService movieEventService)
{
_movieEventService = movieEventService;
Name = "Subscription";
//這裡注意。以前的field不管用,EventStreamFieldType為AddField的引數
AddField(new EventStreamFieldType
{
Name = "movieEvent",
//Arguments給AddField新增引數
Arguments = new QueryArguments(new QueryArgument<ListGraphType<MovieRatingEnum>>
{
Name = "movieRatings"
}),
Type = typeof(MovieEventType),//這裡的型別與GraphQL文件裡的型別相對應
Resolver = new FuncFieldResolver<MovieEvent>(ResolveEvent),//傳遞ResolveEvent方法
Subscriber = new EventStreamResolver<MovieEvent>(Subscribe)//傳遞Subscribe方法
});
}
private MovieEvent ResolveEvent(ResolveFieldContext context)
{
var movieEvent = context.Source as MovieEvent;
return movieEvent;
}
private IObservable<MovieEvent> Subscribe(ResolveEventStreamContext context)
{
//取得列舉的集合,這裡的name應與上面的Arguments的name對應,new List<MovieRating>()給其預設值。空值
var ratingList = context.GetArgument<IList<MovieRating>>("movieRatings", new List<MovieRating>());
//if為過濾操作,any()確定ratingList是否含有元素
if (ratingList.Any())
{
MovieRating ratings = 0;
foreach (var rating in ratingList)
{
ratings = rating;
}
return _movieEventService.EventStream().Where(e => (e.MovieRating & ratings) == e.MovieRating);
}
else
{
return _movieEventService.EventStream();
}
}
}
}
有些複雜,抄過去就是了,回頭多抄幾遍
5.將MovieSubscription新增到MovieSchema類,併到startup類註冊相關服務
MovieSchema類
using GraphQL;
namespace GraphStudy.Movies.Schema
{
public class MovieSchema:GraphQL.Types.Schema
{
public MovieSchema(IDependencyResolver dependencyResolver,
MoviesQuery moviesQuery,
MoviesMutation moviesMutation,
MovieSubscription movieSubscription)
{
DependencyResolver = dependencyResolver;
Query = moviesQuery;
Mutation = moviesMutation;
Subscription = movieSubscription;
}
}
}
startup類註冊
services.AddSingleton<MovieEventType>();
services.AddSingleton<IMovieEventService, MovieEventService>();
services.AddSingleton<MovieSubscription>();
怎麼註冊就不用多說了吧
最後檢視訂閱
先點選右邊兩個,進入監聽模式
後點擊左邊那個create,建立
然後你發現右下邊那張圖出現了下面那張圖的情況,另一個依舊在監聽模式
好了,今天的訂閱模式搞定了,多回去敲敲