views:

2606

answers:

4

I am a somewhat experienced Rails developer and I thought I would try out ASP.NET's version of MVC. In doing so I also decided to try Linq->Sql...

I am a bit confused about the way Linq->Sql handles joins.

A trivial example of my schema is :

books:
id
title

categories:
id
name

books_categories:
book_id
category_id

Simply dragging these tables to the .dbml file doesn't seem to do it. I get a property on my Book class books_categories, what I expect is a property that I can iterate over and get Category classes directly.

Right now I have to do something that feels very wrong

        foreach (books_categories bc in book.books_categories)
        {
            category_names.Add(bc.Category.category.Trim());
        }

[In Response to Accepted answer]
I grudgingly accepted the answer of "write your own glue code". After continuing my research of Linq->Sql I discovered that it is apparently slowly being let go in favor of the (more powereful, IMO) Entity Framework. EF still allows one to use LINQ for queries and does a decent job of figuring out relationships like Ruby's ActiveRecord.

A: 

I don't think your expected result is supported in Linq to Sql.

What you're doing may feel wrong, but I think that's one way to work around that limitation of L2S.

Man, i've really got to get into Rails...

Bramha Ghosh
+6  A: 

Use a partial class implementation for Book and add appropriate methods for categories and their properties. Have the properties front-end the Books_Categories property (you can make this have private visibility to force implementation through your Categories property).

public partial class Books
{
    public IEnumerable<string> CategoryNames
    {
       get
       {  
            return this.Books_Categories
                       .Select( bc => bc.Category.category.Trim() );
       }
    }

    public void AddCategory( Category category )
    {
       this.Books_Categories.Add( new Book_Category
                                  {
                                      Category = category,
                                      Book = this
                                  } );
    }

    public void RemoveCategory( Category category )
    {
       var bc = this.Book_Categories
                    .Where( c => c.Category == category )
                    .SingleOrDefault();
       this.Books_Categories.Remove( bc );
    }
}

Obviously, you'll need to add some error/bounds checking, etc. but you get the idea.

I'll grant you this is not ideal, but at least you have the flexibility to determine how it works.

tvanfosson
wouldn't you only be able to add a Book_Category, to this.Book_Categories in your add and remove methods?
Allen
Edit error -- I probably typed the code in then renamed the property. Will update.
tvanfosson
+1  A: 

Many to many mappings are explicitly supported in the Entity Framework, but not in LINQ to SQL. You can also use third-party ORMs such as NHibernate.

Craig Stuntz
That makes me wonder why I would use Linq->Sql over Ado.NET + Linq. I figured Linq->Sql would support ORM, not doing so feels like a step back.
Bill
LINQ to SQL *is* an ORM (of sorts). You don't use LINQ to SQL with a *different* ORM. Other ORMs replace it. Other ORMs may or may not support LINQ, but most do support it.
Craig Stuntz
If Linq-Sql is an ORM, then I would expect it to figure out how to do joins automatically (hence the relational part) instead of having to write them myself....
Bill
A: 

What you can do if you want to create a book and directly want to add a category to it is: in your view:

        <p>
            <label for="CategorySelect">Category:</label>
            <%= Html.ListBox("CategorySelect") %>
            <%= Html.ValidationMessage("CategorySelect", "*")%>
        </p>

in your bookscontroller:

        public ActionResult New()
    {
            var data = _entities.Categories.ToList();
        ViewData["CategorySelect"] = new MultiSelectList(data, "Id", "Name");
}

        [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult New([Bind(Exclude = "Id")] Book Booknew)
    {
        IEnumerable<int> selectedCategories = Request["CategorySelect"].Split(new Char[] { ',' }).Select(idStr => int.Parse(idStr));

        if (!ModelState.IsValid)
            return View();

        try {
            foreach(var item in selectedCategories){
                BooksCategories bc = new BooksCategories();
                bc.Book = Booknew;
                bc.CategoryId = item;
                _entities.BooksCategories.InsertOnSubmit(bc);
            }
            _entities.Books.InsertOnSubmit(Booknew);
            _entities.SubmitChanges();
Michael