0

現在、MVC4のWeb Api機能を使用して、遅延読み込みとプロキシ作成を無効にしたEntityFramework4を使用してAPIを構築しています。既存のMySQLデータベースを移行しているので、データベースファースト戦略を使用しました。

私のサーバーは、IIS8およびMicrosoftSQLServer2012を搭載したWindowsServer2012(現時点ではまだDatacenter RC)で実行されています。

私が抱えている問題は、「機能」カテゴリではなく、「パフォーマンス」カテゴリにあると思います。たとえば、Personオブジェクトがあります。このオブジェクトには、子のリストと都市オブジェクト(都市の基本情報を含む)があります。

[DataContract(IsReference = true)]
public partial class Person
{
    public Person()
    {
        this.Children = new HashSet<Children>();
    }

    [DataMember]
    public int ID { get; set; }
    [DataMember]
    public int ParentID { get; set; }
    [DataMember]
    public int CityID { get; set; }
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public int Age { get; set; }

    [DataMember]
    public virtual ICollection<Person> Children { get; set; }
    [DataMember]
    public City City { get; set; }
}

[DataContract(IsReference = true)]
public partial class City
{
    [DataMember]
    public int ID { get; set; }
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public int Residents { get; set; }
}

Note: These classes are generated by EntityFramework/DBContext

これらのクラスは、httpgetを使用して個人から情報を取得するためにPersonsControllerで使用されます。

public class PersonsController : ApiController
{
    private MyEntities db = new MyEntities();

    [HttpGet]
    public Person Get(int id, bool getChildren = false, bool getCity = false)
    {
        Person person = this.db.Persons.SingleOrDefault(p => p.ID == id);
        if (person == null)
        {
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
        }

        if (getChildren)
        {
            person.Children = this.db.Persons.Where(c => c.ParentID == person.ID)
        }

        if (getCity)
        {
            person.Customer = this.db.Cities.SingleOrDefault(r => r.ID == person.CityID);
        }

        return person;
    }
}

まず第一に、これがWeb Api内でEntityFrameworkを使用する正しい方法、特にdbオブジェクトの作成である場合、誰かが私にいくつかの情報を提供してくれることを望みました。いわゆるリポジトリクラスの作成に関する複数の記事を読みましたが、そうしないことを選択しました。

今、私がこの質問を作成している問題。ID 1のPersonの情報を取得するリクエストを実行すると、すべてうまくいきます。また、GetChildrenおよびgetCityパラメーターをtrueに設定します。(これは、データベースで利用可能なすべての人に有効です)。ただし、これは一度に1つのリクエストを実行する場合です。Fiddlerを使用して同じリクエストを次々と高速に実行すると、いくつかのリクエストの後、500エラーが表示され始めます。その後、いくつかのリクエストが再び正常になり、その後、2、3回失敗します。

リクエストが失敗すると、次のいずれかのエラーメッセージが表示されます。セッションで他のスレッドが実行されているため、新しいトランザクションは許可されません。またはこのトランザクションで処理中の保留中の要求があるため、トランザクション操作を実行できません。

つまり、このセッションには他のスレッドがあり、キューがすでに存在するためにトランザクションにリクエストを追加できないため、彼は新しいトランザクションを作成できません。

これは、APIがライブになり、同時に複数のユーザーによって使用されるときにも発生する可能性があるため、これが本当に厄介であることを理解していただければ幸いです。

上記のエラーを解決する方法を知っている人はいますか?上記の私の実装例を確認してください、多分それは最適ではありません:)

前もって感謝します!

4

1 に答える 1

0

この問題を解決するには、接続文字列に 'MultipleActiveResultSets=True' を追加してMARSを有効にします。MARS を有効にする必要があるのは、単一の接続、つまり DbContext で複数のクエリを実行しているためです。

代替のより良い方法は、MARS を有効にせず、代わりにこれを実行して単一のクエリを実行するようにアクションを最適化することです (Entityframework熱心な読み込み)。これには、3 つのクエリに対して 1 つのクエリを呼び出すという利点があります (最悪の場合)。

public class PersonsController : ApiController
{
    private MyEntities db = new MyEntities();

    public Person Get(int id, bool getChildren = false, bool getCity = false)
    {
        var people = this.db.Persons;

        if(getChildren)
        {
            people = people.Include("Children");
        }

        if(getCity)
        {
            people = people.Include("City");
        }

        Person person = people.SingleOrDefault(p => p.ID == id);
        if (person == null)
        {
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
        }

        return person;
    }
}
于 2013-02-10T18:23:37.827 に答える