【问题标题】:How to make a derived class use a property of a base class?如何使派生类使用基类的属性?
【发布时间】:2013-11-22 16:40:41
【问题描述】:

一般的想法是从创建一个控件开始,它可以与任何数据库对象一起工作。 数据库通过 LINQ to SQL 访问,数据库对象是从现有数据库自动生成的。

为了使控件与这些对象中的任何一个一起工作,我使用了一个基本接口,所有自动生成的对象都从该接口继承。所以控件实际上可以与该界面一起使用。

这个接口首先应该提供:

  1. 获取对象标识符(主键)的能力
  2. 如果通过对象实例编译和调用,则返回此标识符的表达式

所以这看起来像:

public interface IEntity<TEntity, TEntityId>
    where TEntity : IEntity<TEntity, TEntityId>
    where TEntityId : IEquatable<TEntityId>
{
    Expression<Func<TEntity, TEntityId>> GetIdExpression();
    TEntityId EntityId { get; }
}

那么数据库对象的类定义如下所示:

//let it be the auto-generated part:
public partial class Entity1
{
    public Guid UId { get; set; }
}
//and the manual part:
public partial class Entity1 : IEntity<Entity1, Guid>
{
    public Expression<Func<Entity1, Guid>> GetIdExpression()
    {
        return (Expression<Func<Entity1, Guid>>)(se => se.UId);
    }
    public Guid EntityId
    {
        get { return this.UId; }
    }
}

我们可以测试一下:

var e1 = new Entity1();
e1.UId = Guid.NewGuid();
Console.WriteLine(e1.UId); 
Console.WriteLine(e1.GetIdExpression().Compile()(e1));

这两行的输出是相等的。没关系。

下一个想法是,所有数据库对象的大部分都有int 标识符,名称为Id。最好避免编辑它们中的每一个,即不要编写一个相同的代码来实现IEntity&lt;TEntity, TEntityId&gt;

就我需要指定这个接口的实现而言,我需要一个类。 现在我结束了

public class IntEntity<TEntity> : IEntity<TEntity, int>
       where TEntity : IntEntity<TEntity>
{
    public int Id { get; set; }

    public Expression<Func<TEntity, int>> GetIdExpression()
    {
        return (Expression<Func<TEntity, int>>)(e => e.Id);
    }

    public int EntityId
    {
        get { return this.Id; }
    }
}

然后数据库对象类将从它派生:

//let it be the auto-generated part:
public partial class Entity2
{
    public int Id { get; set; }
}
//and the manual part:
public partial class Entity2 : IntEntity<Entity2>
{
}

现在很明显,Entity2 中的Id 确实隐藏了IntEntity&lt;TEntity&gt; 中的Id。同样的测试也证明了这一点:

var re = new Entity2();
re.Id = 5;
Console.WriteLine(re.Id); //5
Console.WriteLine(re.GetIdExpression().Compile()(re)); //0

所以这个解决方案是错误的...... 我也尝试将IntEntity&lt;TEntity&gt;抽象化,但显然失败了,因为Entity2Id是在自动生成的部分中定义的,而我无法使其override基础的abstractId类。

我可以做些什么来使所有带有int Id 的数据库对象都使用基类IntEntity&lt;TEntity&gt; 中的Id。或者,可能,反之亦然...

这里的重点是GetIdExpression 返回的表达式应该是e =&gt; e.Id 的形式,因为这个表达式稍后会在 LINQ to SQL 查询中使用,所以 LINQ 应该能够将它转换为正确的 SQL 查询.

【问题讨论】:

  • 那么为什么您需要Entity2IntEntity&lt;Entity2&gt; 中的id 属性,而不仅仅是其中一个?
  • 作为求解的变体 - 添加到 Id 中的 IntEntity&lt;Entity2&gt; 关键字 virtualId 中的 Entity2 override
  • @Grundy Id in Entity2 由 LINQ to SQL 自动生成。我无法修改它的声明。
  • @Grundy in IntEntity&lt;Entity2&gt; 我需要Id 才能提供IEntity&lt;TEntity, int&gt; 的实现。

标签: c# generics inheritance linq-to-sql expression


【解决方案1】:

我不确定这正是你想要的,但你可以尝试这样做

public abstract class IntEntity<TEntity> : IEntity<TEntity, int>
   where TEntity : IntEntity<TEntity>
{
    public abstract Expression<Func<TEntity, int>> GetIdExpression();
    public abstract int EntityId
    {
        get;
    }
}

public partial class Entity2
{
    public int Id { get; set; }
}
//and the manual part:
public partial class Entity2 : IntEntity<Entity2>
{
    public override int EntityId
    {
        get { return this.Id; }
    }

    public override Expression<Func<Entity2, int>> GetIdExpression()
    {
        return (Expression<Func<TEntity, int>>)(e => e.Id);
    }
}

事实上,如果没有override virtual,您将无法从基类中的方法访问派生类中的字段
在上面的示例中实际上等于您的第一个示例

更新

作为解决方法,您可以像这样在派生和基类中添加设置 id 的方法 SetId

public class IntEntity<TEntity> : IEntity<TEntity, int>
   where TEntity : IntEntity<TEntity>
{
    public int Id { get; set; }

    public Expression<Func<TEntity, int>> GetIdExpression()
    {
        return (Expression<Func<TEntity, int>>)(e => e.Id);
    }

    public int EntityId
    {
        get { return this.Id; }
    }
}

//let it be the auto-generated part:
public partial class Entity2
{
    public int Id { get; set; }
}
//and the manual part:
public partial class Entity2 : IntEntity<Entity2>
{
    public void SetId(int id)
    {
        Id = id;
        base.Id = id;
    }
}

var re = new Entity2();
re.SetId(5);
Console.WriteLine(re.Id); //5
Console.WriteLine(re.GetIdExpression().Compile()(re)); //5

【讨论】:

  • @horgh 你是什么意思?
  • 由于GetIdExpression 必须只有e =&gt; e.Id 形式,因为在数据库中没有EntityId,只有Id...因此不会解析表达式e =&gt; e.EntityId通过 LINQ to SQL
  • 现在您的代码建议在每个数据库对象中覆盖IntEntity&lt;TEntity&gt; 的抽象成员。实际上,这与我使用Entity1 的第一个示例相同,只是它没有实现IEntity&lt;TEntity, int&gt;,而是导致抽象成员的覆盖。事实上在这个版本中,IntEntity&lt;TEntity&gt; 类是绝对没用的。
猜你喜欢
  • 2016-04-29
  • 1970-01-01
  • 1970-01-01
  • 2016-12-25
  • 2016-04-21
  • 1970-01-01
  • 2011-10-03
  • 2023-03-18
  • 2017-07-05
相关资源
最近更新 更多