【问题标题】:Where to put methods (in entity or repo)将方法放在哪里(在实体或回购中)
【发布时间】:2015-04-18 02:48:44
【问题描述】:

我有两个 EF 生成的类。

public partial class admin
{
    public admin()
    {
        this.user = new HashSet<user>();
    }
    public int id { get; set; }//PK
    //Other properties omitted for brevity
    public virtual ICollection<user> user { get; set; }
}

public partial class user
{  
    public string username { get; set; }//PK
    public string passwd { get; set; }
    public int admin_id { get; set; }//FK
    //Other properties omitted for brevity
    public virtual admin admin { get; set; }
}

用户使用 FK admin_id 属于管理员。如果 admin_id 和 username 相等,则用户是管理员。示例:用户“梅西”、“内马尔”、“苏亚雷斯”和“123”都有 admin_id 123。所以用户“123”是管理员。 (也许不是最好的方法,但与问题无关。)

由于这两个类是 EF 自动生成的并且将来可以更改,我有另一个具有相同命名空间但在不同文件夹中的部分用户类(因此保留方法):

public partial class user
{
    public bool isAdmin()
    {
        return admin_id.ToString().Equals(username);
    }
}

我也有这个用户存储库:

public class EFUserRepo : IUserRepo
{
    private Entities context = new Entities();
    public IQueryable<user> Users { get { return context.user; } }
    public user getUserByPK(string username)
    { 
        return context.user.Find(username);
    }
    public user deleteUser(string username){ return null; }//Yet to implement
    public bool saveUser(user user){ return false; }//Yet to implement
}

我想用另一种方法来获取给定用户的 useradmin,如下所示:

public user getUserAdmin(string username){ }//Note the return type is user, not admin!



我的问题是,我该把这个方法放在哪里?
我可以像这样把它放在 EFUserRepo 中:

public user getUserAdmin(string username)
{
    user user = getUserByPK(username);
    if (user == null) return null;
    return context.user.Find(user.admin_id);            
}

public user getUserAdmin(user user)//Not relevant for question, but might be insightful for others
{
    return getUserAdmin(user.username);
}

在我的控制器中调用这个:

user adminUser = repo.getUserAdmin(loggedOnUser.username);//or use repo.getUserAdmin(loggedOnUser) for same result.

或者我可以像这样把它放在部分用户类中:

public user getUserAdmin()
{
    return this.admin.user.Where(x => x.isAdmin()).FirstOrDefault();
    //Due to DB setup always returns 1 user.
}

在我的控制器中调用这个:

user adminUser = loggedOnUser.getUserAdmin();



我绝对不知道什么是最好的方法。
如果我也想做一个方法,比如:

public admin getAdmin(string username){ }//Note the return type is admin here, not user

然后我可以将它添加到我的用户存储库中:

public admin getAdmin(string username)
{
    user user = getUserByPK(username);
    if (user == null) return null;
    return user.admin;
    //return context.admin.Find(user.admin_id);//Also works, but is it best practise to access the admin collection from within the userrepo, think not
    //return new EFAdminRepo().getAdminByPK(user.admin_id)//Also works, but seams really ugly
}

public admin getAdmin(user user)//Again, not relevant for question, but might be insightful for others
{
    return getAdmin(user.username);
}

在我的控制器中调用这个:

admin admin = repo.getAdmin(loggedOnUser.username);//or use repo.getAdmin(loggedOnUser) for same result.

或者我可以像这样把它放在部分用户类中:

public admin getAdmin()
{
    return this.admin.user.Where(x => x.isAdmin()).First().admin;
}

在我的控制器中调用这个:

admin admin = loggedOnUser.getAdmin();



真正的问题可能是,我用吗

ObjectX obj = repo.methodForObtainingObjectX(entity.params);

ObjectX obj = entity.methodForObtainingObjectX();

【问题讨论】:

  • 您应该将这些方法保存在存储库中。将实体保持为模型总是好的,并且保持尽可能简单。你永远不应该在实体中包含业务逻辑,除非除此之外别无他法。

标签: c# entity-framework asp.net-mvc-3 oop design-patterns


【解决方案1】:

首先,这是作为主键的名称吗?帮自己一个忙:现在就摆脱那个可憎的东西。一旦你说服自己没关系,你就会遇到这样的情况:有两个 Joe Smith,或者 Jane Doe 会结婚,你会想修改她的姓名字段,但不能,因为这是一个关键。只需给自己另一个 int 字段用作键,每条记录只需多几个字节,而且总是正确的。

所以,除了数据库中的字段或导航属性之外,不要让人们告诉您不要向模型类添加任何逻辑。模型一直代表像IsAdmin 这样的简单事物,而无需求助于单独的类。只需拍打[NotMapped] 就可以了。

关于当逻辑变得更复杂时将这些方法放在哪里,我问自己这个问题:该方法生成另一次访问数据库的可能性有多大(并考虑到可能的延迟加载)?如果很有可能,它可能应该进入回购;如果您希望让它总是生成另一个针对数据库的查询,总是把它放在repo中。如有疑问,请将其放入 repo。

现在有个坏消息:这些都不适用于您的情况。

首先,您已经在User 上拥有了与getAdmin 等效的方法 ...这正是User.Admin 的作用,不是吗?只需使用您为此目的定义的关系即可。

其次,如果管理员总是有关联的用户,您的模型中缺少一个关系:您需要,例如,User 类型的 Admin.Self 来表示这个一对一关系,然后只需担心获得Admin (通过AdminRepo.FindByKey()User.Admin 来自先前获取的User)......一旦你有一个AdminAdmin.Self 总是会让你得到她的相关@ 987654335@属性。

在考虑任何抽象(如 repos)之前,确定实际模型中的所有属性和关系,以便您的 repos 可以执行它们应该执行的简单 CRUD 操作。

【讨论】:

  • 我有一个带有 PK id(int) 的 person 表。 user 表有一个 person_id FK...但它没有显示在我的“答案设置”中;)使用[NotMapped] 是一个不错的选择,没有想过...(必须在一个单独的部分文件,因为自动生成的文件将被覆盖) - 关于您将这些方法放在哪里的答案,我正在寻找设计规则。你的很有意义!只需将 EF 生成的类用作 POCO,如果方法不需要访问数据库,则将其放在模型本身上,如果需要或可能需要访问数据库,则将其放入 repo。
  • 关于模型中缺少的关系,您是对的!这是我们选择的设置,也许不是最好的,但它对我们有用……我明白你要解释的内容!您对 getAdmin 方法和 user.Admin 的看法也是正确的!我的问题是关于(无意打孔!)如果由于某种原因导航属性不存在怎么办。然后我必须使用选项2。再次,您提示在哪里放置方法,在 repo 中(当需要 db 行程时)以及何时在模型中(当不需要 db 行程时)非常有帮助!
【解决方案2】:

根据您的解释(以及您使用实体框架等 ORM 的事实),保留 EF 对象的任何数据检索/修改功能是正确的。 EF 对象被称为“模型”,因为它们是模型,并且他们唯一需要做的就是 BE 模型并将数据库结构表示为 POCO 类。

因此将一些逻辑放入其中(更不用说您必须修改 *.tt 文件以使此逻辑持久并且不会在每次模型更新时松散)不是一个好主意。所以正如@Jenish 已经说过的,保持模型结构尽可能简单。

另一方面,你的 repo 应该提供所有必要的数据访问方法,所以你必须把这个方法放在 repo 中。

更新 1

所以关于你的下一个问题,关于放置在哪里以及如何获得用户管理员。我认为如果您使用存储库模式,最好通过“主题”将其分解,因此您将拥有 userrepo、adminrepo 等。然后,根据逻辑归属,一些方法将转到 userrepo,一些到 adminrepo 等等。

但通常,首先,您必须做出决定,您是在前端使用 DB 模型,还是有单独的视图模型类仅用于视图渲染目的。如果第一个是真的,我个人认为没什么大不了的,如果你只是通过导航属性返回用户的管理员。在任何情况下,您都使用 DB 模型类对象作为容器。因此,我认为直接将其作为用户类的导航属性返回,或者通过专用的存储库方法返回,不会有太大的改变。

另一方面,如果您将引入视图模型类并在视图模型 db 模型对象之间进行一些转换,那么在专用存储库上停止所有场景是有意义的。

无论如何,我敢肯定,每个开发人员都必须始终牢记的主要原则是“保持简单”。如果我不能 100% 确定我真的需要它,我个人不会增加任何复杂性。

【讨论】:

  • EF 生成部分类。我将这些类与 edmx 文件放在一个名为 dbEntites 的文件夹中。我有另一个名为 dbEntitiesMethods 的文件夹,其中我使用相同的命名空间放置了自己制作的部分类。这些类包含方法。因此,当由于 db 结构发生变化而必须生成新模型时,它们不会迷失方向。但是,保留生成的模型仅用作 POCO 类是有意义的,并且是设计中的明确选择,我觉得这很有帮助...
  • 但是方法 isAdmin() 会去哪里?(从我自己制作的部分用户类)到 EFUserRepo 我猜:public bool isAdmin(string username) 然后在控制器中使用 bool isAdmin = repo.isAdmin (loggedOnUser.username)
  • 是的,你对部分类是正确的。但我认为最好使用部分类进行模型扩展,而不是在里面添加数据访问和修改逻辑。关于 IsAdmin 属性,我认为就您的“IsAdmin 逻辑”而言,它独立于数据访问并且仅包含一些基本的逻辑检查,将它放在您的模型中(部分类的第二部分)就可以了,它会在那里,我肯定不会违反任何模式或方法。
【解决方案3】:

所以它会在存储库中。 还有一个问题(或者我应该为此开始一个新话题吗?)

在 EFUserRepo 中给定这个方法

public admin getAdmin(string username)
{
    user user = getUserByPK(username);
    if (user == null) return null;
    return user.admin;
    //return context.admin.Find(user.admin_id);//Also works, but is it best practise to access the admin collection from within the userrepo, think not
    //return new EFAdminRepo().getAdminByPK(user.admin_id)//Also works, but seams really ugly
}

我可以在控制器(或其他任何地方)中执行此操作

选项 1:

admin admin = EFUserRepo().getAdmin(loggedOnUser.username);

我可以使用语句“return user.admin;”在 EFUserRepo 中,因为 admin 是用户的导航属性,但是如果由于某种原因导航属性不存在怎么办。然后我将不得不使用注释掉的语句之一。

  • return context.admin.Find(user.admin_id);
  • return new EFAdminRepo().getAdminByPK(user.admin_id);

而且我发现这些陈述非常丑陋,这可能不是最佳实践? (请注意,这些语句位于 user 存储库中!) 一种解决方案是从 EFUserRepo 中删除 getAdmin() 方法并使用 EFAdminRepo,这样我就可以在我的控制器中执行此操作:

选项 2:

user adminUser = EFUserRepo().getUserAdmin(loggedOnUser.username);
admin admin = EFAdminRepo().getAdminByPk(adminUser.admin_id);

我认为 option2 是正确的方法吗?请评论..

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-14
    相关资源
    最近更新 更多