C # абстрактный базовый класс для общих столбцов в LINQ - PullRequest
8 голосов
/ 07 октября 2010

Это то, что я имею до сих пор

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();
        }
    }
}

Моя проблема в том, что отображение не работает:

Элемент данных 'System.Guid [или System.Int32] Id' типа 'ИКС' не является частью отображения для типа 'ИКС'. Член выше корень иерархии наследования?

Прежде чем пытаться отобразить атрибуты, я получил это вместо:

Не удалось найти ключевой элемент 'Id' ключа «Id» для типа «X». Ключ может быть неправильным или поле или свойство на «X» имеет изменились имена.

Я попытался изменить K на Guid, и это работает, но почему? Я не понимаю, как здесь происходит типичная типизация

Я не совсем уверен, что мне на самом деле тоже нужен Интерфейс, я действительно не помню, зачем я его добавил.

Итак, вопрос будет такой: как я могу создать такой класс, как эта работа? Я хочу, чтобы я мог получить доступ к обычно называемому PK (Id), который всегда имеет тип K [который является Guid или Int32], и рефакторинг основных функций, таких как выбор и удаление, по Id

Спасибо!

EDIT:

это работает

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();
        }
    }
}

То, что я хочу, было бы в основном таким же, заменив Guid на K и сделав класс BaseEntity (чтобы я мог использовать один и тот же класс для Int32 и Guid PK

1 Ответ

8 голосов
/ 07 октября 2010

То, что вы хотите сделать, не будет работать с 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 равен «1» - это означает, что если VehicleType из базы данных равен «1», то он создаст подкласс Motorcycle. Вы применяете один атрибут [InheritanceMapping] к базовому классу для каждого подкласса , который вы поддерживаете.

С точки зрения пуриста, это нарушение ОО, поскольку базовый класс знает о своих подклассах. Эта странность обычно отталкивает людей от того, как 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();
    }
}

Обратите внимание, что у меня нет атрибута [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; }
}

Обратите внимание, что свойство Id в этом классе имеет атрибут [Column], и я переопределяю абстрактные свойства. Итак, я проверил, что работает.

Сказав это, есть несколько причин, по которым я подвергаю сомнению ваш текущий дизайн. Сначала у вас есть методы доступа к данным как часть вашей сущности, и многие люди (включая меня) считают это нарушением разделения интересов. Вы можете ввести здесь шаблон Repository и иметь репозиторий для каждой сущности - сделать этот репозиторий универсальным, основываясь на типе и ключе. Другая вещь, которая странна в вышеупомянутом подходе, состоит в том, что BaseEntity имеет свойство Id, а подкласс (в моем примере класс Contact) также имеет свойство Id (чтобы сделать LINQ To SQL счастливым). Пришлось сделать свойство Id в базовом классе абстрактным, а затем переопределить его в конструкторе. Это нарушает СУХОЙ, потому что мне придется делать это для каждой сущности. Но это еще одна вещь, которая является результатом обручей, через которые вы должны перепрыгнуть, чтобы осчастливить LINQ to SQL. Но это будет работать! :)

...