none
Using generics in LINQ RRS feed

  • Question

  • Hi

    I've a data access layer where I'm using LINQ to populate data classes and a business/ core assembly. The normal way to work with business data classes would be (I believe) :


    In DAL:
    1public IEnumerable<Data.Bolig> GetSingle(int boligID) 
    2        { 
    3            //this only works on collections 
    4            //DataLoadOptions options = new DataLoadOptions(); 
    5            //options.AssociateWith<model.Data.Bolig>(c => c.Adresse.Take(3)); 
    6 
    7            //model.Data.Bolig b = new AlphaSolutions.SA.Salgssite.Model.Data.Bolig(); 
    8             
    9            var bolig = from g in this.DataContext.Boliger 
    10
    11
    12 
    13            return bolig.AsEnumerable(); 
    14 
    15 
    16        } 
    17 


    And in my BLL:
    1public Bolig GetBolig(int boligID) 
    2        { 
    3            //TODO: Generic required 
    4            var t = from g in this.rBolig.GetSingleT(boligID) 
    5                    select new Bolig 
    6                    { 
    7                        BoligarealM2 = g.BoligarealM2, 
    8                        Byggeaar = g.Byggeaar 
    9                         
    10                    }; 
    11 
    12            return t.Single(); 
    13 
    14        } 
    15 

    The Bolig entitet is somewhat redundant here and I don't want to reference the DAL from my UI. So my first thought was to initialize the the BoligRepository with a Bolig type from BLL, like this:
    1public class BoligRepository<T>  
    2    { 
    3        public IDataContext DataContext{ getprivate set; } 
    4         
    5         
    6 
    7        /// <summary> 
    8        /// Initializes a new instance of the <see cref="BoligRepository"/> class. 
    9        /// </summary> 
    10        /// <param name="dataContext">The data context.</param> 
    11        public BoligRepository(IDataContext dataContext) 
    12        { 
    13            this.DataContext = dataContext; 
    14        } 
    15 
    16        public IEnumerable<T> GetSingleT(int boligID) 
    17        { 
    18            var bolig = from g in this.DataContext.Boliger 
    19                        where g.BoligID == boligID 
    20                        select g; 
    21 
    22            return bolig.Cast<T>(); 
    23        } 

    But this approach is obviously invalid, so I need some help with this issue.

    Can anybody point me in the right direction?


    Monday, February 23, 2009 2:42 PM

Answers

  • if you want to specify DataLoadOptions, you can just use the partial method OnCreated() from your DataContext to define those, and then it will happen every time you use that DataContext.  Or, you can change the options based on what class/method you are using to make the eager loading not take quite a toll when it doesn't need to.
    Daniel - http://webs.neumont.edu/dstafford
    • Marked as answer by Janus007 Monday, February 23, 2009 7:27 PM
    Monday, February 23, 2009 4:46 PM

All replies

  • First of all, usually the way in which I use LINQ is actually as a generated DAL, so I usually don't even include a DAL when using LINQ.  Secondly, I would not use a global DataContext since you'll be leaving a connection open, I would use a using statement only around the sections that you need.  Lastly, I think what you want is more something like this, though keep in mind that all of your tables will have to have the same name for your identifying column for this solution to work:
    public T GetSingleT<T>(int id) where T : class 
        using (var dc = new TestDataContext()) 
        { 
            T result = dc.GetTable<T>().SingleOrDefault(t => (int)t.GetType().GetProperty("Id").GetValue(t, null) == id); 
            return result == default(T) ? null : result; 
        } 


    Daniel - http://webs.neumont.edu/dstafford
    Monday, February 23, 2009 3:39 PM
  • Thank you, I'll try your solution.

    In the meantime... Could you please explain why you don't use a DAL? And how you then prevent your UI-developers to write "strange" SQL using LINQ? Not that LINQ generates strange SQL, but the fact that it can be misused by someone how doesn't quite understand how to write proper SQL!


    Kind regards
    Janus

    Monday, February 23, 2009 3:55 PM
  • The reason I don't use a DAL is because a traditional DAL is pretty much exactly what LINQ to SQL already handles.  Most of the time, when I am helping develop a large scale application that I am not necessarily the UI-designer on, the data is also used in other places so it ends up being a WCF service, which prevents direct access to the DataContext.
    Daniel - http://webs.neumont.edu/dstafford
    Monday, February 23, 2009 3:59 PM
  • Thank you for you time...

    However if I use a "using datacontext" I cant imagine that lazy loading will work properly i.e. DataLoadOptions options = new DataLoadOptions(); etc. Then I'll have to create full populated dataclasses when I actually don't know if they ever are going to be used, and I'll allways have to return lists from my LINQ (datalayer).
    So for instance, if I would be able to create an advanced query in my BLL (yours WCF-layer) then I would need to use the LINQ-layer, instead of my BLL. Right?



    Monday, February 23, 2009 4:38 PM
  • if you want to specify DataLoadOptions, you can just use the partial method OnCreated() from your DataContext to define those, and then it will happen every time you use that DataContext.  Or, you can change the options based on what class/method you are using to make the eager loading not take quite a toll when it doesn't need to.
    Daniel - http://webs.neumont.edu/dstafford
    • Marked as answer by Janus007 Monday, February 23, 2009 7:27 PM
    Monday, February 23, 2009 4:46 PM
  • Inspired a bit by your approach... only having a LINQ-layer and a Service!

    Though I think I'll create a singleton datacontext from within my Service and pass it along as I originally did. That way I believe I can create a high performance Service without the need to instanciate the DataContext for every DB-call. And I will still be able to, if needed, create genius joins in the Service :) I will place the Service in the same assembly as the LINQ and instead of horizontal partitioning I'll try to keep it vertical and small.

    Can't wait to code it, thank you for your inspiration and feedback.

    Monday, February 23, 2009 7:27 PM
  • I would strongly recommend against having a singleton DataContext in your service, unless you are sure that your data is read-only. Each DataContext instance is intended for a single unit of work, and the longer the DataContext is alive, the higher the chance for its entity cache to become stale. Have a look at this post for more information. Also, if you are running in a service that supports more than one request at a time (which is very likely), be aware that the same DataContext is not intended for use on multiple threads, and with good reason. You can run into some very scary situations, such as running two update requests simultaneously on the same DataContext--since they happen at the same time, it's possible that updates from one request will be run on another request's call to DataContext.SubmitChanges. If that database call fails, then the updates that are rolled back include updates from both requests.

    Keep in mind that the DataContext is cheap to create and dispose. You shouldn't hesitate to use the using(...) pattern that Daniel mentioned earlier.
    Wednesday, February 25, 2009 10:11 PM
    Answerer