LINQ 中常见列的 C# 抽象基类

标签 c# .net sql-server linq abstract-class

这是我目前的情况

using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Text;

namespace Firelight.Business
{
    public interface IBaseEntity<K>
    {
        K Id { get; }
    }

    /// <summary>
    /// Base business database connection object, primary key is int
    /// </summary>
    /// <typeparam name="T">Table name</typeparam>
    public abstract class BaseEntity<T> : BaseEntity<T, Guid> where T : class, IBaseEntity<Guid>
    {
    }

    /// <summary>
    /// Base business database connection object
    /// </summary>
    /// <typeparam name="T">Table name</typeparam>
    /// <typeparam name="K">Primary key type</typeparam>
    public abstract class BaseEntity<T,K> : IBaseEntity<K> where T : class, IBaseEntity<K>
    {
        // Avoids having to declare IBaseConnection at partial class level
        [Column(Name = "Id", CanBeNull = false, IsPrimaryKey = true, IsDbGenerated = true)]
        public K Id { get; set; } // { return default(K); }

        public static Table<T> Table 
        {
            get { return LinqUtil.Context.GetTable<T>(); } 
        }

        public static T SearchById(K id)
        {
            return Table.Single<T>(t => t.Id.Equals(id));
        }

        public static void DeleteById(K id)
        {
            Table.DeleteOnSubmit(SearchById(id));
            LinqUtil.Context.SubmitChanges();
        }
    }
}

我的问题是映射不起作用:

Data member 'System.Guid [or System.Int32] Id' of type 'X' is not part of the mapping for type 'X'. Is the member above the root of an inheritance hierarchy?

在尝试映射属性之前,我得到了这个:

Could not find key member 'Id' of key 'Id' on type 'X'. The key may be wrong or the field or property on 'X' has changed names.

我尝试将 K 更改为 Guid,但它有效,但为什么呢?我在这里看不出泛型类型是个问题

我也不完全确定我是否真的需要这个接口(interface),我真的不记得我为什么要添加它。

因此,问题是:我怎样才能让这样的类(class)发挥作用?我想要它,这样我就可以访问一个通常命名的 PK (Id),它始终具有类型 K [即 Guid 或 Int32],并重构基本功能,如按 Id 进行选择和删除

谢谢!

编辑:

这行得通

using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;

namespace Firelight.Business
{
    public interface IBaseEntity<K>
    {
        K Id { get; set; }
    }

    /// <summary>
    /// Base business database connection object
    /// </summary>
    /// <typeparam name="T">Table name</typeparam>
    public abstract class BaseEntity<T> : IBaseEntity<Guid> where T : class, IBaseEntity<Guid>
    {
        // Avoids having to declare IBaseConnection at partial class level
        public Guid Id { get; set; }

        public static Table<T> Table 
        {
            get { return LinqUtil.Context.GetTable<T>(); } 
        }

        public static T SearchById(Guid id)
        {
            return Table.Single<T>(t => t.Id.Equals(id));
        }

        public static void DeleteById(Guid id)
        {
            Table.DeleteOnSubmit(SearchById(id));
            LinqUtil.Context.SubmitChanges();
        }
    }
}

我想要的基本上是一样的,用 K 替换 Guid 并创建类 BaseEntity(这样我就可以对 Int32 和 Guid PK 使用相同的类

最佳答案

您要执行的操作不适用于 LINQ to SQL。要在 LINQ to SQL 中使用继承,您必须在基类上使用 [InheritanceMapping] 属性。假设您有一个名为 Vehicle 的基类和一个名为 Motorcycle 的子类:

[InheritanceMapping(Type = typeof(Motorcycle), IsDefault = true, Code = 1)]
[Table]
public class Vehicle
{
    [Column]
    public string Make { get; set; }

    [Column]
    public string Model { get; set; }

    [Column(IsDiscriminator = true, Name="VehicleTypeId")]
    public VehicleType VehicleType { get; set; }
}

public class Motorcycle : Vehicle
{
    // implementation here
}

为了使这种继承在 LINQ to SQL 中起作用,您必须将 [InheritanceMapping] 应用于基类并且您还必须有一个鉴别器列(例如,上例中的 VehicleType)。注意 InheritanceMapping 中的 Code 是“1”——这意味着如果数据库中的 VehicleType 是“1”,那么它将创建 Motorcycle 子类。您在基类上为您支持的每个 子类应用一个[InheritanceMapping] 属性。

从纯粹主义者的角度来看,这是对 OO 的违反,因为基类知道它的子类。这种古怪之处通常会使人们对 LINQ to SQL 如何实现继承有点反感。但是你有它。

更新 这有效:

public abstract class BaseEntity<T, K> : IBaseEntity<K> where T : class, IBaseEntity<K>
{
    public abstract K Id { get; set; }

    public static Table<T> Table
    {
        get { return context.GetTable<T>(); }
    }

    public static T SearchById(K id)
    {
        return Table.Single<T>(t => t.Id.Equals(id));
    }

    public static void DeleteById(K id)
    {
        Table.DeleteOnSubmit(SearchById(id));
        context.SubmitChanges();
    }
}

请注意,区别在于我在 Id 属性上没有任何 [Column] 属性。另请注意,我将其抽象化了。实现类如下所示:

[Table(Name = "dbo.Contacts")]
public class Contact : BaseEntity<Contact, int>
{
    [Column]
    public override int Id { get; set; }
    [Column]
    public string FirstName { get; set; }
    [Column]
    public string LastName { get; set; }
}

请注意此类中的Id 属性确实 有一个[Column] 属性并且我正在覆盖抽象属性。所以我验证了它是有效的。

话虽如此,我质疑您当前的设计有几个原因。首先,您将数据访问方法作为实体的一部分,许多人(包括我自己)认为这违反了关注点分离。您可以在此处引入存储库模式,并为每个实体创建一个存储库——根据类型和键使该存储库通用。上述方法的另一个奇怪之处是 BaseEntity 有一个 Id 属性,而子类(在我的例子中是 Contact 类)也有一个属性Id(使 LINQ To SQL 快乐)。必须使基类中的 Id 属性抽象,然后在实现者中重写它。这违反了 DRY,因为我必须为每个实体执行此操作。但这是另一件事,它是您必须跳过才能使 LINQ to SQL 快乐的箍的结果。但它起作用! :)

关于LINQ 中常见列的 C# 抽象基类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3877868/

相关文章:

javascript - 为什么我的值(value)四舍五入(向上或向下)

C# 和 LINQ - 任意语句而不是 let

sql - 优化 SQL 表的性能 - 索引

C# 将位图加载到 PictureBox 中 - 速度慢并且会阻塞 UI ..还有其他选择吗?

c# - 如何在不按住 Alt 的情况下显示下划线(快捷方式)?

c# - 如何将所有选定的文件名传递给 C# 应用程序?

.net - 在 .Net 桌面应用程序中,Scorm 内容究竟如何成为 'played'?

c# - 错误 : 40 - Could not open a connection to SQL Server

sql - 无法删除 Sql Azure 上的表

c# - 不是所有的代码路径都有返回值,我不知道该怎么办