首页 新闻 赞助 找找看

MVC,EF,Repository模式,UOW模式,DTO,取得新KEY值

0
悬赏园豆:60 [已解决问题] 解决于 2014-11-02 13:47

菜鸟一枚,在此虚心请教

自己学的MVC,然后就慢慢的接触到了Repository模式,等等(标题里写的那些)

现在发现把这些和到一起遇到了一个自己不知如何解决的办法

闲话到此结束

 

 

下面开始铺垫了

 

使用EF(Code First),且又想在查询的时候按需查询,所以必须使用Linq to Entity 的结果映射

而且映射的类型不能是在DbContext 使用的使用的model类型,否则会报错

因为这样,就使用了 DTO,这样的话 按需查询(查询表中某一部分字段)  操作就正常了

 

然后又引入了Repository模式,和UOW(unit of work)

这样的话,在DbContext中记录数据的操作,与提交到数据库 就分开了

算了,不知道该怎么写了。。

给个例子吧

 

public MyContext:DbContext

{

  public DbSet<Product> Product {get;set;}

}

 

 

public class ProductRepository:Repository<Product>,IProductRepository

{

  public ProductRepository( MyContext context ):base(context){}

 

  IQueryable<Product> Products {get { return context.Product; }}

 

  void InsertProduct ( ProductDTO productDTO ){

    Prodcut p = new Prodcut{

      ProductID = productDTO.ProductID,

      Name = productDTO.Name

    };

 

    context.Product.Add(p);

  }

 

}

 

public class UnitOfWork ( ) {

  ....

  ...

  void SaveChange(){

    context.SaveChange();

  }

}

 

public ProductController:Controller

{

  ....

  [Httppost]

  public ActionResult Create(ProductDTO productDTO)

  {

    productRepository.InsertProdcut(productDTO);

    unitOfWork.SaveChange();

    .....

  }

  ....

}

 

 

Controller中只操作DTO

比如

新建商品,

通过将DTO传递给 具体的Repository的InsertProduct方法

在InsertProduct方法内通过 productDTO 来构建 product实例

然后将product实例传递给context.Product.Add()方法

在Controller中通过调用UnitOfWork实例的SaveChange()方法来提交操作到数据库

 

而EF呢

新的主键(identity的 primaryKey)

会在提交后(SaveChange()方法调用后),更新到实体对象的属性中

而我上面因为使用了DTO,

而调用SaveChange()之后也不能访问到Product类型的实例

所以在Controller中不知道该怎么取得新的KEY值

 

 ~~~~~~~~~~~~~~~~~~~~~~

我现在唯一的思路就是

用ProductDTO对象  构造Product对象 的代码写到

Controller中,(而不是想我上面那样放到Repository中)

这样的话,SaveChange()调用后,应该能够在Controller中通过具体对象访问到新Key

但是

感觉这样的话,感觉不太好,似乎啊,在我的认知中Prodcut应该属于数据访问层,Controller不应该访问他们。。

 

所以在这里,想问问大家,你们是怎么使用的

 

MVC,EF,Repository模式,UOW模式,DTO

这几个 攒到一起,怎么用好呢?

算了的主页 算了 | 初学一级 | 园豆:3
提问于:2014-11-01 17:13
< >
分享
最佳答案
0

你说了这么多,谁看得过来啊。

如果一切都是KEY的问题,那真没有必要写这么多。

听说过宝洁香皂盒检测的故事没有,用一个工业风扇就解决了需要几百万才能解决的问题。

但如果还有其他数据层基础逻辑的话,那没有办法,

要么逻辑存在两个地方,要么就必须让新建对象交给商业对象处理再返回。

收获园豆:60
爱编程的大叔 | 高人七级 |园豆:30839 | 2014-11-01 18:13

其实 我也很纠结,在想提这个问题的时候,就有种不好的预感的

但是,这个问题我没有太好解决办法,又找不到会的人帮忙解决(目前公司不用这套东西,我只能自己看)

哎,,好无奈

算了 | 园豆:3 (初学一级) | 2014-11-01 18:38

这么着吧,大叔 能否提供一个 范例

使用

MVC,EF,Repository模式,UOW模式,DTO

能够方便取得新key值的

或者给个思路也好啊

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

但如果还有其他数据层基础逻辑的话,那没有办法,

要么逻辑存在两个地方,要么就必须让新建对象交给商业对象处理再返回。

数据层逻辑?不知道我理解的对不对

没有吧,数据库反问就是 EF , Repository , uow

---

逻辑存放在两个地方?怎么理解?不是很明白。

还有就是新建对象交给商业对象处理。

  这个商业对象 是什么?controller可以看做是商业对象么?

算了 | 园豆:3 (初学一级) | 2014-11-01 18:48

@算了: 因为你的整个问题太多文字,很多技术的集成,每个人理解不同。这个很难说的开。

不过关于KEY,我知道相当多人还困扰于Indentity自动生成的事,就这点来说,我已经改用GUID当PrimaryKey好多年了,没发现啥特别不舒服的地方。(当然可能是没碰到高级应用)

对于Order -OrderDetail这类的主从应用,使用GUID当主键,好处非常多,因为不用等数据库。

 

至于商业对象,不是Controller,而是Mode。

准确的说法,不知道你是否看过CSLA.NET。

爱编程的大叔 | 园豆:30839 (高人七级) | 2014-11-01 20:17

@算了: 我感觉上你的理解上应该有些地方是出问题了。

DTO   ---- Model

Entity ---- Model

通常普通应用,不需要DTO,只用EF的Entity当Model就行了。

 

如果你用到DTO,基本上你还得再引用AutoMapper之类的自动映射技术,

而DTO的引入,在我的理解中,是为了减少移动对象序列化的成本,

这个应该在更复杂的系统设计中才有必要引入的。

 

MVC在这儿一点关系都没有。至于你说的

又想在查询的时候按需查询,所以必须使用Linq to Entity 的结果映射

而且映射的类型不能是在DbContext 使用的使用的model类型,否则会报错

因为这样,就使用了 DTO,这样的话 按需查询(查询表中某一部分字段)  操作就正常了

应该是你还不会用EF造成的,我可以肯定的告诉你,没有这回事。

爱编程的大叔 | 园豆:30839 (高人七级) | 2014-11-01 20:22

@爱编程的大叔: 

1:CSLA.NET。我还真没看过,有空会看看的

2:对于DTO的理解。。。。数据传输对象对象。它是针对view的需求而定义的吧,只用于存储和传递数据吧

大致这个理解

3:

DTO   ---- Model

Entity ---- Model

也是认同的

4:对于按需查询

我刚才又再次实验了下

~~~~~~~~~~~~~~~~~~~~~~~~~~~

public ActionResult Index()
{

  List<good> goods = (from g in goodsRepository.Goods
            where g.delet_mark == "0"
            select new good
            {
              goods_id = g.goods_id,
              goods_ab = g.goods_ab,
              goods_name = g.goods_name
            }).OrderBy(x=>x.goods_id).Take(10).ToList();

 

return View(goods);

}

异常详细信息: System.NotSupportedException: The entity or complex type 'TyougyouBDA_Success.Domain.Models.good' cannot be constructed in a LINQ to Entities query.

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~

public ActionResult Index()
{
  List<GoodsDTO> goods = (from g in goodsRepository.Goods
                where g.delet_mark == "0"
                select new GoodsDTO
                {
                  goods_id = g.goods_id,
                  goods_ab = g.goods_ab,
                  goods_name = g.goods_name
                }).OrderBy(x=>x.goods_id).Take(10).ToList();

 

  return View(goods);
}

 

这样则 正常

~~~~~~~~~~~~~~~~~~~~~~~~~~~

下面是一些相关类型的定义

public class GoodsDTO
{
  public string goods_id { get; set; }
  public string goods_ab { get; set; }
  public string goods_name { get; set; }
}

 

public partial class good
{
  public string goods_id { get; set; }
  public string goods_ab { get; set; }
  public string goods_name { get; set; }
  public string goods_brand_id { get; set; }
  public string goods_class_id { get; set; }
  public string goods_spec { get; set; }
  public string goods_max_pack { get; set; }
  public string goods_min_pack { get; set; }
  public string suppl_id { get; set; }
  .........

  .........

}

补充一句,当时之所以这样做,是因为一开始遇到这个问题的时候,从园子里的其他帖子开始的

一下两个差不多阐述的是一个问题。

博客园现代化建设—[Entity Framework]在LINQ查询中指定返回的字段

Entity Framework搜索指定字段解决方案

而且,按照里面的方法确实也成功了

所以也就这么用了。

 

 

5:AutoMapper这个嘛,我略有所闻,也看过一些相关资料。不过现在依然手动设置的,呵呵。主要是有更优先的问题。需要解决。。。。。

 

大叔,我知道我的问题很长很长 很烦人的

但是我也是在没办法了。

我还有20多分。回头都加给大叔吧

大叔现在除了你没人能救小弟于水火了。

五体投地 望大叔 指条 明路、。。

(系统提示:发帖人已处于暴走边缘。)

算了 | 园豆:3 (初学一级) | 2014-11-01 22:15

@算了: 

List<good> goods = (from g in goodsRepository.Goods
            where g.delet_mark == "0"
            select new good
            {
              goods_id = g.goods_id,
              goods_ab = g.goods_ab,
              goods_name = g.goods_name
            }).OrderBy(x=>x.goods_id).Take(10).ToList();

说一下这个吧,这个当然是不行的。可是没人让你这样用啊。

IQueryable<good> goods = from g in goodsRepository.Goods
            where g.delet_mark == "0"
                   Order by g.goods_id
            select g;

你不就是为了把MODEL传到VIEW吗?

爱编程的大叔 | 园豆:30839 (高人七级) | 2014-11-01 22:24

@爱编程的大叔: 

我的目的 的确是 把model传递到view中,

但是有一个小问题。

数据库表的字段很多很多。而view中使用其中一小部分

如果这样传的话,不是把view中所有列的数据都传递到view中了么?

 

我之所以那么用,是想只查询需要的数据。

下面是两种写法生成的SQL

------------------

SELECT TOP (10)
[Project1].[C1] AS [C1],       【只查询这几个字段】
[Project1].[goods_id] AS [goods_id],
[Project1].[goods_ab] AS [goods_ab],
[Project1].[goods_name] AS [goods_name]
FROM ( SELECT
[Extent1].[goods_id] AS [goods_id],
[Extent1].[goods_ab] AS [goods_ab],
[Extent1].[goods_name] AS [goods_name],
1 AS [C1]
FROM [dbo].[goods] AS [Extent1]
WHERE N'0' = [Extent1].[delet_mark]
) AS [Project1]
ORDER BY [Project1].[goods_id] ASC

------------------

SELECT TOP (10)         【而大叔的写法,会把整个表所有的数据都查询出来】
[Extent1].[goods_id] AS [goods_id],
[Extent1].[goods_ab] AS [goods_ab],
[Extent1].[goods_name] AS [goods_name],
[Extent1].[goods_brand_id] AS [goods_brand_id],
[Extent1].[goods_class_id] AS [goods_class_id],
[Extent1].[goods_spec] AS [goods_spec],
[Extent1].[goods_max_pack] AS [goods_max_pack],
[Extent1].[goods_min_pack] AS [goods_min_pack],
[Extent1].[suppl_id] AS [suppl_id],
[Extent1].[goods_quality_day] AS [goods_quality_day],
[Extent1].[max_stora_count] AS [max_stora_count],
[Extent1].[min_stora_count] AS [min_stora_count],
[Extent1].[min_weight] AS [min_weight],
[Extent1].[max_volume] AS [max_volume],
[Extent1].[volume_unit_id] AS [volume_unit_id],
[Extent1].[weight_unit_id] AS [weight_unit_id],
[Extent1].[price_in_type] AS [price_in_type],
[Extent1].[tax_rate] AS [tax_rate],
[Extent1].[delet_mark] AS [delet_mark],
[Extent1].[select_mark] AS [select_mark],
[Extent1].[memo] AS [memo],
[Extent1].[sell_type] AS [sell_type],
[Extent1].[goods_type] AS [goods_type],
[Extent1].[goods_in_price] AS [goods_in_price],
[Extent1].[goods_out_price] AS [goods_out_price],
[Extent1].[goods_price1] AS [goods_price1],
[Extent1].[goods_price2] AS [goods_price2],
[Extent1].[goods_price3] AS [goods_price3],
[Extent1].[goods_price4] AS [goods_price4],
[Extent1].[goods_price5] AS [goods_price5],
[Extent1].[goods_price6] AS [goods_price6],
[Extent1].[goods_price7] AS [goods_price7],
[Extent1].[goods_price8] AS [goods_price8],
[Extent1].[goods_sale_price] AS [goods_sale_price],
[Extent1].[purchase_mark] AS [purchase_mark],
[Extent1].[alter_time] AS [alter_time],
[Extent1].[delet_date] AS [delet_date],
[Extent1].[compe_price] AS [compe_price],
[Extent1].[goods_state] AS [goods_state],
[Extent1].[state_date] AS [state_date]
FROM [dbo].[goods] AS [Extent1]
WHERE N'0' = [Extent1].[delet_mark]
ORDER BY [Extent1].[goods_id] ASC

 

 

 

算了 | 园豆:3 (初学一级) | 2014-11-01 22:46

@算了: 唉!大叔虽然不是大婶,不过还不至于不知道你想选择字段,减少数据库负担。

而且我的写法完全支持选择字段的。大叔的软件模式对于限制字段的需求远远超过你的需求,呵呵,具体情况就不展开了。

你听说过Lazy Loading吗?可能你只是听说过,没有想过可以这样用吧?

我并没有让你不选择Field,只是你想要Model,传个Iqueryable过去,在View层再

var query =(from c in model

      select c.goods_id, c.goods_ab, c.goods_name).take(10).tolist();

这样就行了。

 

当然,可能有人会说这样写不好。

你自己看吧。

爱编程的大叔 | 园豆:30839 (高人七级) | 2014-11-01 23:10

@爱编程的大叔: 

我知道,Lazy Loading

只是,习惯这么用罢了

大叔提供的方法我明白的。我的方式是在controller中就将数据加载了(ToList()方法执行值就加载了)

而大叔的方法是在view中加载的,然后通过匿名类型存储和访问数据

确实也是一种方式

嗯 ,且这样的方法有一个好处

就是如果是在controller中 加载 并通过匿名类型存储的话 , 不能传递到view中。

 

大叔这么弄的话 就规避了  匿名类型无法作为 参数和返回值 使用的问题

 

 

至于有人说不好,我猜猜啊,是不是说 访问数据库 是在view中发生的,而觉得这样不好呢。。呵呵猜测

 

待我做个试验

算了 | 园豆:3 (初学一级) | 2014-11-01 23:31
其他回答(1)
0

在DTO弄一个id属性,然后savechange后,把实体的id赋值给dto不就可以了

但是一切需要在新增后获取id都不是一个好办法,所以你源头就错了,返回id干嘛?

lawbc | 园豆:63 (初学一级) | 2014-11-02 00:30

比如 管理商品的界面

新增一个商品,是这样的

打开新增界面,输入数据,调教。创建成功,页面跳转到 此商品的 详细信息界面

那么在 跳转到详细信息界面的时候 怎么做?

当让是 提供商品ID,然后让相应的 动作方法 通过ID查询数据 并渲染视图

所以 需要在新增后 获取新的ID

支持(0) 反对(0) 算了 | 园豆:3 (初学一级) | 2014-11-02 00:56

@算了: 只要你还继续使用Identity自增ID,数据访问层在SaveChanges后返回实体对象就是不可避免的。

不管你用了简单还是复杂的技术,也不管你用了多少层转换。

支持(0) 反对(0) 爱编程的大叔 | 园豆:30839 (高人七级) | 2014-11-02 08:13

@爱编程的大叔: 

大叔的分析和结论 确实切中要害

那么使用GUID的话

我猜测下啊 

大叔的意思是 通过C# 来获取GUID , 然后赋值给 实体对象 再更新到数据库吧

不知猜的对不对。

一开始我猜的是通过数据库获取GUID,不过一想也不对这样的话还是需要 通过数据库返回GUID

然后又猜了猜 不知猜的对不对

不过相信应该是如此吧

好吧,这个问题就如此吧

其实

以后工作中的话,数据库 怎么设计不一定是我说了算。所以用不用GUID确实不好说呢。。。。。。。

支持(0) 反对(0) 算了 | 园豆:3 (初学一级) | 2014-11-02 13:47
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册