我正在尝试为我的客户端移动应用创建一个简单的API。我创建了新的空Web API
项目和一个动作,但它非常慢:
[HttpGet, Route("manga")]
public List<MangaDTO> GetList()
{
using (var mc = new MangaContext()) // Sometimes takes 10s to instantiate the context
{
var list = mc.Mangas.ToList(); // Takes ~3.5s to get all
var mangas = new List<MangaDTO>();
foreach (var o in list) // This loop takes literally forever. ~35s on my server.
mangas.Add(new MangaDTO(o));
return mangas;
}
}
有没有更有效的方法来实现我想要的?获取响应需要花费太长时间,当我将其部署到远程服务器时,它甚至不会显示结果 - 只是超时错误。
我可以做些什么来加快速度?另外,这是MangaContext
类:
[DbConfigurationType(typeof(MySql.Data.Entity.MySqlEFConfiguration))]
public class MangaContext : DbContext
{
private static MangaContext current;
public static MangaContext Current
{
get { if (current == null) current = new MangaContext(); return current; }
}
public MangaContext()
{
Database.CreateIfNotExists();
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MangaContext>());
}
public virtual DbSet<Manga> Mangas { get; set; }
public virtual DbSet<Genre> Genres { get; set; }
public virtual DbSet<Alias> Aliases { get; set; }
}
我在这里遗漏了什么吗?因为我怀疑实例化应该需要~10s。
编辑:
我从Context的构造函数中删除了Database.CreateIfNotExists()
调用。另外,这是MangaDTO类:
public class MangaDTO
{
public MangaDTO(Manga m)
{
id = m.Id;
name = m.Name;
genres = m.Genres.Select(o => o.Name).ToList();
author = m.Author;
artist = m.Artist;
score = m.Score;
year = m.Year;
aliases = m.Aliases.Select(o => o.Name).ToList();
}
public int id { get; set; }
public string name { get; set; }
public List<string> genres { get; set; }
public string author { get; set; }
public string artist { get; set; }
public int year { get; set; }
public List<string> aliases { get; set; }
public int score { get; set; }
}
......和模特:
public class Manga
{
public Manga() { }
public Manga(string name, string desc, string author, string artist, int year, int status, int chapters, string url)
{
Name = name;
Description = desc;
Author = author;
Artist = artist;
Year = year;
Status = status;
Url = url;
Aliases = new List<Alias>();
Genres = new List<Genre>();
ChaptersCount = chapters;
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Author { get; set; }
public string Artist { get; set; }
public string Url { get; set; }
public int Year { get; set; }
public int Status { get; set; }
public int ChaptersCount { get; set; }
public int Score { get; set; }
public virtual List<Alias> Aliases { get; set; }
public virtual List<Genre> Genres { get; set; }
public override string ToString()
{
return Name;
}
}
public class Alias
{
public Alias() { }
public Alias(string name, Manga manga)
{
Name = name;
Manga = manga;
}
public int Id { get; set; }
public string Name { get; set; }
public virtual Manga Manga { get; set; }
}
public class Genre
{
public Genre() { }
public Genre(string name)
{
Name = name;
Mangas = new List<Manga>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Manga> Mangas { get; set; }
}
编辑2
现在它在我的远程服务器上引发异常,但是
{"Message":"An error has occurred.","ExceptionMessage":"An error occurred while executing the command definition. See the inner exception for details.","ExceptionType":"System.Data.Entity.Core.EntityCommandExecutionException","StackTrace":" at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)\r\n at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlan.Execute[TResultType](ObjectContext context, ObjectParameterCollection parameterValues)\r\n at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)\r\n at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClassb.b__9()\r\n at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)\r\n at System.Data.Entity.Core.Objects.ObjectQuery`1..GetEnumerator>b__0()\r\n at System.Lazy`1.CreateValue()\r\n at System.Lazy`1.LazyInitValue()\r\n at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()\r\n at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)\r\n at MangaAPI.Controllers.MangaController.GetList() in C:\\Users\\mrrey\\OneDrive\\Documents\\Visual Studio 2015\\Projects\\XYZ Manga Reader\\MangaAPI\\Controllers\\MangaController.cs:line 39\r\n at lambda_method(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.b__9(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()","InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.","ExceptionType":"MySql.Data.MySqlClient.MySqlException","StackTrace":" at MySql.Data.MySqlClient.ExceptionInterceptor.Throw(Exception exception)\r\n at MySql.Data.MySqlClient.MySqlConnection.HandleTimeoutOrThreadAbort(Exception ex)\r\n at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)\r\n at MySql.Data.Entity.EFMySqlCommand.ExecuteDbDataReader(CommandBehavior behavior)\r\n at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TInterceptionContext,TResult](Func`1 operation, TInterceptionContext interceptionContext, Action`1 executing, Action`1 executed)\r\n at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(DbCommand command, DbCommandInterceptionContext interceptionContext)\r\n at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)","InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.","ExceptionType":"System.TimeoutException","StackTrace":" at MySql.Data.Common.MyNetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)\r\n at MySql.Data.MySqlClient.TimedStream.Read(Byte[] buffer, Int32 offset, Int32 count)\r\n at System.IO.BufferedStream.Read(Byte[] array, Int32 offset, Int32 count)\r\n at MySql.Data.MySqlClient.MySqlStream.ReadFully(Stream stream, Byte[] buffer, Int32 offset, Int32 count)\r\n at MySql.Data.MySqlClient.MySqlStream.LoadPacket()\r\n at MySql.Data.MySqlClient.MySqlStream.ReadPacket()\r\n at MySql.Data.MySqlClient.NativeDriver.GetResult(Int32& affectedRow, Int64& insertedId)\r\n at MySql.Data.MySqlClient.Driver.NextResult(Int32 statementId, Boolean force)\r\n at MySql.Data.MySqlClient.MySqlDataReader.NextResult()\r\n at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)","InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.","ExceptionType":"System.IO.IOException","StackTrace":" at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)\r\n at MySql.Data.Common.MyNetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)","InnerException":{"Message":"An error has occurred.","ExceptionMessage":"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond","ExceptionType":"System.Net.Sockets.SocketException","StackTrace":" at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)"}}}}}
答案 0 :(得分:2)
我不能说为什么实例化一个新的上下文需要这么长时间,除了最终使用的构造函数和db初始化程序中的代码,所以你可以尝试删除它们(替换为Database.SetInitializer(null);
)并查看是否有帮助
但是查询肯定会加速。目前,您正在将整个Manga
表加载到内存中,然后为每条记录加载,还有两个查询由
genres = m.Genres.Select(o => o.Name).ToList();
和
aliases = m.Aliases.Select(o => o.Name).ToList();
因为m.Genres
和m.Aliases
是延迟加载的导航属性。另请注意,它们首先加载所有相关对象,然后LINQ to Objects在内存中执行Select(o => o.Name)
。
你可以通过这样直接投射到MangaDTO
来避免所有这些(单个SLQ查询,没有实体跟踪开销):
using (var mc = new MangaContext())
{
var query = mc.Mangas
.Select(m => new MangaDTO
{
id = m.Id,
name = m.Name,
genres = m.Genres.Select(o => o.Name).ToList(),
author = m.Author,
artist = m.Artist,
score = m.Score,
year = m.Year,
aliases = m.Aliases.Select(o => o.Name).ToList(),
});
var mangas = query.ToList();
return mangas;
}
确保将无参数构造函数添加到MangaDTO
。您现在可以衡量ToList
来电,看看需要多长时间。