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,创建
然后你发现右下边那张图出现了下面那张图的情况,另一个依旧在监听模式
在这里插入图片描述

好了,今天的订阅模式搞定了,多回去敲敲

猜你喜欢

转载自blog.csdn.net/qq_41841878/article/details/85218713