5

私は、ProductsとShoppingCartsの2つの異なるエンティティを持つアプリケーションを開発しています。各製品は一意であり、一意の識別子があります。別のカートにまだ入っておらず、ShoppingCartに販売されていない商品を追加したいと思います。

簡略化された製品エンティティ:

public class Products
{
    public int Id { get; set; }
    public string Name{ get; set; }
    public bool Sold { get; set; }
}

ショッピングカートエンティティの簡略化:

public class ShoppingCarts
{
    public int Guid Guid { get; set; }
    public int ProductId { get; set; }
}

したがって、最初にすべてのProduct.Idを取得してから、カートに追加します。私の方法は次のようになります。

private IQueryable<Products> GetAvailableProductId(int quantity)
{
    var query = (from p in _context.Set<Products>()
                join sc in _context.Set<ShoppingCarts>() on p.Id equals sc.ProductId into subset
                from sc in subset.DefaultIfEmpty()
                where !p.Sold && sc == null
                select p).Take(quantity);
    return query;
}

何らかの理由で、同じProductIdを持つ2つのエンティティが異なるカートに追加されることがあります。これにより、アプリケーションは同じ製品を2つ販売できるようになりました。トランザクションを行う前に、アプリケーションで別のチェックを実行することで、これを修正することになりました。

私は最近コードを再検討し、これらの投稿に出くわしました LINQクエリ:エンティティへのキーLINQに基づいて、あるリストのオブジェクトが別のリストに存在するかどうかを判断し、 NOTINテーブルに参加します

私の質問は、クエリをこのようなものに変更すると、二重加算が防止されるかどうかです。

private IQueryable<Products> NewGetAvailableProductId(int quantity)
{
    var query = (from p in _context.Set<Products>()
                where !_context.Set<ShoppingCarts>().Any(x => x.ProductId == p.Id) &&  !p.Sold
                select p).Take(quantity);
    return query;
}

ご不明な点がございましたら、お気軽にお問い合わせください。

ありがとう、

4

3 に答える 3

6

元のクエリから個別のレコードを取得すると、目的の結果が得られるはずです。Take()の前のDistinct()に注意してください。

var query = (from p in _context.Set<Products>()
                join sc in _context.Set<ShoppingCarts>() on p.Id equals sc.ProductId into subset
                from sc in subset.DefaultIfEmpty()
                where !p.Sold && sc == null
                select p).Distinct().Take(quantity);

重複する理由は、元のクエリが製品テーブルとカートテーブルの間の一致のリストを提供するためです。たとえば、カートproduct1cart1ありcart2product2カートがない場合、結合から次の結果が得られます。

product1, cart1
product1, cart2
product2, null

次に、ヌルカートを除外します

product1, cart1
product1, cart2

次に、製品オブジェクトのみを選択します

product1
product1

この時点で、重複した製品が残ります。追加した個別の関数は、このリストを取得して、重複するエントリの1つを除くすべてを削除します。あなたを残して、

product1

同様の結果が得られたとしても、クエリがまったく異なる可能性があるため、各クエリによって生成されたSQLを確認する価値があります。最初のクエリではLEFTOUTERJOINが使用され、2番目のクエリではIN句が使用されると思われます。私の経験では、IN句はかなり遅いので、LEFT OUTER JOINを使用しますが、可能であれば避ける必要があります。明らかに、これは自分の環境で測定する必要があります。

また、2番目のクエリにはwhere !p.Sold、最初のクエリにあったクエリがありません。

于 2012-07-11T15:57:13.987 に答える
3

意図せず間違った木に吠えているような気がします。

簡単なシナリオは次のとおりです。

  • ユーザー 1 は商品の購入を希望しています。アプリは、カートに入っているかどうかを確認します。いいえ。
  • ユーザー 2 は商品の購入を希望しています。アプリは、カートに入っているかどうかを確認します。いいえ。
  • ユーザー 1 のスレッドは、カートに追加するプロセスを完了します。
  • ユーザー 2 のスレッドは、カートに追加するプロセスを完了します。前のチェックが成功したので、まだ安全であると想定しています。

基本的に、トランザクション、クリティカル セクション、シングルトン、または同様のデバイスを使用して、1 人だけが単一の操作としてカートに追加できるようする必要があります。単一の作業単位として成功または失敗する必要があります。

于 2012-07-11T16:29:55.740 に答える
1

この質問をチェックしてください: LINQ to Entity, Joining on NOT IN tables。上記のソリューションよりもはるかにクリーンなアプローチ。

クエリを見ると、繰り返しレコードが表示されないようにするものは何もありません。これを使用する必要があります:オブジェクトのリストからプロパティの一意のリストを取得するために Linq を使用するにはどうすればよいですか?

于 2013-01-14T16:13:18.250 に答える