菜鸟一枚,在此虚心请教
自己学的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
这几个 攒到一起,怎么用好呢?
你说了这么多,谁看得过来啊。
如果一切都是KEY的问题,那真没有必要写这么多。
听说过宝洁香皂盒检测的故事没有,用一个工业风扇就解决了需要几百万才能解决的问题。
但如果还有其他数据层基础逻辑的话,那没有办法,
要么逻辑存在两个地方,要么就必须让新建对象交给商业对象处理再返回。
其实 我也很纠结,在想提这个问题的时候,就有种不好的预感的
但是,这个问题我没有太好解决办法,又找不到会的人帮忙解决(目前公司不用这套东西,我只能自己看)
哎,,好无奈
这么着吧,大叔 能否提供一个 范例
使用
MVC,EF,Repository模式,UOW模式,DTO
能够方便取得新key值的
或者给个思路也好啊
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
【
但如果还有其他数据层基础逻辑的话,那没有办法,
要么逻辑存在两个地方,要么就必须让新建对象交给商业对象处理再返回。
】
数据层逻辑?不知道我理解的对不对
没有吧,数据库反问就是 EF , Repository , uow
---
逻辑存放在两个地方?怎么理解?不是很明白。
还有就是新建对象交给商业对象处理。
这个商业对象 是什么?controller可以看做是商业对象么?
@算了: 因为你的整个问题太多文字,很多技术的集成,每个人理解不同。这个很难说的开。
不过关于KEY,我知道相当多人还困扰于Indentity自动生成的事,就这点来说,我已经改用GUID当PrimaryKey好多年了,没发现啥特别不舒服的地方。(当然可能是没碰到高级应用)
对于Order -OrderDetail这类的主从应用,使用GUID当主键,好处非常多,因为不用等数据库。
至于商业对象,不是Controller,而是Mode。
准确的说法,不知道你是否看过CSLA.NET。
@算了: 我感觉上你的理解上应该有些地方是出问题了。
DTO ---- Model
Entity ---- Model
通常普通应用,不需要DTO,只用EF的Entity当Model就行了。
如果你用到DTO,基本上你还得再引用AutoMapper之类的自动映射技术,
而DTO的引入,在我的理解中,是为了减少移动对象序列化的成本,
这个应该在更复杂的系统设计中才有必要引入的。
MVC在这儿一点关系都没有。至于你说的
又想在查询的时候按需查询,所以必须使用Linq to Entity 的结果映射
而且映射的类型不能是在DbContext 使用的使用的model类型,否则会报错
因为这样,就使用了 DTO,这样的话 按需查询(查询表中某一部分字段) 操作就正常了
应该是你还不会用EF造成的,我可以肯定的告诉你,没有这回事。
@爱编程的大叔:
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查询中指定返回的字段
而且,按照里面的方法确实也成功了
所以也就这么用了。
5:AutoMapper这个嘛,我略有所闻,也看过一些相关资料。不过现在依然手动设置的,呵呵。主要是有更优先的问题。需要解决。。。。。
大叔,我知道我的问题很长很长 很烦人的
但是我也是在没办法了。
我还有20多分。回头都加给大叔吧
大叔现在除了你没人能救小弟于水火了。
五体投地 望大叔 指条 明路、。。
(系统提示:发帖人已处于暴走边缘。)
@算了:
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吗?
@爱编程的大叔:
我的目的 的确是 把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
@算了: 唉!大叔虽然不是大婶,不过还不至于不知道你想选择字段,减少数据库负担。
而且我的写法完全支持选择字段的。大叔的软件模式对于限制字段的需求远远超过你的需求,呵呵,具体情况就不展开了。
你听说过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();
这样就行了。
当然,可能有人会说这样写不好。
你自己看吧。
@爱编程的大叔:
我知道,Lazy Loading
只是,习惯这么用罢了
大叔提供的方法我明白的。我的方式是在controller中就将数据加载了(ToList()方法执行值就加载了)
而大叔的方法是在view中加载的,然后通过匿名类型存储和访问数据
确实也是一种方式
嗯 ,且这样的方法有一个好处
就是如果是在controller中 加载 并通过匿名类型存储的话 , 不能传递到view中。
大叔这么弄的话 就规避了 匿名类型无法作为 参数和返回值 使用的问题
至于有人说不好,我猜猜啊,是不是说 访问数据库 是在view中发生的,而觉得这样不好呢。。呵呵猜测
待我做个试验
在DTO弄一个id属性,然后savechange后,把实体的id赋值给dto不就可以了
但是一切需要在新增后获取id都不是一个好办法,所以你源头就错了,返回id干嘛?
比如 管理商品的界面
新增一个商品,是这样的
打开新增界面,输入数据,调教。创建成功,页面跳转到 此商品的 详细信息界面
那么在 跳转到详细信息界面的时候 怎么做?
当让是 提供商品ID,然后让相应的 动作方法 通过ID查询数据 并渲染视图
所以 需要在新增后 获取新的ID
@算了: 只要你还继续使用Identity自增ID,数据访问层在SaveChanges后返回实体对象就是不可避免的。
不管你用了简单还是复杂的技术,也不管你用了多少层转换。
@爱编程的大叔:
大叔的分析和结论 确实切中要害
那么使用GUID的话
我猜测下啊
大叔的意思是 通过C# 来获取GUID , 然后赋值给 实体对象 再更新到数据库吧
不知猜的对不对。
一开始我猜的是通过数据库获取GUID,不过一想也不对这样的话还是需要 通过数据库返回GUID
然后又猜了猜 不知猜的对不对
不过相信应该是如此吧
好吧,这个问题就如此吧
其实
以后工作中的话,数据库 怎么设计不一定是我说了算。所以用不用GUID确实不好说呢。。。。。。。