如何在DTO中正确存储许多结果?

时间:2019-05-07 00:04:49

标签: c# entity-framework asp.net-mvc-4 asp.net-web-api2

我已将存储过程附加到数据库,该数据库应仅通过简单搜索即可返回结果。该查询已添加到我的实体并调用常规方法。我面临的问题是将此过程的结果作为列表存储到特定的DTO。

是否有任何方法可以有效地将此存储过程的结果作为列表存储到DTO?

以下是我到目前为止所拥有的

Controller:

[Produces("application/json")]
    [RoutePrefix("api/jobs")]
    public class OutputController : ApiController
    {
        private TestCoastalToolsEntities _output;

        public OutputController()
        {
            _output = new TestCoastalToolsEntities();
            _output.Configuration.ProxyCreationEnabled = false;
        }        

        /**Search**/
        // POST: api/postsearch
        [System.Web.Http.HttpPost, System.Web.Http.Route("postsearch")]
        public async Task<IHttpActionResult> PostSearch(SearchInputDTO srequest)
        {
            OutputDTO<SearchInputDTO> output = new OutputDTO<SearchInputDTO>();
            SearchInputDTO SearchInput = null;
            var searchString = srequest.SearchValue.ToString();
            SearchInput.Results = _output.searchLog2(searchString);
            if (_oput != null)
            {
                output.Success = true;
                output.Results = _SearchInput.Results;
                var json = new JavaScriptSerializer().Serialize(output);
                return Ok(json);

            }
            return Ok(_ot);
        }

    }
}
-------------------------------------
Search DTO:

namespace toolPortal.API.Data.DTO
{
    public class SearchInputDTO
    {
        public List<object> Results { get; set; }
        public SearchInputDTO(output output) {
            this.ID = output.ID;
            this.Name = output.Name;
            this.Job = output.Job;
            this.Start = output.Start;
            this.End = output.End;
            this.Logs = output.Logs;           

        }
    }
}

预期结果是存储过程运行并将结果列表存储到SearchInputResults。从那里,这些结果应该存储在另一个DTO中,以便在返回时传递出去。

1 个答案:

答案 0 :(得分:0)

使用EF,您将需要利用Select()将实体映射到DTO,尽管您将需要考虑DTO的整个结构。例如,“日志”数据结构将包括什么?是单个字符串值,字符串列表还是日志记录列表?

使用Select()需要利用属性设置器,而不是接受实体的构造函数。

这样的模式:

public class Entity
{ 
   public string Field { get; set; }
}

public class Dto
{ 
    public string Field { get; set; }
}

var dtos = context.Entities
    .Where(x => x.IsActive)
    .Select(x => new Dto
    {
        Field = x.Field
    })
    .ToList();

使用构造函数查看示例:     公开课Dto     {         公共字符串字段{get;私人套装; }

    public Dto(Entity entity)
    {
       Field = entity.Field;
    }
}

var dtos = context.Entities
    .Where(x => x.IsActive)
    .Select(x => new Dto(x))
    .ToList();

这不适用于EF&Select。 EF可以映射到对象,但只能通过属性和无参数的构造函数。有一个需要注意的技巧,但是请避免看到它:

var dtos = context.Entities
    .Where(x => x.IsActive)
    .ToList()
    .Select(x => new Dto(x))
    .ToList();

在选择之前加上额外的ToList(),该调用将起作用,因为EF将执行查询并返回实体列表,然后将Select()作为Linq2Object查询执行。之所以要避免这种情况,是因为EF将从实体中选择 all 属性,在该属性中,我们仅应撤回我们关心的属性。如果您的Dto构造函数填充开始在相关实体上进行迭代,也很容易陷入延迟加载性能陷阱。使用Select可以从实体和任何相关实体仅加载您需要的字段,从而使EF可以针对所需数据构建有效的查询,而无需任何延迟加载陷阱。

使用AutoMapper,可以通过设置从实体到DTO的映射,然后利用ProjectTo<Dto>()来简化此过程。

因此,如果您希望DTO用成功的结果集合来表示结果(例如成功标志,错误消息):

[Serializable]
// Our results container.
public class SearchResultsDTO
{
    public bool IsSuccessful { get; private set; } = false;
    public string ErrorMessage { get; private set; }
    public ICollection<SearchResultDTO> Results { get; private set; } = new List<SearchResultDTO>();

    private SearchResultsDTO() {}

    public static SearchResultsDTO Success(ICollection<SearchResultDTO> results)
    {
        var results = new SearchResultsDTO
        {
            IsSuccessful = true,
            Results = results
        };
        return results;
    }
    public static SearchResultsDTO Failure(string errorMessage)
    {
        var results = new SearchResultsDTO
        {
            ErrorMessage = errorMessage
        };
        return results;
    }
}

[Serializable]
public class SearchResultDTO
{
    public int ID {get; set;}
    public string Name {get; set;}
    public string Job {get; set;}
    public DateTime Start {get; set;}
    public DateTime End {get; set;}
    public ICollection<string> Logs {get; set;} = new List<string>();  
}

然后从DbContext中填充这些内容:(在存储库中或任何读取数据的地方)

using (var context = new SearchContext())
{
    var results = context.Logs
        .Where(x => x.Name.Contains(sRequest))
        .Select(x => new SearchResultDTO
        {
            ID = x.ID,
            Name = x.Name,
            Job = x.Job,
            Start = x.Start,
            End = x.End,
            Logs = x.LogLines.Select(y => y.Line).ToList(),
        }).ToList();

     var resultDto = SearchResultsDTO.Success(results);
     return resultsDto;
}

这假定日志条目具有作业,名称,开始,结束日期/时间,然后是“行”列表或要显示为“日志”的条目。 (其中,日志表具有相关的LogLine表,例如具有一行或多行)。这演示了如何利用Select将日志记录不仅映射到DTO中,而且还将相关记录映射到诸如也可以完成字符串的收集或其他DTO的收集。

一旦选择了DTO,我就使用静态工厂方法将其填充到容器DTO中,以填充成功的读取或失败的读取。 (例如,可以在异常处理程序中进行设置。)或者,您可以新建一个容器类并填充属性,使用构造函数/ w参数,或者仅返回DTO列表。 EF查询中未 引用SearchResultsDTO容器。