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

bev*_*qua 8 .net c# linq sql-server 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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

类型为"X"的数据成员"System.Guid [或System.Int32] Id"不是"X"类型映射的一部分.成员是否位于继承层次结构的根目录之上?

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

无法在"X"类型上找到键'Id'的关键成员'Id'.密钥可能有误或"X"上的字段或属性更改了名称.

我尝试将K换成Guid并且它有效,但为什么呢?我不知道泛型输入是一个问题

我不完全确定我实际上也需要接口,我真的不记得为什么我添加了它.

所以,问题是:我怎样才能创作出像这样的作品呢?我想要它所以我可以访问一个通常命名的PK(Id),它总是具有类型K [Guid或Int32],并重构基本函数,如Id的select和delete

谢谢!

编辑:

这很有效

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我想要的基本上是相同的,用K替换Guid并使类BaseEntity(所以我可以使用相同的类用于Int32和Guid PKs

Ste*_*tti 9

您要做的事情不适用于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
}
Run Code Online (Sandbox Code Playgroud)

为了使这个继承在LINQ to SQL中工作,你必须将它应用于[InheritanceMapping]基类,你还必须有一个鉴别器列(例如,上例中的VehicleType).请注意,InheritanceMapping中的代码为"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();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,区别在于我没有[Column]Id属性的任何属性.还要注意我把它作为摘要.实现类看起来像这样:

[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; }
}
Run Code Online (Sandbox Code Playgroud)

请注意Id,此类[Column]属性确实具有属性,并且我正在覆盖抽象的属性.所以我验证了有效.

话虽如此,我还有几个原因可以质疑您当前的设计.首先,您已将数据访问方法作为实体的一部分,许多人(包括我自己)认为这违反了"关注点分离".您可以在此处引入Repository模式,并为每个实体创建一个存储库 - 根据类型和密钥使该存储库具有通用性.关于上述方法的另一个奇怪之处是它BaseEntity有一个Id属性和子类(在我的例子中是Contactclass)也有Id的属性(使LINQ To SQL开心).必须在基类抽象中创建Id属性,然后在实现者中覆盖它.这违反了DRY,因为我必须为每个实体执行此操作.但这是另一件事,因为你必须跳过这个让LINQ to SQL开心的箍.但它奏效!:)