在Web API上的多对多关系

26 浏览
0 Comments

在Web API上的多对多关系

我正在努力学习Entity Framework Core。不幸的是,当你创建一个多对多的关系时,它似乎要求一个连接类。请参见下面的类结构:

Person(人)

Sport(运动)

PersonSport(人-运动)

一个人有很多种运动,而一种运动也有很多人参与。在使用Entity Framework时,我会在Person类中放置一个Sport集合,在Sport类中放置一个Person集合。然而,现在我必须创建PersonSport类。请参见下面的API控制器(这是复制问题所需的唯一代码):

namespace WebApplication1.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class PersonController : Controller
    {
        [Route("")]
        [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)]
        //[ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)]
        [HttpGet]
        public async Task GetPerson()
        {
            Person p1 = new Person { Id = Guid.NewGuid() };
            Sport s1 = new Sport { Id = Guid.NewGuid(), Description = "Football" };
            Sport s2 = new Sport { Id = Guid.NewGuid(), Description = "Running" };
            PersonSport ps1 = new PersonSport { Person = p1, PersonId = p1.Id, Sport = s1, SportId = s1.Id };
            PersonSport ps2 = new PersonSport { Person = p1, PersonId = p1.Id, Sport = s2, SportId = s2.Id };
            List personSports = new List();
            personSports.Add(ps1);
            personSports.Add(ps2);
            p1.AssignSports(personSports);
            return Ok(p1);
        }
    }
    public class Entity
    {
        public Guid Id { get; set; }
    }
    public class Person : Entity
    {
        private readonly List _personSports;
        public IReadOnlyCollection PersonSports => _personSports.AsReadOnly();
        //public Guid Id { get; set; }
        public Person()
        {
            _personSports = new List();
        }
        public void AssignSports(IEnumerable personSports)
        {
            this._personSports.AddRange(personSports);
        }
    }
    public class Sport : Entity
    {
        //public Guid Id { get; set; }
        public string Description { get; set; }
    }
    public class PersonSport
    {
        public Person Person { get; set; }
        public Sport Sport { get; set; }
        public Guid PersonId { get; set; }
        public Guid SportId { get; set; }
    }
}

我调试Web API项目并导航到:http://localhost:57320/api/Person,看到如下结果:

enter image description here

注意到JSON格式不正确。如果我尝试从控制台应用程序/单元测试应用程序中使用Web API,我会看到以下错误:

System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   --- End of inner exception stack trace ---
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)
   at System.Net.Http.HttpConnection.FillAsync()
   at System.Net.Http.HttpConnection.ChunkedEncodingReadStream.CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.GetStringAsyncCore(Task`1 getTask)
   at ConsoleApp1.Program.CallWebAPI() 

我该如何解决这个问题?我用于消费Web API的代码如下:

HttpClient enquiryClient = new HttpClient();
var responseString = await enquiryClient.GetStringAsync("http://localhost:57320/api/Person");
var response = JsonConvert.DeserializeObject(responseString);

0
0 Comments

很多到很多的关系是Web API中常见的一个问题。这个问题的出现是因为在实现实体框架之前,实体中的痛点在于实体本身。解决这个问题的方法是实现实体框架。下面是一个示例代码片段,用于测试API:

public async Task GetPerson()
{
    var person = new Person() { Id = new Guid.NewGuid() };
    var sports = new [] {
        new Sport() { Id = Guid.NewGuid(), Description = "Football" },
        new Sport() { Id = Guid.NewGuid(), Description = "Soccer" }
    };
    person.AssignSports(sports);
    return Ok(person);
}

在实现实体框架后,其他部分,如虚拟属性、连接表、数据加载和导航属性等,将会出现。下面的代码片段是将json选项添加到启动文件中的解决方法:

.AddJsonOptions(options => {
    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
    options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
})

关于第一个问题,是的,这是正确的。如果设置了实体,可以将运动分配给人员。这样可以更直观地进行操作。在幕后,实体框架将填充连接表,尽管这取决于设置。

另一个要指出的事情是,由于使用了API,更清晰的方法是将域模型与数据层分开。这需要额外的工作,但你将获得一个更易维护的解决方案。有关更多信息,请查看以下链接:

stackoverflow.com/questions/23648832

如果你正在使用CQRS,将写数据库映射到域模型是正常的做法。你提到的链接谈到了MVVM,我认为这是一个客户端模式。当你谈论隔离域模型时,是否指的是这样的内容?

github.com/sapiens/ModernMembership/blob/master/src/ModernMembership/LocalMember.cs

关于这个问题,我的回答是在仓储模式中获取数据对象,然后在返回之前将其转换为域对象。直接映射到域模型是一个好的做法,但你应该将其映射到数据模型作为ORM和域模型之间的中介。这是我观点,希望能理解。

术语可能会令人困惑,因为它们的定义并不总是正确使用的。提示:如果你正在使用ORM,则不要使用仓储模式:ORM已经是仓储模式。所以:数据库->ORM/实体/数据模型->域模型,其中1)是数据库本身(没有代码/类等),2)是ORM,实体框架,实体,数据层等等可以使用的其他名称,3)是域,它在应用程序中使用,在第三方中公开,并在业务逻辑中使用。这也是CQRS的结果和输入。

数据模型、数据层、实体、ORM基本上都是相同的:它们都存在于数据层上下文中,靠近数据库。通过API公开的东西必须接近域模型,具有自说明的对象。

数据库映射到域模型。在这种情况下,如何避免PersonSport实体,并使用Person.Sports集合而不是Person.personsports?

定义“映射”。在我的定义中,它是从一个“东西”到另一个“东西”的转换;所以:数据库映射到实体/ORM,实体/ORM映射到域模型,反之亦然。

这是最后一个问题,然后我就会接受。如果我正在使用CQRS,那么我将拥有一个域模型、一个写模型(映射到数据库的实体类)和一个读模型(映射到数据库的实体类)。我对你说的理解正确吗?

是的,让我举个例子。在CQRS中,你有查询和命令。你的命令可能是一个在域中定义的操作。例如:AddNotificationMethod;如果这个命令在更广泛的域中定义,比如跨应用程序:它需要是一个域模型,因为实体可能在所有这些应用程序中都没有定义。对于查询也是一样的:比如说GetCar,结果是一辆车(显然)。但它是一个域车还是一个实体车?如果你将来要在不同的上下文中使用这辆车,它应该是一个域模型。

请注意:如果你创建一个API,你几乎可以定义其他应用程序参与你的设置。

0