扩展DataContext实体 - 在“child”类中使用InsertOnSubmit(this)

时间:2009-05-03 23:16:54

标签: c# linq-to-sql

我正在扩展这个DataContext实体,看起来像这样:

namespace Entities
{
    public class User
    {
        public Int32 Id { get; set; }
        public String Username { get; set; }
    }
}

..像这样:

public class User : Entities.User
{
    new public Int32 Id
    {
        get { return base.Id; }
    }


    public void Insert()
    {
        using (var dc = new DataContext())
        {
/*
The "this" keyword should match the type that InsertOnSubmit() expects.
And it does. But I get the following error:

System.NullReferenceException: {"Object reference not set to an instance
of an object."}
*/
            dc.Users.InsertOnSubmit(this); // Exception occurs here

            dc.SubmitChanges();
        }
    }
}

我正在使用自定义User类:

var u = new User { Username = "Test" };

u.Insert();

我没有得到的是:我已经实例化了这个类,为什么我得到一个NullReferenceException?


更新


扩展实体类:使用枚举器覆盖属性,同时仍然可以在DataContext实例上的Insert / UpdateDeleteOnSubmit方法上使用“this”关键字

enum AccessLevels
{
    Basic,
    Administrator
}


namespace Entities
{
    public class User
    {
        public Int32 Id { get; set; }
        public String Username { get; set; }
        public Int32 AccessLevel { get; set; }
    }
}

我如何扩展或改变上面的实体类并实现AcessLevels枚举器,替换AccessLevel属性? - 这不会改变实体类的签名,所以我能够在DataContexts上的Insert / UpdateDeleteOnSubmit方法上使用“this”关键字。

2 个答案:

答案 0 :(得分:4)

您不能通过继承以这种方式扩展LINQ-to-SQL实体类型 - 您应该使用partial class现有生成的实体添加额外的方法。因为LINQ-to-SQL支持继承(对于有区别的表等),它需要与已知实体类型匹配完全 - 而不是意外的子类。

namespace Entities {
    partial class User {
        /* your extra method(s) here */
    }
}

在上面,这是组合与designer.cs中的partial类来创建你的类型。

另一种方法(如果不能选择部分类)是通过扩展方法。

static class EntityExtensions {
    public static void SomeMethod(this User user) {...}
}

如果类型之间存在通用方法,则可以通过声明接口,使用 on 接口上的扩展方法,并使用部分类将接口添加到特定类型来执行此操作:

namespace Entities {
    partial class User : IFunkyInterface {
        /* interface implementation, if necessary */
    }
}

static class EntityExtensions {
    public static void SomeMethod(this IFunkyInterface obj)
    {...}
}

或者如果您需要知道类型:

static class EntityExtensions {
    public static void SomeMethod<T>(this T obj)
          where T : class, IFunkyInterface
    {...}
}

答案 1 :(得分:2)

重新进行枚举编辑(作为第二个答案添加以保持简单)...

首先 - 枚举和值之间是否有直接的1:1映射?例如,如果Basic为7且Administrator为12,则:

enum AccessLevels
{
    Basic = 7,
    Administrator = 12
}

然后将dbml中该属性的类型(通过设计器)从int更改为(完全限定的)枚举:Entities.AccessLevel。 LINQ-to-SQL支持枚举作为直接整数映射或直接字符串映射。

如果这不可能(更复杂的场景),您可以隔离存储(int)和面向对象(枚举)模型;将属性重命名为AccessLevelStorage(或您喜欢的任何其他内容),并在部分类中执行映射:

partial class User {
    public AccessLevel AccessLevel {
        get {
            switch(AccessLevelStorage) {
                case 1: return AccessLevelStorage.Foo;
                ... etc
                default: ...throw an exception?
            }
         }
         set {
            switch(value) {
                case AccessLevel.Foo: AccessLevelStorage = 1; break;
                ...etc
                default: ...throw an exception?
            }
         }
}

唯一需要注意的是,LINQ查询只能用于存储属性 - 而不是自定义映射属性。如果您在声明上下文的级别执行查询,则可以将存储属性的访问权限更改为internal - 但如果您在此程序集之外执行查询,则需要将其保留为public。您可能希望添加[Browsable(false)]以阻止它出现在UI模型中,但这就是它。