首页 > 解决方案 > 使用Odata基于EF查询DTO

问题描述

我有一个带有 SQL Server 数据库和 EF 数据模型的 ASP.NET Core Web API 设置。

版本:

问题是:我不能使用过滤器,在展开中选择(嵌套展开)。OData 未将过滤器添加到 SQL Server Profiler 上显示的 SQL 查询的 where 条件的示例 URL:

https://localhost:44327/odata/clientcontract?$expand=ContactsInfo($filter=value eq '100003265')

https://localhost:44327/odata/clientcontract?$expand=Documents($filter=documentnnumber eq '100003265')

这些是我的数据库优先实体模型:

public partial class ClientRef
{
    public ClientRef()
    {
        Addresses = new HashSet<Address>();
        Assets = new HashSet<Asset>();
        ClientContactInfoComps = new HashSet<ClientContactInfoComp>();
        ClientRelationCompClient1Navigations = new HashSet<ClientRelationComp>();
        ClientRelationCompClient2Navigations = new HashSet<ClientRelationComp>();
        Clients = new HashSet<Client>();
        CommentComps = new HashSet<CommentComp>();
        Companies = new HashSet<Company>();
        Documents = new HashSet<Document>();
        PhysicalPeople = new HashSet<PhysicalPerson>();
    }

    [Column("Inn")]
    public int Id { get; set; }

    public virtual ICollection<Address> Addresses { get; set; }
    public virtual ICollection<Asset> Assets { get; set; }
    public virtual ICollection<ClientContactInfoComp> ClientContactInfoComps { get; set; }
    public virtual ICollection<ClientRelationComp> ClientRelationCompClient1Navigations { get; set; }
    public virtual ICollection<ClientRelationComp> ClientRelationCompClient2Navigations { get; set; }
    public virtual ICollection<Client> Clients { get; set; }
    public virtual ICollection<CommentComp> CommentComps { get; set; }
    public virtual ICollection<Company> Companies { get; set; }
    public virtual ICollection<Document> Documents { get; set; }
    public virtual ICollection<PhysicalPerson> PhysicalPeople { get; set; }
}

public partial class Document
{
    public int Id { get; set; }
    public string DocumentNumber { get; set; }
    public int DocumentType { get; set; }
    public int Inn { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }
    public DateTime? DocumentExpireDate { get; set; }

    public virtual ClientRef InnNavigation { get; set; }
}

public partial class ClientContactInfoComp
{
    public int Id { get; set; }
    public int Inn { get; set; }
    public int ContactInfoId { get; set; }
    public int? Point { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }

    [ForeignKey("ContactInfoId")]
    public  ContactInfo ContactInfo { get; set; }
    public virtual ClientRef InnNavigation { get; set; }
}

public partial class ContactInfo
{
    public ContactInfo()
    {
        CallHistories = new HashSet<CallHistory>();
        ClientContactInfoComps = new HashSet<ClientContactInfoComp>();
        CommentComps = new HashSet<CommentComp>();
    }

    public int Id { get; set; }
    public int? Type { get; set; }
    public string Value { get; set; }

    public virtual ICollection<CallHistory> CallHistories { get; set; }
    public virtual ICollection<ClientContactInfoComp> ClientContactInfoComps { get; set; }
    public virtual ICollection<CommentComp> CommentComps { get; set; }
}

public partial class CommentComp
{
    public int Id { get; set; }
    public int Inn { get; set; }
    public int? CommentId { get; set; }
    public int? ContactId { get; set; }

    public virtual Comment Comment { get; set; }
    public virtual ContactInfo Contact { get; set; }
    public virtual ClientRef InnNavigation { get; set; }
}

public partial class Comment
{
    public Comment()
    {
        CommentComps = new HashSet<CommentComp>();
    }

    public int Id { get; set; }
    public string Text { get; set; }
    public DateTime? CreateTimestamp { get; set; }
    public string Creator { get; set; }

    public virtual ICollection<CommentComp> CommentComps { get; set; }
}

这些是我的 DTO:

[DataContract]
public class ClientContract
{
    public ClientContract()
    {
        ContactsInfo = new List<ContactInfoContract>();
        Documents = new List<DocumentContract>();
        Relations = new List<RelationContract>();
        ClientComment = new CommentContract();
    }

    [DataMember(Name = "INN")]
    [Key]
    public int INN { get; set; }

    [DataMember(Name = "validfrom")]
    public DateTime ValidFrom { get; set; }

    [DataMember(Name = "validto")]
    public DateTime ValidTo { get; set; }

    [DataMember(Name = "clienttype")]
    public ClientType ClientType { get; set; }

    [DataMember(Name = "companyname")]
    public string CompanyName { get; set; }

    [DataMember(Name = "firstname")]
    public string FirstName { get; set; }

    [DataMember(Name = "lastname")]
    public string LastName { get; set; }

    [DataMember(Name = "fathername")]
    public string FatherName { get; set; }

    [DataMember(Name = "pinnumber")]
    public string PinNumber { get; set; }

    [DataMember(Name = "birthdate")]
    public DateTime? BirthDate { get; set; }

    [DataMember(Name = "positioncustom")]
    public string PositionCustom { get; set; }

    [DataMember(Name = "position")]
    public int Position { get; set; }

    [DataMember(Name = "monthlyincome")]
    public decimal MonthlyIncome { get; set; }

    [DataMember(Name = "clientcomment")]
    public CommentContract ClientComment { get; set; }        

    [DataMember(Name = "contactsinfo")]
    public List<ContactInfoContract> ContactsInfo { get; set; }
    
    [DataMember(Name = "documents")]
    [ForeignKey("Documents")]
    public List<DocumentContract> Documents { get; set; }

    [DataMember(Name = "relations")]
    public List<RelationContract> Relations { get; set; }
}

public class DocumentContract
{
    [Key]
    public int Id { get; set; }

    [DataMember(Name = "documentNumber")]
    public string documentNumber { get; set; }

    [DataMember(Name = "documentType")]
    public int documentType { get; set; }

    [DataMember(Name = "documentexpiredate")]
    public DateTime? documentExpireDate { get; set; }
}

[DataContract]
public class ContactInfoContract
{
    public ContactInfoContract()
    {
        ContactComment = new CommentContract();
    }

    [Key]
    public int Id { get; set; }

    [DataMember(Name = "type")]
    public int Type { get; set; }

    [DataMember(Name = "value")]
    public string Value { get; set; }

    [DataMember(Name = "contactComment")]
    public CommentContract ContactComment { get; set; }
}

public class CommentContract
{
    [DataMember(Name = "Text")]
    public string Text { get; set; }

    [DataMember(Name = "creator")]
    public string Creator { get; set; }
}

在 DTO 中没有关系模型。例如:EF中有一个ClientContactInfoComp模型,它连接ClientRefs和ContactInfo模型,而在DTO中ClientContract是直接用ContactInfoContract引用的。

Startup.cs 中的模型生成器

private  IEdmModel GetEdmModel()
{
        var builder = new ODataConventionModelBuilder();
        builder.EntitySet<ClientContract>("ClientContract").EntityType.HasKey(x => x.INN).Name = "ClientRef";
        builder.EntitySet<DocumentContract>("DocumentContract");
        builder.EntitySet<ContactInfoContract>("ContactInfoContracts").EntityType.HasKey(x => x.Id);

        return builder.GetEdmModel();
}

public class ClientContractController : ControllerBase
{
    [EnableQuery(MaxExpansionDepth = 10)]
    public IQueryable<ClientContract> Get()
    {
        var clientRefs = _context.ClientRefs
                         .Include(x => x.Clients.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
                         .Include(x => x.PhysicalPeople.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
                         .Include(x => x.Companies.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
                         .Include(x => x.Documents.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
                         .Include(x => x.ClientContactInfoComps.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
                         .ThenInclude(x => x.ContactInfo)
                         .Include(x => x.Assets.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now));

        List<ClientContract> contracts = new List<ClientContract>();

        foreach (var clientRef in clientRefs)
        {
            ClientContract clientContract = new ClientContract() { INN = clientRef.Id };

            foreach(var c in clientRef.Clients)
            {
                clientContract.ClientType = (ClientType) c.ClientType;
            }

            foreach (var pp in clientRef.PhysicalPeople)
            {
                clientContract.FirstName = pp.FirstName;
                clientContract.LastName = pp.LastName;
            }

            foreach (var comp in clientRef.Companies)
            {
                clientContract.CompanyName = comp.CompanyName;
            }

            foreach (var doc in clientRef.Documents)
            {
                clientContract.Documents.Add(new DocumentContract()
                {
                    documentNumber = doc.DocumentNumber,
                    documentExpireDate = doc.DocumentExpireDate,
                    documentType = doc.DocumentType
                    
                });
            }

            foreach (var comp in clientRef.ClientContactInfoComps)
            {
                clientContract.ContactsInfo.Add(new ContactInfoContract
                {
                    Type = comp.ContactInfo.Type.Value,
                    Value = comp.ContactInfo.Value
                });
            }

            contracts.Add(clientContract);
        }

        return contracts.AsQueryable();
    }
}

标签: asp.net-coreodatawebapi

解决方案


使用时应该使用属性名称而不是属性名称OData

OData客户端库依赖于它自己的属性OriginalNameAttribute来获取有关类/成员名称的知识,因为服务器发出它们。您可以从这里看到详细信息。

当我删除 [DataContract] 属性时它可以工作。

在此处输入图像描述

public class ClientContract
{
    public ClientContract()
    {
        ContactsInfo = new List<ContactInfoContract>();
    }

    [Key]
    public int INN { get; set; }

    public string CompanyName { get; set; }

    public List<ContactInfoContract> ContactsInfo { get; set; }


}


public class ContactInfoContract
{
    [Key]
    public int Id { get; set; }

    [DataMember(Name = "type")]
    public int Type { get; set; }

    [DataMember(Name = "value")]
    public string Value { get; set; }

}

推荐阅读