加入两个列表在IEnumerable C#Entity框架中返回IEnumerable

时间:2017-07-12 08:46:50

标签: c# asp.net-mvc entity-framework linq

我试图加入两个列表,但是当我期待只有简单的List时,结果在IEnumerable中给出了IEnumerable。我可以将IEnumerables转换为列表,但我很困惑为什么会发生这种情况。为什么这只是为了从连接中获取一个列表而需要做什么?

namespace arena.DAL
{

public class EquipmentData
{
    private Arena db = new Arena();

    public ArmorModel getArmor()
    {
        var user = UserData.getCurrentUser();

        var equipmentArmorList = db.EquipmentModels.Where(e => e.AccountId == user.Id && e.EquipmentType == (int)Constants.EquipmentConstants.EquipmentType.Armor).ToList();

        var armorList = db.ArmorModels.ToList();

        var result = from f in equipmentArmorList
                     join s in armorList
                     on f.EquipmentId equals s.Id into g
                     select g.ToList();
        return result;
    }

}
}


public class EquipmentModel
{
    public EquipmentModel()
    {

    }

    public int Id { get; set; }
    public string AccountId { get; set; }
    public int EquipmentId { get; set; }
    public int EquipmentType { get; set; }
    public bool Equipped { get; set; }
}

public class ArmorModel
{
    public ArmorModel()
    {

    }
    public int Id { get; set; }
    public int Level { get; set; }
    public string Name { get; set; } 
    public string Biographi { get; set; }
    public double Health { get; set; }
    public double Stamina { get; set; }
    public double Strength { get; set; }
    public double Initiative { get; set; }
    public double Avoidance { get; set; }
    public double Armor { get; set; }
    public double Durability { get; set; }
    public int MinDamage { get; set; }
    public int MaxDamage { get; set; }
    public int ExperienceGain { get; set; }
    public int CurrencyGain { get; set; }
    public int Cost { get; set; }
    public int Slot { get; set; }
}

1 个答案:

答案 0 :(得分:1)

主要问题是在GetArmor中不清楚你要求的是什么。

返回值表示它应该只返回一个ArmorModel。然而,您的查询会生成您尝试返回的List。当然这不会编译。

此外,即使在编辑之后,EquipmentModel和ArmorModel之间的关系也不清楚。

在您的查询中,变量f的类型为EquipmentModel,而s的类型为ArmorModel。您在EquipmentModel.EquipmentId == ArmorModel.Id上执行了加入。

似乎每个ArmorModel都有零个或多个EquipmentModels。或者在数据库术语中:ArmorModelEquipmentModel之间存在一对多关系,其中EquipmentModel具有其所属ArmorModel主键的外键在财产EquipmentId

除了在属性命名中相当混乱的选择之外,这种一对多关系在您的类中没有正确建模。如果您希望实体框架代表您的一对多关系,那么类定义应该是:

See this article about proper one-to-many Entity Framework relations

class ArmorModel
{
    public int Id {get; set;}

    // an ArmorModel has many EquipmentModels
    public virtual ICollection>EquipmentModel> EquipmentModels {get; set;}
    ...
}

public class EquipmentModel
{
    public int Id {get; set;}

    // an EquipmentModel belongs to an ArmorModel via foreign key
    public int EquipmentId {get; set;}
    public virtual ArmorModel ArmorModel {get; set;}
    ...
}

public class MyDbContext : DbContext
{
    public DbSet<EquipmentModel> EquipmentModels {get; set;}
    public DbSet<ArmorModel> ArmorModels {get; set;}
}

由于表名/外键等不匹配,需要一些属性或流畅的API。考虑将属性EquipmentId的名称更改为正确的默认值ArmoreModelId。实体框架将自动理解一对多关系:

public class EquipmentModel
{
    public int Id {get; set;}

    // an EquipmentModel belongs to an ArmorModel via proper default foreign key
    public int ArmorModelId {get; set;}
    public virtual ArmorModel ArmorModel {get; set;}
    ...
}

现在回到你的问题。由于您的查询与您应该返回的类型之间存在如此大的不匹配,因此无法确定您想要的内容。

如果您希望所有ArmorModel的集合中有一个特殊的ArmorModels。您似乎希望只有ArmorModel一个或多个EquipmentModels匹配某些属性。

在正确的一对多定义之后,您的查询会更简单:

var matchingArmoreModels = db.ArmorModels
    // I don't want all ArmorModels,
    // I only want those ArmorModels that have at least one EquipmentModel
    // that match certain conditions with AccountId and EquipmentType
    .Where(armorModel => armorModel.EquipmentModels
        .Where(equipmentModel => equipmentModel.AccountId == user.Id
            && equipmentModel.EquipmentType == ...)
        // it is enough if it has at least one matching EquipmentModel:
        .Any());

现在,您的所有ArmorModels至少有一个EquipmentModel具有正确的AccountIdEquipmentType

当且仅当您确定应该有一个元素时,您可以返回Single

ArmorModel myOneAndOnlyArmorModel = matchingArmorModels.Single();
return myOneAndOnlyArmorModel;

如果您认为可能没有匹配ArmorModel,请使用SingleOrDefault。如果您认为可能有多个匹配ArmorModels返回完整序列,或返回第一个匹配的序列:FirstOrDefault

最后:可能是一个EquipmentModel不属于一个ArmorModel,但它可以属于几个ArmorModel。在这种情况下,一对多关系是many-to-many relation, which is described here

在Fluent API中配置一对多

如果您使用entity framework code first conventions,则无需明确定义一对多关系。实体框架将自动检测。

但是,如果要为外键或导航属性使用非标准名称,就像使用EquipmentModel中的外键一样,则必须帮助实体框架。

最简单的方法是在覆盖DbContext.OnModelCreating

时对此进行描述

每个EquipmentModel都属于一个ArmorModel,使用外键EquipmentId。每个ArmorModel都在属性EquipmentModels

中有许多EquipmentModel
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<EquipmentModel>()
       .HasRequired(equipmentModel => equipmentModel.ArmorModel)
       .WithMany(armorModel => armorModel.EquipmentModels)
       .HasForeignKey(equipmentModel => equipmentModel.EquipmentId);
}

或者你可以类似地配置ArmorModel:ArmorModel有许多EquipmentModelss。每个EquipmentModel都属于一个ArmorModel,使用外键EquipmentId。

modelBuilder.Entity<ArmorModel>()
    .HasMany(armorModel => armorModel.EquipmentModels)
    .WithRequired(equipmentModel => equipmentModel.ArmorModel)
    .HasForeignKey(equipmentModel => EquipmentId);

请务必不要同时指定两者。毕竟:只有一对一的关系