实体框架核心包括逐个字符串

时间:2017-12-06 08:11:35

标签: c# sql-server entity-framework asp.net-core entity-framework-core

我已经构建了一个小应用程序,它使用来自第三方的SQL Server数据库的数据。他们有这样的结构将他们的对象链接在一起:

一个名为'Objecten'的表,它保存最常见的数据,如ObjectId,Unid,Name,Description,......所有这些对象都有一个'ObjectType'列,它是一个VARCHAR /字符串,例如:'例如' VRIJ1OBJECT','VRIJ2OBJECT','HARDWARE'

相关数据存储在一个包含以下名称的表格中:“VRIJ1OBJECTEN”,“VRIJ2OBJECTEN”,“HARDWARE”。

在我的(ASP.NET Core)代码中,我使用的是EF Core 2.0。我尝试返回一个(分页的)对象列表及其相关数据,但我不知道如何使用字符串名称加入表。

所以目前,当我为页面返回10个对象时,我正在进行11次查询。 1获取10个对象,每个对象1个,根据其ObjectType从表中获取相关数据。这大约需要3秒钟,而1个查询只需要大约200毫秒。 API应该更快。

一些代码:

private async Task<(List<ObjectViewModel> objecten, int totaalAantalObjecten)> _getTopDeskObjectenAsync(string q = null, string sort = "Naam-", int take = 0, int skip = 0, string e = null, bool qall = false, string categorie = "", RolstoelZoekenVM rolstoelZoekenVm = null, bool aotCategorie = false, AotZoekenVM aotZoekenVm = null)
{
    var objecten = await _db.Object.Where(o => o.Status != -1).ToListAsync();

    ... (paging, filtering, ...) ...

    var returnObjecten = new List<ObjectViewModel>();
    foreach (var o in objecten){
        returnObjecten.Add(await _dbObjectToViewModelAsync(o));
    }
    return (returnObjecten, totalCount);
}

private async Task<ObjectViewModel> _dbObjectToViewModelAsync(TopDeskDatabase.Object o)
{
    var vrijObject = await GetDbVrijObjectAsync(o);
    return new ObjectViewModel
    {
        ... filling up the VM properties ...
    }
}

// THIS should be done by the SQL Server in the query
public async Task<IVrijobject> GetDbVrijObjectAsync(TopDeskDatabase.Object o)
{
    switch(o.Type.ToLower())
    {
        case "vrij1object":
                return await _db.Vrij1object.FirstOrDefaultAsync(d => d.Objectid == o.Unid);
            case "vrij2object":
                return await _db.Vrij2object.FirstOrDefaultAsync(d => d.Objectid == o.Unid);
            case "vrij3object":
                return await _db.Vrij3object.FirstOrDefaultAsync(d => d.Objectid == o.Unid);
            case "vrij4object":
                return await _db.Vrij4object.FirstOrDefaultAsync(d => d.Objectid == o.Unid);
            case "vrij5object":
                return await _db.Vrij5object.FirstOrDefaultAsync(d => d.Objectid == o.Unid);
            case "hardware":
                return await _db.Hardware.FirstOrDefaultAsync(d => d.Objectid == o.Unid);
            case "inventaris":
                return await _db.Inventaris.FirstOrDefaultAsync(d => d.Objectid == o.Unid);
            case "telefonie":
                return await _db.Telefonie.FirstOrDefaultAsync(d => d.Objectid == o.Unid);
        }

        throw new InvalidDataException($"Object '{o.RefNaam}' is van type '{o.Type}', welke niet VRIJxOBJECT, INVENTARIS, TELEFONIE of HARDWARE is!");
}

1 个答案:

答案 0 :(得分:0)

您可以为每种可能的返回类型创建一个LEFT OUTER JOIN的查询,为每种类型存储生成的对象,然后通过强制转换为IVrijobject来压缩数据客户端。

请参阅以下示例,其中包含2种类型(注意我使用EF6创建了它,因此EF Core可能需要进行一些更改)

public class ObjectModel
{
    public int ID { get; set; }

    public int ObjectTypeID { get; set; }

    [StringLength(10)]
    public string ObjectTypeDiscriminator { get; set; }
}

public interface IObjectTypeModel
{
    int ID { get; set; }
    string Data { get; set; }
}

// ObjectTypeDiscriminator = "Type1"
public class ObjectType1Model : IObjectTypeModel
{
    public int ID { get; set; }

    [StringLength(100)]
    public string Data { get; set; }
}

// ObjectTypeDiscriminator = "Type2"
public class ObjectType2Model : IObjectTypeModel
{
    public int ID { get; set; }

    [StringLength(100)]
    public string Data { get; set; }
}

class DbC : DbContext
{
    public DbC()
    {
    }

    public DbSet<ObjectModel> Objects { get; set; }
    public DbSet<ObjectType1Model> Type1Objects { get; set; }
    public DbSet<ObjectType2Model> Type2Objects { get; set; }


    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

测试程序

class Program
{
    static void Main(string[] args)
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<DbC>());

        // initialize some test data
        using (var db = new DbC())
        {
            var t1_1 = db.Type1Objects.Add(new ObjectType1Model { Data = "Object T1 1" });
            var t1_2 = db.Type1Objects.Add(new ObjectType1Model { Data = "Object T1 2" });
            var t2_1 = db.Type2Objects.Add(new ObjectType2Model { Data = "Object T2 1" });
            var t2_2 = db.Type2Objects.Add(new ObjectType2Model { Data = "Object T2 2" });
            db.SaveChanges();
            db.Objects.Add(new ObjectModel { ObjectTypeID = t1_1.ID, ObjectTypeDiscriminator = "Type1" });
            db.Objects.Add(new ObjectModel { ObjectTypeID = t1_2.ID, ObjectTypeDiscriminator = "Type1" });
            db.Objects.Add(new ObjectModel { ObjectTypeID = t2_1.ID, ObjectTypeDiscriminator = "Type2" });
            db.Objects.Add(new ObjectModel { ObjectTypeID = t2_2.ID, ObjectTypeDiscriminator = "Type2" });
            db.SaveChanges();
        }
        // fresh context for query demonstration
        using (var db = new DbC())
        {
            db.Database.Log = x => Console.WriteLine(x);
            var result =
                from o in db.Objects
                join t1 in db.Type1Objects on new { Discriminator = o.ObjectTypeDiscriminator, ObjectTypeID = o.ObjectTypeID } equals new { Discriminator = "Type1", ObjectTypeID = t1.ID } into types1
                join t2 in db.Type2Objects on new { Discriminator = o.ObjectTypeDiscriminator, ObjectTypeID = o.ObjectTypeID } equals new { Discriminator = "Type2", ObjectTypeID = t2.ID } into types2
                from t1 in types1.DefaultIfEmpty()
                from t2 in types2.DefaultIfEmpty()
                select new
                {
                    Obj = o,
                    T1 = t1,
                    T2 = t2,
                };
            foreach (var item in result)
            {
                // only one concrete type will have a non-null value
                var T = (IObjectTypeModel)item.T1 ?? item.T2;
                Console.WriteLine("{0,20}{1,20}", item.Obj.ObjectTypeDiscriminator, T.Data);
            }
        }

        Console.ReadKey();
    }
}

输出:

Opened connection at 06.12.2017 10:25:34 +01:00

SELECT
    [Extent1].[ID] AS [ID],
    [Extent1].[ObjectTypeID] AS [ObjectTypeID],
    [Extent1].[ObjectTypeDiscriminator] AS [ObjectTypeDiscriminator],
    [Extent2].[ID] AS [ID1],
    [Extent2].[Data] AS [Data],
    [Extent3].[ID] AS [ID2],
    [Extent3].[Data] AS [Data1]
    FROM   [dbo].[ObjectModels] AS [Extent1]
    LEFT OUTER JOIN [dbo].[ObjectType1Model] AS [Extent2]
        ON ([Extent1].[ObjectTypeDiscriminator] = N'Type1') AND ([Extent1].[ObjectTypeID] = [Extent2].[ID])
    LEFT OUTER JOIN [dbo].[ObjectType2Model] AS [Extent3]
        ON ([Extent1].[ObjectTypeDiscriminator] = N'Type2') AND ([Extent1].[ObjectTypeID] = [Extent3].[ID])


-- Executing at 06.12.2017 10:25:34 +01:00

-- Completed in 6 ms with result: SqlDataReader



               Type1         Object T1 1
               Type1         Object T1 2
               Type2         Object T2 1
               Type2         Object T2 2
Closed connection at 06.12.2017 10:25:34 +01:00

正如您所看到的,生成的查询将为每个对象使用单独的结果列,这可能会或可能不会比基于鉴别器合并结果的效率低(我没有调查此方面)。如果您的Unid足够独特,那么您不需要加入条件的Discriminator部分。