views:

27

answers:

1

Context: ASP.NET MVC 2.0, Linq-to-Sql, .Net 3.5, IIS7, MS SQL 2008

I'm working on a game fan site.

I have a table as follows:

ToonId int (primary key)
RealmId (FK into Realms table)
OwnerId int (FK into Users table)
ToonName nvarchar(50)
IsMain bit

That is a single user may own multiple toons on multiple realms (aka servers), but exactly one toon must be marked as main per user per realm (only for realms where there is at least one toon).

Suppose I have two toons (on one realm): Foo (marked as main)
and Bar (not main)

I want to change my main, and for that I do something like: Foo.IsMain = false Bar.IsMain = true

I'm using Linq-to-SQL.

Question: how can I make said transition without entering a state when I have either more than one or zero main toons.

Note: I have a materialized view (filtered by IsMain = 1) that defines a composite key (OwnerId, RealmId), as such when I do main-toon transition I get "unique key violation" exception from that materialized view.

I have tried using transaction, but I still get exception on 'db.SubmitChanges()'.

Option 2: I suspect that I have the flaw in the database design (what's the name of this flaw?). Should I create a mapping table?

OwnerId, RealmId -> ToonId
+1  A: 

Question 1

You need to use transactions.

 DBDataContext db = new DBDataContext();
 using (TransactionScope ts = new TransactionScope())
 {
     try
     {
         Toon toon1 = db.Toons.First(p => p.ToonId == 4);
         Toon toon2 = db.Toons.First(p => p.ToonId == 5);
         toon1.IsMain = false;
         toon2.IsMain = true;
         db.SubmitChanges();
         ts.Complete();
     }
     catch (Exception e)
     {
        Console.WriteLine(e.Message);
     }
}

Question 2

You need to think about your design a bit more.

Step 1

It is a player who has a main Toon so I would put a MainToonId on your Users table and remove your IsMain from your Toon table.

After this your tables would be Users ( Existing Stuff, MainToonId ), Realms ( Existing Stuff), Toons (ToonId, RealmId, OwnerId, ToonName )

Step 2

As you've suggested you could probably move the Realm/Owner/Toon link to its own table (or even ToonOwner and ToonRealm tables). The link information is related to Toon, but it's not an identifying part of being a Toon. The resolution of this "flaw" is normalisation and a database that needs it is described as "needing normalisation". However, this step is completely optional in this case and may be over-normalising for your needs.

If you did go with one matchup table, the new table would have a PK of the 3 values combined (if you are a natural keyer) otherwise a standard PK and a UNIQUE on the 3 values (if you are not a natural keyer). You should also have a UNIQUE on ToonId as each Toon can presumably only belong to one Realm or Owner.

Graphain
thank you. toon1.IsMain = false; toon2.IsMain = true; from your code works, but if I swap it (set new main, then clear current) I get the exception - which is the thing I want to avoid (because it feels wrong). Also I can't put MainToonId on Users table, since each realm (that has at least one toon) must have a main toon.
1) I got the impression each User had a Main Toon, not each Realm. If it's each realm then simply move the MainToonId to the Realm table instead. 2) Okay, its within a transaction so you dont want to set new main then clear current otherwise youll get an error (two mains). You want to clear then set new main. However, dont be worried that the database will ever have no main toon for both, as sure while within your transaction it does, noone else can see the database in this state (if either calls fails the whole transaction fails and reverts to how it was).
Graphain
During a transaction you still do things *as if* they were in order (they are) but you don't have to worry about the database being in the state in between for anyone else.
Graphain
Suppose there are two realms : X and Y. And I have toons A,B on realm X, and toons C,D on realm Y. I will _have_ to have a main on each toon. E.g. toon B will be main on X, and C be main on Y. So there are #realms * #users mains.
typo ^ meant to say "I will _have_ to have a main on each REALM."
Okay it sounds like you are saying each user has a main on each realm. In that case leave the db design as is (or move the user/realm/ismain out to its own table with a toonid ref, probably unnecessary in this case). Otherwise the transaction code is identical and works for you.
Graphain