数据访问层 - LINQ-To-SQL 和泛型。我可以优化这个吗?

发布于 2024-09-14 05:05:11 字数 1713 浏览 1 评论 0原文

我们正在努力改进 DAL,它是用 LINQ 编写的,可与 MS SQL 数据库通信。我们的目标是用尽可能少的代码实现良好的可重用性。

LINQ 生成的文件利用泛型和反射将 LINQ 生成的类映射到 SQL 对象(在我们的例子中为表和视图)。

请参阅现有访问器的示例。该方法驻留在包含自定义构造函数、访问器和修改器的分部类中:

public clsDVD getDVD(int dvdId)
{
   try
   {
      using (DataContext dvdDC = new DataContext(ConnectionStringManager.getLiveConnStr()))
      {
                    // Deferred loading
                    dvdDC.DeferredLoadingEnabled = false;

                    var tDVD = dvdDC.GetTable<DVD>();

                    return (from t in tDVD 
                            // Filter on DVD Id
                            where t.DVDId == (dvdId)
                            select t).Single();                   
      }
   catch (Exception e)
   {
       Logger.Log("Can't get requested DVD.", e);
       throw;
   }
 }

我相信这非常容易维护,因为大部分工作都是在 var tDVD 之后完成的,

建议不要这样做声明 tDVD 并使用 dataContext.TableName,但在幕后它仍然调用 GetTable

我认为改进这一点的唯一方法是将这个部分类分成 4 个(CRUD)部分类。例如,

clsDVD_Select, clsDVD_Update, clsDVD_Insert, clsDVD_Delete

在这种情况下,每个类将代表一组行为。

我们正在讨论的想法是看看是否可以在 LINQ 泛型之上使用泛型。

例如,我们不会使用部分类,而是通过使用针对 SQL 数据库的反射来动态找出类的属性。我首先关心的是性能影响。将会有多么重大的意义。

我们将使用以下内容代替 ClsDVD.getDVD(1231)GenericDC.Select(1231)

.Select方法将找出主键并对该表运行选择查询。我很难理解这是如何运作的。假设我们可以让它适用于简单的选择,即使用主键过滤器进行选择,但是当我们开始进行复杂的连接和分组选择时会发生什么。当我们希望每个 DVD 类别有多个选择时会发生什么?

我最后关心的是良好实践。以前有人告诉我,拥有一致的代码是件好事。例如,如果我决定使用数据表,那么我应该在整个项目中坚持使用数据表。让项目的一半包含数据表,另一半包含用户定义的类,这是一个坏主意。你同意这一点吗?

我认为现有的实现非常好,但也许我错过了一些非常明显的东西,并且有一种更简单、更面向对象的方法来实现相同的结果?

谢谢

We are working on improving our DAL which is written in LINQ that talks to the MS SQL database. Our goal is to achieve good re-usability with as little code as possible.

LINQ generated files are making a use of generics and reflection to map LINQ generated classes to the SQL objects (tables and views in our case).

Please see the example of the existing accessor. This method resides in the partial class that contains custom constructors, accessors and mutators:

public clsDVD getDVD(int dvdId)
{
   try
   {
      using (DataContext dvdDC = new DataContext(ConnectionStringManager.getLiveConnStr()))
      {
                    // Deferred loading
                    dvdDC.DeferredLoadingEnabled = false;

                    var tDVD = dvdDC.GetTable<DVD>();

                    return (from t in tDVD 
                            // Filter on DVD Id
                            where t.DVDId == (dvdId)
                            select t).Single();                   
      }
   catch (Exception e)
   {
       Logger.Log("Can't get requested DVD.", e);
       throw;
   }
 }

I believe that this is very easy to maintain, since the most of the work is done after var tDVD

It has been suggested not to declare tDVD at all and use dataContext.TableName, but behind the scenes it still calls GetTable<>.

The only way I can see of improving this is breaking this one partial class into 4 (CRUD) partial classes. E.g.

clsDVD_Select, clsDVD_Update, clsDVD_Insert, clsDVD_Delete

In this case each class will represent a set of behaviours.

The idea that we are discussing is to see whether it's possible to use generics on top of LINQ generics.

For example, instead of having the partial classes, we would figure out the properties of the class on the go by using reflection against the SQL database. My first concern here is performance impact. How significant will it be.

Instead of ClsDVD.getDVD(1231) we'd have something on the lines of: GenericDC.Select<DVD>(1231)

.Select method would figure out the primary key and run a select query on that table. I'm struggling to understand how can this work. Lets say we can get this to work for the simple select, i.e. select with a filter on primary key, but what is going to happen when we start doing complex joins and group by selects. What happens when we want to have multiple selects per DVD class?

My final concern is to do with good practices. I have been told before that it's good to have consistant code. For example, If I decide to use datatables , than I should stick to datatables throughout the project. It's a bad idea to have half of the project with datatables and another half with user defined classes. Do you agree on this?

I'm in a position where I think that existing implementation is quite good but maybe I'm missing out something very obvious and there is a much easier, more OO way of achieving the same results?

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一杆小烟枪 2024-09-21 05:05:11

这是使这种情况更加通用的一种方法。冲洗并重复其他 CRUD 操作。对于某些情况,性能可能不可接受。在这些情况下,我会重组程序的该部分以调用非通用版本。

public T GetSingleItem(Func<T,bool> idSelector ) where T : ??? // forgot what type it needs to be off the top of my head
{ 
    try 
    { 
        using (DataContext context = new DataContext(ConnectionStringManager.getLiveConnStr())) 
        { 
            context.DeferredLoadingEnabled = false; 
            return context.GetTable<T>().Single( item => idSelector( item );
        } 
    }
    catch (Exception e) 
    { 
        Logger.Log("Can't get requested item.", e); 
        throw; 
    } 
} 

这就是您获得该物品的方式。不太优雅,因为您必须告诉通用函数您将使用哪一列。

GenericDC.GetSingleItem<DVD>( dvd => dvd.ID == 1231 )

为了使其更加通用,将其限制为具有 ID 的单个项目...

public IEnumerable<T> GetItems(Func<T,bool> selectFunction ) where T : ??? // forgot what type it needs to be off the top of my head
{ 
    try 
    { 
        using (DataContext context = new DataContext(ConnectionStringManager.getLiveConnStr())) 
        { 
            context.DeferredLoadingEnabled = false; 
            return context.GetTable<T>().Select( item => selectFunction( item );
        } 
    }
    catch (Exception e) 
    { 
        Logger.Log("Can't get requested item.", e); 
        throw; 
    } 
} 

那么您可以这样调用它:

GenericDC.GetItems<DVD>( dvd => dvd.Title == "Title" && dvd.Cast.Contains( "Actor" ) );

另一种可能的解决方案是创建一个自定义代码生成器,您可以在一个地方对其进行修改并创建类似的例程对于所有其他类型。如果您遇到性能问题,这可能是一个很好的解决方案。您可能希望限制对您使用的模板代码段的更改。

Here is one way to make this situation a little more generic. Rince and repeat for the other CRUD opperations. For some sitiations the performance may be unacceptable. In those cases I would restructure that part of the program to call a non generic version.

public T GetSingleItem(Func<T,bool> idSelector ) where T : ??? // forgot what type it needs to be off the top of my head
{ 
    try 
    { 
        using (DataContext context = new DataContext(ConnectionStringManager.getLiveConnStr())) 
        { 
            context.DeferredLoadingEnabled = false; 
            return context.GetTable<T>().Single( item => idSelector( item );
        } 
    }
    catch (Exception e) 
    { 
        Logger.Log("Can't get requested item.", e); 
        throw; 
    } 
} 

This would be how you woudl have to get the item. Not quite as elegant becase you have to tell the generic function which column you are going to be using.

GenericDC.GetSingleItem<DVD>( dvd => dvd.ID == 1231 )

To make this even more generic that limiting it to a single item with an ID...

public IEnumerable<T> GetItems(Func<T,bool> selectFunction ) where T : ??? // forgot what type it needs to be off the top of my head
{ 
    try 
    { 
        using (DataContext context = new DataContext(ConnectionStringManager.getLiveConnStr())) 
        { 
            context.DeferredLoadingEnabled = false; 
            return context.GetTable<T>().Select( item => selectFunction( item );
        } 
    }
    catch (Exception e) 
    { 
        Logger.Log("Can't get requested item.", e); 
        throw; 
    } 
} 

Then you can call it like:

GenericDC.GetItems<DVD>( dvd => dvd.Title == "Title" && dvd.Cast.Contains( "Actor" ) );

Another possible solution would be to create a custom code generator that could you could modify in one place and create the similar routines for all other types. This would probably be a good solution if you are running into performace problems. You would want to limit the changes to the template piece of code that you use.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文