为模型创建通用的Save()方法

3

我有一个相对简单的系统,就本问题而言,它基本上包括三个部分:模型、仓库和应用程序代码。

核心是模型。我们使用一个简单的编造示例:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

在同一个项目中有一个通用的仓库接口。最简单的形式如下:
public interface IRepository<T>
{
    T Save(T model);
}

该接口的实现在一个单独的项目中,并使用StructureMap进行注入。为了简单起见:

public class PersonRepository : IRepository<Person>
{
    public Person Save(Person model)
    {
        throw new NotImplementedException("I got to the save method!");
        // In the repository methods I would interact with the database, or
        // potentially with some other service for data persistence.  For
        // now I'm just using LINQ to SQL to a single database, but in the
        // future there will be more databases, external services, etc. all
        // abstracted behind here.
    }
}

因此,在应用程序代码中,如果我想保存一个模型,我会这样做:
var rep = IoCFactory.Current.Container.GetInstance<IRepository<Person>>();
myPerson = rep.Save(myPerson);

这很简单,但感觉可以自动化很多。这种模式贯穿应用程序代码,因此我想做的是在所有模型上创建一个通用的Save(),它只是对上述应用程序代码的缩写调用。这样,我们只需要调用:

myPerson.Save();

但是我似乎无法找到一种方法来做到这一点。也许它看起来很简单,而我只是没有从正确的角度看待它。起初,我尝试创建一个空的ISaveableModel<T>接口,并打算让每个“可保存”的模型实现它,然后对于单个通用的Save()方法,我会在接口上添加一个扩展:

public static void Save<T>(this ISaveableModel<T> model)
{
    var rep = IoCFactory.Current.Container.GetInstance<IRepository<T>>();
    model = rep.Save(model);
}

但是它告诉我rep.Save(model)有无效的参数。看起来它没有像我希望的那样连接类型推断。我尝试使用一个BaseModel<T>类,让模型继承:

public class BaseModel<T>
{
    public void Save()
    {
        this = IoCFactory.Current.Container.GetInstance<IRepository<T>>().Save(this);
    }
}

但编译错误仍然存在。有没有办法实现我想要的效果?我对设计非常灵活,所以如果我的架构方法不正确,我可以退后一步并改变大局。


你使用的是哪个数据库库?Linq To SQL?Entities? - Oliver
在你的例子中,BaseModel<T> 没有实现 ISaveableModel<T>。你试过了吗? - agent-j
@Oliver:目前我在存储库实现中使用LINQ to SQL是为了简单起见,但在这种设计中,所有数据访问都抽象在存储库接口后面,这并不重要,对吧?一个总体的目标是严格将所有数据访问代码排除在核心域项目之外。我还没有找到一个很好地做到这一点的ORM(虽然我可能只是使用它们不正确)。 - David
@agent-j:嗯,我认为在这种情况下,这两个是逻辑上互斥的。如果一个有效,就没有理由使用另一个。我宁愿使用接口而不是继承来完成这个任务,但如果必须使用继承,则接口为空,扩展方法消失,因此就不会有接口了。 - David
它们可能需要一些编辑才能正常工作。我建议使用反射来循环遍历您的类型化对象中的字段。我这里没有它们,稍后我会发布它们。 - Oliver
显示剩余5条评论
2个回答

3
一个通用的扩展方法能解决它吗?
public static T Save<T>(this T current)
{
    var rep = IoCFactory.Current.Container.GetInstance<IRepository<T>>();
    rep.Save(current);
}

你可以将其限制为你的ISaveableModel<T>接口。上面的返回类型未实现,但你可以将其放置在布尔值或状态标志中,无论哪种方式。

是的,看起来这是一个不错的方向。我还在扩展方法上添加了一个where子句,以防止它被调用到系统中的任何对象上,只能在实现给定接口的对象上调用。目前看起来运行良好。谢谢! - David

1
在这两种方法中,Save() 函数的参数均不是 T 类型。 第一种方法中,它是 ISaveableModel<T>,而在第二种方法中,它是 BaseModel<T>。 由于存储库是基于 T 的通用类型,因此 Save 方法将期望一个 T 类型的变量。您可以在调用 Save 之前添加简单的 T 强制转换来修复它。
或者,您的 IRepository<T> 可以进行更改。
public interface IRepository<T>
{
    T Save(ISaveableModel<T> model);
}

哪个更有意义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接