Using LINQ to SQL across layers : an alternative to reflection or cloning | dimebrain

我只选择其中的几句重点翻译一下。

1,对官方回复的解释是,DataContext对象不应该在实例化后长久的使用。……另外,从我所能判断的,LINQ to SQL事实上也并不是一个健全的ORM。

      所以,或许我们应该在LINQ to SQL实体之上写包装类,即使他们看起来像真实的业务对象。

      JSY注2009-8-25:这和我前两天确定的通过LINQ进行N-Tier开发的思想有些冲突——我是将LINQ to SQL的实体类完整的当作真实的业务对象来在每个层次间共享了。

Using LINQ to SQL across layers : an alternative to reflection or cloning

December 15th, 2007

Posted in LINQ to SQL, Tagged with C#, DLINQ, LINQ, LINQ to SQL

The Backstory

There are many of us who are excited about the opportunities that LINQ to SQL provides for strongly typed access to databases, but there is a lot of passionate debate about its questionable effectiveness as a basic ORM tool between business layers, with Rick Strahl providing the most thorough explanation here.

The majority of complaints focus on the fact that it isn’t possible to create an entity, disconnect it from its data context, reattach it at some other point, and then apply the changes. At it stands, this pattern cannot be achieved without needing one of two things to satisfy the demands of optimistic concurrency, which cares not only about what the entity looks like now compared to the proposed changes, but also what it looked like when it was requested from a previous context:

1) A timestamp field on the entity to use as a basic proof; unfortunately the resulting generated SQL will update every field by brute force.

2) The original entity state before changes were made; this requires the extra developer effort of retrieving and persisting two objects at all times, the original state of the entity and the changed state, which is arguably what an ORM should handle for you. You can see one implementation of this on Rocky Moore’s post that employs cloning, and another in the comments on Rick’s follow-up post that suggests using reflection to substitute for original entity state via a copy method.

Is there an alternative for those of us writing layered web applications that want to use LINQ to SQL entities?

The Response

Paraphrasing the official response, a DataContext is not meant to be instantiated and kept around for very long, so being greedy with this object is not the correct way to work around this issue, and from what I can tell, LINQ to SQL is not really supposed to be a robust ORM, either.

So, yes we should probably write wrappers around LINQ to SQL entities, even though they look like real business entities, and no, we probably shouldn’t create data contexts that are built to last. Your choices then become opting out of optimistic concurrency by setting UpdateCheck.Never on every property of your LINQ to SQL entity, or provide one of the implementations above.

The fact that you’d have to reference a data layer project in your presentation project to make use of the entities inside should give it away that LINQ to SQL is not really meant to be a collection of business objects all on its own, but, with a little disregard for convention you can make it work, and surprisingly well.

A Solution

We can still accomplish our original goal of updating LINQ to SQL entities on the client side (for most of us that means through a business layer) and sending these changes down to the database, without having to code each change as an explicit business layer transaction (for example, writing methods with names like UpdatePhotoCaption), without using the Attach method which gets us into trouble, and without forcing design changes or carrying extra objects on the stack. Here’s how it might work:

1. Client requests an entity from the business layer:

view sourceprint?

1.Photo profilePhoto = new Facade(UserId).GetProfilePhoto();

2. Business layer serves up the entity with a simple LINQ query:

view sourceprint?

1.var db = new DatabaseContext();

2.return db.Photos.SingleOrDefault(p => p.UserId == UserId);

3. The client changes some arbitrary properties of the entity using a delegate Action, and sends that script to the business layer:

view sourceprint?

1.profilePhoto = new Facade(UserId).Update(profilePhoto, delegate(Photo p)

2.{

3. p.Caption = “I’d rather be skiing!”;

4. p.Rating++;

5.});

4. The business layer calls a data layer abstraction class to perform the updates:

view sourceprint?

1.return Database.Update(profilePhoto, update);

5. The data layer abstraction class retrieves the original entity, invokes the delegate to “play” the changes, and returns the updated object:

view sourceprint?

01.public static T Update<T>(T entity, Action<T> update)

02. where T : class

03.{

04. T updated = null;

05.

06. using(var db = new DatabaseContext())

07. {

08. Table<T> table = db.GetTable<T>();

09. T original = table.FirstOrDefault(e => e == entity);

10. if(original != null)

11. {

12. update(original);

13. updated = original;

14. }

15. db.SubmitChanges();

16. }

17.

18. return updated;

19.}

Conclusion

At the very least, we can provide some manner of encapsulation from client to business to data layers, and still use LINQ to SQL in a pseudo-ORM fashion.

Using LINQ to SQL across layers : an alternative to reflection or cloning | dimebrain

posted @ 2009-08-26 00:51 汗水房 阅读(...) 评论(...) 编辑 收藏