Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
epswing
Nov 4, 2003

Soiled Meat
Ok this is really blowing my mind. VB.NET.

code:
    Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim conn As OdbcConnection
        Dim cmd As OdbcCommand
        Dim sql As String
        Dim rdr As OdbcDataReader

        Dim connString As String = _
    "Driver={Microsoft Access Driver (*.mdb)};" & _
    "Dbq=db1.mdb;pwd=mypass"
        conn = New Odbc.OdbcConnection(connString)
        conn.Open()

        sql = "SELECT * FROM t"
        cmd = New OdbcCommand(sql, conn)
        rdr = cmd.ExecuteReader()
        While (rdr.Read())
            MsgBox(rdr.GetValue(0).ToString() & " " & rdr.GetValue(1).ToString())
        End While

        conn.Close()
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim conn As OdbcConnection
        Dim cmd As OdbcCommand
        Dim sql As String

        Dim connString As String = _
    "Driver={Microsoft Access Driver (*.mdb)};" & _
    "Dbq=db1.mdb;pwd=mypass"
        conn = New Odbc.OdbcConnection(connString)
        conn.Open()

        sql = "INSERT INTO t(col1, col2) VALUES ('hi', 'there')"
        cmd = New OdbcCommand(sql, conn)
        MsgBox(cmd.ExecuteNonQuery())

        conn.Close()
    End Sub
Here are two subroutines attached to two buttons. The first selects * from t and prints the first two columns with a space in between. The second inserts 'hi' into the first column and 'there' into the second column.

I run the program, click the first button. I get nothing as expected. Click the second button, and a msgbox with "1" comes up (so 1 row affected). I click it again, and again, 1 row affected. I click the first button and I get two consecutive msgboxes, both with "hi there". Great!

I go look at the access mdb file...no rows have been added. I close the program, run it again, click the first button. Nothing.

The inserts make it into the database, I can select them out, but it's like they're not REALLY there. Or something. I'm really confused.

Adbot
ADBOT LOVES YOU

epswing
Nov 4, 2003

Soiled Meat
Ok after more than an hour, I finally got it. VS dumps all your resources to a ./bin/Debug subdirectory, INCLUDING THE LOCAL DATABASE you may be operating on. That's why there was always a fresh copy of the db when I started the program.

Unbelievable.

epswing
Nov 4, 2003

Soiled Meat
Crossposting from the VCS thread: http://forums.somethingawful.com/showthread.php?threadid=3113983&pagenumber=15#post382874231

Background: I'm coming from eclipse/gedit/vi. Normally I just set my VCS to ignore the .settings folder and whatever else eclipse dumps into the root of my project. The idea is that anyone could use any ide/editor at any time, so don't push any ide-specific files into the repo. Makes sense.

In the case of a C++ app written with VS 2010, I get the feeling that the code is pretty much married to the ide/compiler. No one is ever going to use anything but VS 2010. Thing is, any time there's any kind of change to the settings, or compiler options, or anything really, a few ide-specific files change such as MyProject.sln, MyProject.vcproj, etc.

So...what do I do here? If I ignore these, my co-workers can't launch their project upon checkout. If I include them, a good number of commits will contain ide-specific files/changes.

epswing
Nov 4, 2003

Soiled Meat
^^^ E: :siren: awesome

Mercurial (and VisualHG)

vvv E: :siren: much thanks, dudes

epswing fucked around with this message at 21:10 on Oct 5, 2010

epswing
Nov 4, 2003

Soiled Meat
Thank you SO much :D

epswing
Nov 4, 2003

Soiled Meat
What was wrong?

epswing
Nov 4, 2003

Soiled Meat
What do you generally use for persistence? I've read about NHibernate and the Entity Framework, both of which sound heavy, and have played around with SubSonic, which seems nice but it's too early to tell.

I'm using .NET 4, C#, WPF, MSSQL, putting together an app that requires nothing crazier than a many-to-many relationship.

What ORM would you use if you were starting from scratch?

epswing
Nov 4, 2003

Soiled Meat

Derpes Simplex posted:

I'm rewriting my MVC apps to use NHibernate with the Fluent interface (coming from LINQ2SQL) and it seems pretty nice. A little heavy, yes, but easy enough to abstract away.

wwb posted:

Subsonic is neat and I haven't had much seat time with recent versions, but I'd probably run with the Entity Framework over that -- just as easy, if not easier, to use, lots of great tool support, etc. nHibernate definitely supports some more complex scenarios, but even with fluent NH there is quite a bit of a learning curve there.

I read a little more about Linq2Sql and it seems nice, straightforward, easy to work with. SubSonic's activerecord implementation seems to be working well, but something provided by Microsoft seems more stable/safe. Is there something 'wrong' with Linq2Sql? E: Also I should mention, this is for a desktop app (1 user), not a busy web server, so the ORM doesn't need to be able to handle ten billion requests per second.

wwb posted:

Most important thing to keep in mind, IMHO, is that your auto-persistance capable domain entities should never go down the wire, stick to simple view models. Might sound like alot of repetition, but the amount of pain you save long-term is well worth it.

Can you elaborate on 'domain entities should never go down the wire'? E: Are you talking about what this guy is saying?

epswing fucked around with this message at 15:07 on Oct 25, 2010

epswing
Nov 4, 2003

Soiled Meat
Why would you want to do this?

epswing
Nov 4, 2003

Soiled Meat

Ugg boots posted:

Because var is loving retarded

Worst argument.

I'm not calling you crazy. I'm new to .NET/C#, and var seems convenient, especially for whatever LINQ decides to return. What's wrong with var?

epswing
Nov 4, 2003

Soiled Meat
I'm building an app which will have several sections. This seems to fit the NavigationWindow/Page model, which allows users to use back/next buttons like a browser.

I'd also like to include a number of buttons along the top of the window, each of which take the user directly to a certain Page (think bookmarks), and a StatusBar along the bottom of the window.

Conceptually I'd like to do this:

code:

<Window
    x:Class="WinDispatchWPF.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="my awesome program" Width="640" Height="480">
    
    <DockPanel LastChildFill="True">
        <ToolBar DockPanel.Dock="Top">
            <Button Name="btnDashboard" Content="Dashboard" Click="btnDashboard_Click" />
            <Button Name="btnUsers" Content="Users" Click="btnUsers_Click" />
        </ToolBar>
        <StatusBar DockPanel.Dock="Bottom">
            <TextBlock Text="Ready"/>
        </StatusBar>
        <NavigationWindow ...>
           ...
        </NavigationWindow ...>
    </DockPanel>
</Window>
The NavigationWindow being the last child would take up the center space of the Window.

It seems I'm not allowed to put Windows inside of Windows (NavigationWindow extends Window). Ok fine, so maybe I'll just make my MainWindow a NavigationWindow, give it a DockPanel in which I'll put the ToolBar/Statusbar...oh wait, I can't, because "the type NavigationWindow doesn't support direct content".

I realize I can put the ToolBar and StatusBar in each Page, but there must be some way I can avoid duplication here.

epswing
Nov 4, 2003

Soiled Meat

Begby posted:

There are probably about 50 ways to skin that cat.

Right! I'm looking for The Right Way.

I'll try what you've mentioned, thanks.

vvv Edit: That's exactly what I was looking for. Much thanks!

epswing fucked around with this message at 21:21 on Oct 26, 2010

epswing
Nov 4, 2003

Soiled Meat
I'm building a management app. I like dependency injection, and ORM. I'm currently using C#, WPF, MSSQL, and SubSonic 3.0.0.4's ActiveRecord implementation.

I have a Core project, and potentially many UI projects (WPF, WinForms, ASP.NET) which reference Core. Core talks to the DB and contains Service classes like UserService, which is where methods like Delete(User user) live, which throw exceptions if you're doing something silly like trying to delete yourself, for example.

Now picture a user edit form in WPF, click the save button, and somewhere in WPF-land a User newUser object is constructed. Currently that User object is an ActiveRecord object generated by SubSonic, and I pass it to UserService's SaveUser(User u) method, which, after some checking, will call u.Save().

I don't like the fact that I can skip the business access layer (UserService in Core) and save an object directly from my UI with newUser.Save(). I think I want the UI to only have access to POCOs, but this doesn't fit with the ActiveRecord pattern, unless I build some sort of translation machinery to get from SubSonic's User to a POCO User.

(aside: SubSonic's User class IS actually a POCO generated by a .tt file (doesn't extend anything), but it's rife with ActiveRecord goodness that I don't want the UI to have access to)

I could put business logic in SubSonic's generated ActiveRecord classes (User), making it safe to do user.Save() from anywhere, for example a partial method BeforeSave() called automatically by Save() (this would involve some .tt file editing), but then the business logic is spread across many ActiveRecord classes, the business access layer (Service classes) kind of fades away, I lose dependency injection, testing becomes harder, etc.

----

Is there a pattern I can use to prevent the UI from circumventing the business access layer, while still using SubSonic?

Should I be using SubSonic's SimpleRepository pattern instead of ActiveRecord?

Am I using the wrong tool? Should I check out NHibernate, LinqToSql, etc?

I'm trying to avoid 'planing to throw one away' (Brooks) by actually doing things The Right Way the first time around. I don't think I'm writing any groundbreaking code, I'm sure this is a solved problem. I'm new to .NET and maybe there are some obvious answers I don't know about.

(Thanks for reading my novel.)

epswing
Nov 4, 2003

Soiled Meat
I'm starting a new project, and I've been using WPF for the front end. Is WPF not a sure thing? Apparently they're been no new releases since February, and there's a thread on their discussion board about abandonment.

:smith:

epswing
Nov 4, 2003

Soiled Meat
Whoops, the title of the page (Windows Presentation Foundation (WPF)) and the link (http://wpf.codeplex.com) threw me.

Edit: Speaking of which, I have a binding question.

This works:

code:
<DataGrid Name="grdUsers" SelectionChanged="grdUsers_SelectionChanged">
	<DataGrid.Columns>
		<DataGridTextColumn Binding="{Binding Path=Username}" Header="Username" Width="*" />
		<DataGridTextColumn Binding="{Binding Path=Email}" Header="Email" />
	</DataGrid.Columns>
</DataGrid>
<StackPanel Name="editPanel">
	<TextBlock Text="Username" />
	<TextBox Name="txtUsername" Text="{Binding Path=Username}" />
	<TextBlock Text="Email" />
	<TextBox Name="txtEmail" Text="{Binding Path=Email}" />
</StackPanel>

private void grdUsers_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
	user = (User)e.AddedItems[0];
	editPanel.DataContext = user;
}
But if I try this in XAML...

code:
<DataGrid Name="grdUsers">
	<DataGrid.Columns>
		<DataGridTextColumn Binding="{Binding Path=Username}" Header="Username" Width="*" />
		<DataGridTextColumn Binding="{Binding Path=Email}" Header="Email" />
	</DataGrid.Columns>
</DataGrid>
<StackPanel Name="editPanel" DataContext="{Binding Path=SelectedValue, ElementName=grdUsers}">
	<TextBlock Text="Username" />
	<TextBox Name="txtUsername" Text="{Binding Path=Username}" />
	<TextBlock Text="Email" />
	<TextBox Name="txtEmail" Text="{Binding Path=Email}" />
</StackPanel>
When I click a row in the grid, the two textboxes in the stackpanel are properly set. If I click another row in the grid, nothing happens (text stays the same in the textboxes, no exceptions thrown). What am I doing wrong? How do I cause the StackPanel to update its DataContext on subsequent selection changes to the DataGrid?

epswing fucked around with this message at 22:08 on Nov 4, 2010

epswing
Nov 4, 2003

Soiled Meat
I've tried binding to Path=SelectedValue (updates the textboxes the first time i click a row in the grid, ignores subsequent selections), Path=SelectedItem (same), and Path=SelectedIndex (no reaction to any grid selecting).

I've tried each of those with Mode=TwoWay, no difference.

This is supposed to be easy, right? In my codebehind file, I have a List<User> users, and a User user. I want to display the users in the grid, and when one is clicked, see their details in a group of textboxes. I can't do this entirely in XAML with bindings?

(And why the heck would it work the first time I click on a row, and ignore subsequent selections??)

epswing
Nov 4, 2003

Soiled Meat
Ah sweet jesus...

code:
<Style x:Key="CellStyle" TargetType="DataGridCell">
	<Setter Property="FocusVisualStyle" Value="{ x:Null }" />
	<Setter Property="Focusable" Value="False" />             <------fuckme
</Style>
Stolen from http://wpf.codeplex.com/Thread/View.aspx?ThreadId=39588 (see last post)

I was trying to remove this http://d.imagehost.org/view/0014/wpfgridcellselection.png

This is entirely my fault for not posting the entire xaml file.

gently caress.

epswing
Nov 4, 2003

Soiled Meat
Yes that's exactly what I wanted, thanks so much. :)

Edit: another binding question.

Part of binding is so I don't have to do this:

code:
<TextBox Name="txtUsername"/>
<TextBox Name="txtEmail"/>

User user = GetUserFromSomewhere();
txtUsername = user.Username;
txtEmail = user.Email;
So I bind like this and the two textboxes are indeed filled in, and changes to user are reflected properly in each textbox:

code:
<StackPanel Name="panel">
  <TextBox Text="{Binding Path=Username}" />
  <TextBox Text="{Binding Path=Email}" />
</StackPanel>

User user = new User() { Username = "test", Email = "test@test.com" };
panel.DataContext = user;
user.Username = "hey I can see this";
But if I want to load another user into those textboxes, I'm going to be doing user = GetUserFromDb(); somewhere, which replaces the original user variable, and doesn't update the textboxes. I was going to implement INotifyPropertyChanged but I don't think that will help me, I'm not trying up keep particular fields up to date, I'm trying to fill a group of textboxes with an object.

I'm hunting for a method to say "hey, textboxes, the thing you were bound to has been replaced by another instance of the same type, refresh yourself!" but maybe this is entirely the wrong way to be going about this.

I watched that 90 minute video about MVVM, I see what they're getting at, but my app is small enough that MVVM feels like total overkill.

epswing fucked around with this message at 17:25 on Nov 6, 2010

epswing
Nov 4, 2003

Soiled Meat

PDP-1 posted:

The problem I'm having is in the (???) area, I can't figure out how to cast to a type when all I have is a string representation of the type name.

Can you use implicit typing here? I'm not familiar with Activator.CreateInstance, so I'm not sure if the compiler can figure out what you want:

code:
string typeName = xmlReader.GetAttribute("Type");
var logger = Activator.CreateInstance(Type.GetType(typeName));

epswing
Nov 4, 2003

Soiled Meat
And if you cast it, like it suggests?

code:
string typeName = xmlReader.GetAttribute("Type");
ILogger logger = (ILogger)Activator.CreateInstance(Type.GetType(typeName));
You don't care which logger you get, as long as it implements ILogger, right?

epswing
Nov 4, 2003

Soiled Meat
If someone can point me in the right direction, I have a number of TextBoxes bound to the nearest DataContext. I'd like to highlight a nearby Save button if any properties change. The DataContext object implements INotifyPropertyChanged, I believe I'm supposed to use this, but I'm not sure how.

Further to that, there's also a ListBox containing lots of these objects, and I'd like to highlight the rows which are "modified but not saved".

I'm looking at triggers, but most examples seem to look at properties on the object, and I have an IsDirty() method (from SubSonic's ActiveRecord implementation). I'm not sure if I can key a trigger off the return value of a method, and haven't yet found a reliable/working example of this.

(Of course, I can manually do this by hooking each textbox to a codebehind method, and changing the colour of the button there, but I figure there's an elegant way to do this with data binding. Right?)

epswing
Nov 4, 2003

Soiled Meat

fankey posted:

If you can modify the object which is being used as the DataContext, the easiest way would be to add an IsDirty property and set that when any other property changes.

Ok, fair enough, I've added an IsDirty property. Linking a button's background property to the property of the 'current' object in a list in a datacontext is where I'm stuck.

The DataContext is set to an ObservableCollection<User>, and User contains an IsDirty property. The ListBox's ItemsSource is also set to the same collection. I'm using the ICollectionView to move around the list (view.MoveCurrentToPrevious(), view.MoveCurrentToNext(), etc).

For the button that's supposed to change background if a User in the ObservableCollection<User> changes, should I be doing something like <Button ... Background="{Binding ..."/>? Or should I be reading more about Style Triggers? Same question for the ListBox (I want to highlight rows that have changed).

epswing
Nov 4, 2003

Soiled Meat

Begby posted:

The easiest thing here is bind the color to an observable property that has the color value. When the value of the property changes, the background color will change as well without any fancy trigger stuff.

This is exactly, precisely what I want to do. I have no idea how to do it. What's in CS and what's in XAML? I bought and am reading WPF Unleashed, which is turning out to be a great book, but in certain cases I still need handholding :/

Edit: I started doing something like this...

code:
<Style x:Key="PropertyChangedStyle" TargetType="Button">
	<Style.Triggers>
		<DataTrigger Binding="{Binding Source=WhatGoesHere? Path=ThisIsDirty}" Value="True">
			<Setter Property="Background" Value="Red" />
		</DataTrigger>
	</Style.Triggers>
</Style>

<Button Style="{StaticResource PropertyChangedStyle}" ... />
...but I'm not sure what or how to point the source to "the current item in an observable list which is set as the datacontext".

Begby posted:

You may want to look into the MVVM pattern at some point, you don't have to use it in its entirety, but it sure has some good methods at moving a lot of the logic and "magic" out of your view. It sure is nice to have nothing but bindings in your XAML and no style triggers or anything like that.

Yep, I'm half-using MVVM, putting objects into a ViewModel object and setting the ViewModel itself as the DataContext of the Window (or Page, in my specific case). But for the purposes of this example, assume I'm not doing any MVVM and all my controls have names, etc.

epswing fucked around with this message at 21:06 on Nov 12, 2010

epswing
Nov 4, 2003

Soiled Meat

fankey posted:

EDIT : Rereading your question, if you have a ListBox of all your items and you want to bind your button to the currently selected item you can do something like
code:
  <ListBox x:Name="theList"....
  
  <Button>
    <Button.Style>
      <Style TargetType="Button">
        <Style.Triggers>
          <DataTrigger Binding={Binding ElementName=theList, Path=SelectedItem.IsDirty}" Value="True">
             ....

I had high hopes for this but it doesn't work.

epswing fucked around with this message at 18:14 on Nov 15, 2010

epswing
Nov 4, 2003

Soiled Meat

bobua posted:

I don't understand delegates. There, I said it.

DISCLAIMER: I'm pretty new to C#, take anything I say with a grain of salt. Part of why I'm trying to answer your question is to validate my own understanding. If I'm really loving poo poo up, please correct me. (Also note that this is a simple example, and I probably wouldn't do it this way in practice.)

One way to understand where delegates are useful is callbacks. Here's a quick and dirty example. Say I'm writing an input validation library. Aside from specific rules that check for string length, or matching regex, I also want to allow users of my library to define custom rules, by passing me a function that accepts some params and returns bool.

code:
public abstract class Rule
{
    public abstract bool Validate();
}

public class CustomRule : Rule
{
    // this is the function signature i need
    public delegate bool CustomRuleFunction(params object[] args);
    
    public CustomRuleFunction TheFunction { get; private set; }
    public object[] TheArgs { get; private set; }

    public CustomRule(CustomRuleFunction function, params object[] args)
    {
        TheFunction = function;
        TheArgs = args;
    }

    public override bool Validate()
    {
        return TheFunction(TheArgs);
    }
}
So now I can create and use a custom rule like this:

code:

public void SaveUser()
{
    string somePassword = GetThePasswordFromSomeInput();
    
                         // function...     args...
    Rule r = new CustomRule(IsGoodPassword, somePassword);
    
    call r.Validate() somewhere here
}

// this matches the delegate above
private static bool IsGoodPassword(params object[] args)
{
    string candidatePassword = (string)args[0];
    
    if (the password is good enough by my standards) {
        return true;
    } else {
        return false;
    }
}
So I'm passing IsGoodPassword (which takes an array of objects and returns bool) to the CustomRule constructor, and it works because that constructor is expecting a CustomRuleFunction, which is a delegate, which takes an array of objects and returns bool (so they match). I can then assign that to TheFunction and TheArgs, and call TheFunction(TheArgs) later.

bobua posted:

Every time I see one used, I think... 'why not just call the method directly?'

Because my validation library doesn't know what crazy rules you might want. So I let you define your own, by creating a "function type" that returns bool and accepts an array of objects. So you can create all sorts of zany custom rule functions, and pass them to me, and because they match the delegate I specified, I can execute them.

epswing fucked around with this message at 00:29 on Nov 16, 2010

epswing
Nov 4, 2003

Soiled Meat
Why do you need the ability to decrypt?

epswing
Nov 4, 2003

Soiled Meat
This is silly, but if you only want to avoid Intern Isabelle from just looking at plaintext passwords...

http://dotnetpad.net/ViewPaste/LkEfHP2YfUucTVWQLdx1yw

code:
        public static string EncodeBase64(string str)
        {
            byte[] bytes = System.Text.UTF8Encoding.UTF8.GetBytes(str);
            return System.Convert.ToBase64String(bytes);
        }

        public static string DecodeBase64(string str)
        {
            byte[] bytes = System.Convert.FromBase64String(str);
            return System.Text.UTF8Encoding.UTF8.GetString(bytes);
        }
Note: this simply prevents someone's eyeball from physically reading a plaintext password on a screen, and offers zero actual security.

epswing
Nov 4, 2003

Soiled Meat
Prelude: Sorry for basically spamming this thread with my problems. I really, really appreciate the help you've all offered so far. Please note that replying with "you should really just read link X and/or book Y" is perfectly acceptable. I'm reading through this and this currently, trying to get up to speed.

This is more of an architecture question. The setup looks like MSSQL <-> Linq to Sql <-> ServiceClass <-> WPFClass.

Say I have tables Employees (Id, AddressId, Name) and Addresses (Id, Street, City), and classes Employee and Address as you would expect.

EmployeeService.cs (the service class)
code:
public void SaveEmployee(Employee employee)
{
	using (var db = new DbDataContext())
	{
		employee.CheckValid();
		db.Employees.Attach(employee);
		db.Refresh(System.Data.Linq.RefreshMode.KeepChanges, employee);
		db.SubmitChanges();
	}
}

public List<Employee> GetAllEmployees()
{
	using (var db = new DbDataContext())
	{
		DataLoadOptions opts = new DataLoadOptions();
		opts.LoadWith<Employee>(x => x.Address);
		db.LoadOptions = opts;
		
		return (from x in db.Employee
				orderby x.Name
				select x).ToList();
	}
}
EmployeeEditor.xaml.cs (the wpf class)
code:
public EmployeeEditor(EmployeeService employeeService)
{
	Employees = new ObservableCollection<Employee>(employeeService.GetAllEmployees());
	DataContext = Employees;
	lstEmployees.ItemsSource = Employees; // a listbox
}
EmployeeEditor.xaml (the wpf gui)
code:
<TextBox Text="{Binding Path=Name}" />
<TextBox Text="{Binding Path=Address.Street}" />
<TextBox Text="{Binding Path=Address.City}" />
This almost works. I get both the employee name and the address street/city displayed in textboxes, so I feel like I'm on the right track. When I try to save an employee I get "An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported."

1) Where should the DbDataContext be created and destroyed? In the service class, or in the wpf class? Should I keep one big DbDataContext open throughout the lifetime of the application (this seems wrong)?

2) Assuming I'm using a new DbDataContext per "request" (list these entities, save this entity, etc) then the act of reattaching an entity loaded from another DataContext will occur pretty much every mouse-click. Is this not how Linq to Sql is supposed to work?

3) Should I be specifically enabling/disabling db.ObjectTrackingEnabled?

4) When saving, I have to go through this Attach(employee); Refresh(KeepChanges, employee); process (instead of just calling Attach(employee, true); which indicates the entity is in a modified state) otherwise I get "An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy." I don't understand what this means (the docs aren't very helpful here), and the fact that I'm circumventing it with a special call to Refresh makes me think I'm doing something improperly.

5) Is Linq to Sql bad for an N-tier environment/application? Am I an idiot for not using Entity Framework, or something else? I've read this Linq to Sql vs Linq to Entities article, and it seems like Linq to Sql matches what we want better than EF.

6) I'm just creating a CRUD here, why isn't this trivial? Do I just need to buckle down and read more?

---

E: Thinking about this error some more: ("An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.") Basically this is telling me that it's NOT supported to load an entity with one DataContext and save it with another. So I need to use one DataContext for both of these operations. So the one DataContext to rule them all DOES need to persist for the lifetime of the application. This makes no goddamn sense to me :(. Why would the Attach and Refresh methods even exist, then?

epswing fucked around with this message at 22:08 on Nov 18, 2010

epswing
Nov 4, 2003

Soiled Meat
Just for chuckles, I created a static void Copy(Employee src, ref Employee dst). If I take an entity that was originally produced by another L2S DataContext, modified by the user via WPF, and copy it into a new object of the same type with this Copy method, Attach now works.

Which is ridiculous.

Honestly, how is this supposed to work.

epswing
Nov 4, 2003

Soiled Meat
So you're saying I should create UI-friendly classes that are basically identical (in terms of public properties) to my DB-friendly classes. Meaning after pulling objects out of the db, I'd need to create the entire object graph myself, and do something like...

uientity.Id = dbentity.Id;
uientity.Name = dbentity.Name;
uientity.Street1 = dbentity.Street1;
uientity.Street2 = dbentity.Street2;
uientity.City = dbentity.City;
uientity.State = dbentity.State;
uientity.Zip = dbentity.Zip;
uientity.Country = dbentity.Country;

...for every entity on the way to the UI, and...

dbentity.Id = uientity.Id;
dbentity.Name = uientity.Name;
dbentity.Street1 = uientity.Street1;
dbentity.Street2 = uientity.Street2;
dbentity.City = uientity.City;
dbentity.State = uientity.State;
dbentity.Zip = uientity.Zip;
dbentity.Country = uientity.Country;

.. on the way to the DB?

I mean I see what you're saying, keeping db-related entities away from the ui, but doesn't the above look silly? Is there something that can intelligently do this for me? It reminds me of My First PHP Application in the late 90s. :(

E: Here's an example of a db entity generated by VS 2010: http://pastebin.com/u6SYf9LE . It looks pretty clean to me, something I would initially think would be usable in an UI context (it even implements INotifyPropertyChanged).

epswing fucked around with this message at 16:47 on Nov 19, 2010

epswing
Nov 4, 2003

Soiled Meat
While I know my UI will map 1-1 with my DB (I'm designing both)...I'm starting to understand the benefits of decoupling the two.

I watched the automapper video. Awesome stuff.

PS: I still think Linq to Sql DataContext being unable to attach an entity created with another DataContext is ridiculous.

epswing
Nov 4, 2003

Soiled Meat

Dietrich posted:

Why are your UI and your DB classes the same?

You either designed your DB around your UI's concerns or designed your UI around your DB's concerns.

Take a look at the pastebin link above. Doesn't it look reasonable to use that "DB class" in your UI, at first glance? My "UI class" will look nearly identical.

Don't get me wrong, I am now going to decouple and write UI classes, but...do you see what I'm saying? Especially since those DB classes are generated automatically. Now I'm going to have to add/change a column in my db, regenerate the DB class, add a property to my UI class, and modify my LHS/RHS mapping code to convert between DB and UI classes. I was hoping to avoid that overhead. Automapper at least helps with the conversions.

epswing fucked around with this message at 18:37 on Nov 19, 2010

epswing
Nov 4, 2003

Soiled Meat

wwb posted:

Ever try and write an object persistance layer? I'll guess not . . . . .

I haven't. So I agree I'm an outsider, just musing. I understand complex change-tracking is going on under the hood. But the idea itself of saying "here's an object generated by your framework, please save it," I feel that this concept should be simple for the client (me) to implement. Instead I am forced to create a new identical object, and save that. This, to me, smells bad. Maybe if L2S offered a Detach method...

wwb posted:

Yes, we all see what you are saying. I suspect most of us were here at some point. But we've all learned that the flexibility is worth the small bit of overhead -- you really don't want persistence polluting the application.

Right. Point taken and appreciated!

epswing
Nov 4, 2003

Soiled Meat
I know how N-tier works, I'm quite familiar with the separation of concerns (in fact, I wrote an MVC framework in XQuery and gave a talk on it at a conference in San Fran last year). I just haven't done it with a statically typed language, ORM, and the (now clear) necessity for separate types between boundaries.

Admittedly, my brain is somewhat infected by web-based dynamically typed languages here.

E: Oh snap, don't get me wrong, thanks for your help/reply, I don't at all mean to downplay your post. Especially the testing bit...it's a world of difference to know the major thing you just modified hasn't broken something on the other side of the system. I should write more unit tests :(

epswing fucked around with this message at 04:22 on Nov 20, 2010

epswing
Nov 4, 2003

Soiled Meat

AnomalousBoners posted:

but the one for the spectrum analyzer costs $1700

I know $1700 might sounds like a lot, but if you make between $40-$50 per hour, that's only about 40 hours of work. If you're going to take more than a week to do it yourself, you might as well just buy the pre-written fully functional software, rather than frankensteining something awful that barely works.

epswing
Nov 4, 2003

Soiled Meat
What's so bad about Equals?

epswing
Nov 4, 2003

Soiled Meat

dwazegek posted:

If "A.Equals(B) != B.Equals(A)", then your Equals implementation isn't correct.

Right.

Maybe "just use .Equals and move on with your life" has already sunk in because I learned Java in school, and string comparison required the use of .equals.

epswing
Nov 4, 2003

Soiled Meat
nm

epswing fucked around with this message at 04:36 on Dec 3, 2010

epswing
Nov 4, 2003

Soiled Meat
Do the contents of Builder not fit well with a relational database? I have to ask myself "is there a good reason to store xml as text in a column of an rdbms?" because it doesn't make too much sense.

Adbot
ADBOT LOVES YOU

epswing
Nov 4, 2003

Soiled Meat
So I've got this nice little block of code to run the contents of an sql against the db (SQL Server 2008 R2 Express):

code:
using (var db = new DbDataContext())
{
	try
	{
		db.Connection.Open();
		db.Transaction = db.Connection.BeginTransaction(IsolationLevel.Serializable);
		
		using (var command = db.Connection.CreateCommand())
		{
			command.Transaction = db.Transaction;
			command.CommandText = File.ReadAllText("/path/to/file.sql");
			command.ExecuteNonQuery();
		}
		
		db.Transaction.Commit();
	}
	catch (Exception e)
	{
		db.Transaction.Rollback();
		throw e;
	}
	finally
	{
		db.Connection.Close();
	}
}
Isn't it pretty? It's all transactionified, which is a big requirement here.

Except my sql file contains naughty words like SET and IF and GO. Here's an excerpt.

code:
SET NUMERIC_ROUNDABORT OFF
GO
SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS ON
GO
IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID('tempdb..#tmpErrors')) DROP TABLE #tmpErrors
GO
CREATE TABLE #tmpErrors (Error int)
GO
SET XACT_ABORT ON
GO
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
GO

PRINT N'Creating [dbo].[Plants]'
GO
CREATE TABLE [dbo].[Plants]
(
[Id] [int] NOT NULL IDENTITY(1, 1),
[AddressId] [int] NULL,
[Name] [varchar] (50) COLLATE Latin1_General_CI_AS NOT NULL
)
GO
IF @@ERROR<>0 AND @@TRANCOUNT>0 ROLLBACK TRANSACTION
GO
IF @@TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
GO
PRINT N'Creating primary key [PK_Plants] on [dbo].[Plants]'
GO
ALTER TABLE [dbo].[Plants] ADD CONSTRAINT [PK_Plants] PRIMARY KEY CLUSTERED  ([Id])
GO
IF @@ERROR<>0 AND @@TRANCOUNT>0 ROLLBACK TRANSACTION
GO
IF @@TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
GO
PRINT N'Creating index [IX_PlantName] on [dbo].[Plants]'
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_PlantName] ON [dbo].[Plants] ([Name])
GO
IF @@ERROR<>0 AND @@TRANCOUNT>0 ROLLBACK TRANSACTION
GO
IF @@TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
GO

PRINT N'Creating [dbo].[Drivers]'
...
Actually I just realized this sql script has transactions already. Hm.

Anyways things choke on command.ExecuteNonQuery(); saying there's incorrect syntax near "GO" and "IF" and whatnot.

So.

I have an sql file, and a connection to a database, and I want to run the sql against the database in a transaction. This should be simple, right? So before I go on some codehunt for Microsoft.SqlServer.Management.Smo (I don't think I even have this dll, somehow...), do you have any suggestions/advice on how to run such a script? Do I really need to have my clients install an SMO redistributable to get this working? Alternatively do I really need to tokenize the script on "GO" (which sounds really messy)?

Update: Snagged a few dlls from the Microsoft SQL Server directory in Program Files, trying to run something like this code. Couldn't, because "Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information". So I "fixed" that by adding useLegacyV2RuntimeActivationPolicy="true" to the startup element of my app.config, now the code runs but gives me "An exception occurred while executing a Transact-SQL statement or batch." which is funny because the same sql file runs fine if I paste it into SSMS.

:negative:

epswing fucked around with this message at 17:56 on Dec 17, 2010

  • Locked thread