The Setup:
I have a large form with many fields that are collected to update a Product object. So in the ASPX page the user changes the fields that need updating and they hit submit. In the code behind I do something like this;
Dim p as New MyCompany.Product()
p = p.GetProductById(ProductID)
I extend the Product partial class of Linq to SQL to add this method (GetProductById) to the object
p.Name = txtName.Text
p.SKU = txtSKU.Text
p.Price = txtPrice.Text
...
p.Update()
This is an Update method in the extended Product partial class. I update the database, send emails and update history tables so i want this method to do all those things.
There are 50 more fields for the project so obviously it would be ridiculous to have a method that collects all 50 fields (and I don't want to go that route anyway bc it's harder to debug IMO)
The Problem:
If I get the Product via Linq to SQL using a DataContext then I can never update it again because it errors about not being able to attach and entity that's already attached to another DataContext.
The Question:
SO if I get an object through a method in my BLL, update it in the ASPX page and then try to send the updates through the BLL again to update the database, how should I go about doing this?
Regardless of LINQ-to-SQL or not, here's what I do. Upon submission, I search for the item (it should be quick if it is a single item using the PK), my DAL returns a data object, and I use reflection to map each element in the page with corresponding properties in the data object. My DAL only updates items that changed.
I think what you have to do is the same, gathering all values and submitting them. If LinqToSql is not smart enough to determine what changed then it may not be the best alternative.
Related
So I have the following model structure in my Django App:-
class SuperModel(models.Model):
f1 = models.CharField()
f2 = models.CharField()
class Model(SuperModel):
f3 = models.CharField()
class OverrideModel(models.Model):
fpk = models.OneToOneField(Model, primary_key=True)
f1 = models.CharField()
f2 = models.CharField()
Basically, in my application, the fields f1 and f2 in the Model table contain user information that I have entered. The user has the ability to override this information and any changes he/she makes in the data is stored in the OverrideModel table (because I do not want to lose the information that I had entered first). Think of it as me creating user profiles earlier while now I want the user to be able to edit his/her own profile without losing the information that I had entered about them.
Now, since the rest of my application (views/templates etal) work with the field names in the Model class, what I want is to create a view of the data that fetches the field f1 from the override table if it exists, otherwise it should pickup f1 from the table it used to earlier without resorting to a raw queryset.
I will describe everything I have considered so far so that some of the other constraints I am working with become clear:-
Model.objects.annotate(f1=Case(When(overridemodel__f1__isnull=True, then=F('f1')), default=F('overridemodel__f1'))).
This throws the error that the annotate alias conflicts with a field already in the table.
Model.objects.defer('f1').extra(select={'f1': 'CASE WHEN ... END'}, tables=..., where=...).
This approach cannot be applied because I could not figure out a way to apply an outer join using extra. The override model may not have a row corresponding to each model row. Specifying the override table in the tables clause performs a cross product operation which combined with where can be used to perform an inner join, not an outer join (although I'd be happy to be proved wrong).
EDIT: I have realized that select_related might be able to solve the above problem but if I filter the queryset generated by Model.objects.select_related('overridemodel').defer('f1').extra(select={'f1': 'CASE WHEN ... END'}, tables=..., where=...) on the field f1, say qs.filter(f1='Random stuff') the where clause for the filter query uses the Model.f1 field rather than the f1 field generated in extra. So this approach is also futile.
Using Model.objects.raw() to get a raw queryset.
This is a non-starter because the Django ORM becomes useless after using raw and I need to be able to filter / sort the model objects as part of the application.
Defining methods/properties on the Model class.
Again, I will not be able to use the same field names here which involves hunting through code for all usages and making changes.
Creating a view in the database that gives me what I want and creating an unmanaged model that reads the data from that view.
This is probably the best solution for my problem but having never used an unmanaged model before, I'm not sure how to go about it or what pitfalls I might encounter. One problem that I can think of off the top of my head is that my view always has to be kept in sync with the models but that seems a small price to pay compared to hunting through the codebase and making changes and then testing to see if anything broke.
So, there you have it. As always, any help / pointers will be greatly appreciated. I have tried to provide as minimal an example as possible; so if any more information is required I'll be happy to provide it.
Also, I am using Django 1.8 with MySQL.
I realized that there is no easy canonical way to solve my problem. Even with using option 5 (creating a view that is ORM manipulated using an unmanaged Model), I would lose the related query names on the original model that are being used in my filtering / sorting.
So, for anyone else with a similar problem I would recommend the approach I finally went with which is not keeping an OverrideModel but an OverriddenModel which keeps the values that are overridden whenever the user makes changes and updating the original Model with the override values so that the model always contains the values on which filtering / querying is going to occur
I am trying to find a solution the above problem.
Basically, I have a model 'Job', which has and belongs to many contacts and locations. When a user begins creating a new job, they should be able to begin adding locations and contacts to the new record. The problem is when I try save the record. How can a user associate location and contact records to a job record that doesn't yet have an ID? The error message I get on the create action is this,
For every contact I try to add I get a
Validation failed: Job contacts is invalid
Which i guess comes from the fact a Job ID doesn't exist at the time of assignment.
The parameters that come through to the controller are all within the job_params, which is how I want them to be received. This is aided with SimpleForm, which has the handy 'association' method for habtm records. If possible I would like to keep everything within a form object using simple form (not using field_tags etc). So for example the contact field looks like this.
=f.association :contacts, collection: job.client.contacts, as: :check_boxes
So what are my options? Is there something I can do differently?
I had considered the following:
Creating a record in the new action and setting it with a flag to state its invalidity until the necessary fields have been populated. At which point it can become a 'proper' record, but this feels as though it has the potential to get quite messy.
Using STI to start a dummy record?
Maybe I'm way off piste with all this, but even some direction would be greatly appreciated.
Assign the users id to the job. Then, when looking for jobs for the user do
jobs = Job.where("user_id = ?", user.id)
I've been using the ADO.NET Strogly-Typed DataSet model for about 2 years now for handling CRUD and stored procedure executions. This past year I built my first MVC app and I really enjoyed the ease and flexibility of LINQ. Perhaps the biggest selling point for me was that with LINQ I didn't have to create "Insert" stored procedures that would return the SCOPE_IDENTITY anymore (The auto-generated insert statements in the DataSet model were not capable of this without modification).
Currently, I'm using LINQ with ASP.NET 3.5 WebForms. My inserts are looking like this:
ProductsDataContext dc = new ProductsDataContext();
product p = new product
{
Title = "New Product",
Price = 59.99,
Archived = false
};
dc.products.InsertOnSubmit(p);
dc.SubmitChanges();
int productId = p.Id;
So, this product example is pretty basic, right, and in the future, I'll probably be adding more fields to the database such as "InStock", "Quantity", etc... The way I understand it, I will need to add those fields to the database table and then delete and re-add the tables to the LINQ to SQL Class design view in order to refresh the DataContext. Does that sound right?
The problem is that any new fields that are non-null are NOT caught by the ASP.NET build processes. For example, if I added a non-null field of "Quantity" to the database, the code above would still build. In the DataSet model, the stored procedure method would accept a certain amount of parameters and would warn me that my Insert would fail if I didn't include a quantity value. The same goes for LINQ stored procedure methods, however, to my knowledge, LINQ doesn't offer a way to auto generate the insert statements and that means I'm back to where I started.
The bottom line is if I used insert statements like the one above and I add a non-null field to my database, it would break my app in about 10-20 places and there would be no way for me to detect it. Is my only option to do a solution-side search for the keyword "products.InsertOnSubmit" and make sure the new field is getting assigned?
Is there a better way?
Thanks!
Create a NON-Null field, with a default value of whatever. This way the old rows will have a default value you know of, letting you go back and add values to the table.
I have a question related to this one. I don't want to do a calculation (aggregation), but I need to get display values from an association. In my C# code, I can directly reference the value, because the foreign key constraint made Linq generate all the necessary wiring.
When I specify the IQueryable as the Gridview datasource property, and reference something that is not a column of the primary entity in the result set, I get an error that the column does not exist.
As a newbie to Linq, I am guessing the assignment implicitely converts the IQueryable to a list, and the associations are lost.
My question is, what is a good way to do this?
I assume that I can work around this by writing a parallel query returning an anonymous type that contains all the columns that I need for the gridview. It seems that by doing that I would hold data in memory redundantly that I already have. Can I query the in-memory data structures on the fly when assigning the data source? Or is there a more direct solution?
The gridview is supposed to display the physician's medical group associations, and the name of the association is in a lookup table.
IQueryable<Physician> ph =
from phys in db.Physicians
//from name in phys.PhysicianNames.DefaultIfEmpty()
//from lic in phys.PhysicianLicenseNums.DefaultIfEmpty()
//from addr in phys.PhysicianAddresses.DefaultIfEmpty()
//from npi in phys.PhysicianNPIs.DefaultIfEmpty()
//from assoc in phys.PhysicianMedGroups.DefaultIfEmpty()
where phys.BQID == bqid
select phys;
(source: heeroz.com)
So, based on Denis' answer, I removed all the unneeded stuff from my query. I figured that I may not be asking the right question to begin with.
Anyways, the page shows a physician's data. I want to display all medical group affiliations in a grid (and let the user insert, edit, and update affiliations). I now realize that I don't need to explicitly join in these other tables - Linq does that for me. I can access the license number, which is in a separate table, by referencing it through the chain of child associations.
I cannot reference the medical group name in the gridview, which brings me back to my question:
AffiliationGrid.DataSource = ph.First().PhysicianMedGroups;
This does not work, because med_group_print_name is not accessible for the GridView:
A field or property with the name 'med_group_print_name' was not found on the
selected data source.
Again, bear with me, if it is all too obvious that I don't understand Linq ... because I don't.
Your query seems strange. You should try to simply display
ph = from phys in db.Physicians
where phys.BQID == bqid
select phys;
in your grid. That should work.
Also, why the calls to Load()? If the DataContext is not disposed when the grid is binding, you should not need it.
If you still have issues, can you please post the error message you get, that would help...
Part 2
The problem is that you have the name is effectively not in the PhysMedGroup. You need to navigate one level down to the MedGroupLookup to access the name, since it is a property of that class.
Depending on the technology you are using (it seems to be either WinForms or Web Forms), you will need to configure your data-binding to access MedGroupLookup.med_group_print_name.
I am trying to inherit from my generated datacontext in LinqToSQL - something like this
public class myContext : dbDataContext {
public System.Data.Linq.Table<User>() Users {
return (from x in base.Users() where x.DeletedOn.HasValue == false select x);
}
}
But my Linq statement returns IQueryable which cannot cast to Table - does anyone know a way to limit the contents of a Linq.Table - I am trying to be certain that anywhere my Users table is accessed, it doesn't return those marked deleted. Perhaps I am going about this all wrong - any suggestions would be greatly appreciated.
Hal
Another approach would to be use views..
CREATE VIEW ActiveUsers as SELECT * FROM Users WHERE IsDeleted = 0
As far as linq to sql is concerned, that is just the same as a table. For any table that you needed the DeletedOn filtering, just create a view that uses the filter and use that in place of the table in your data context.
You could use discriminator column inheritance on the table, ie. a DeletedUsers table and ActiveUsers table where the discriminator column says which goes to which. Then in your code, just reference the Users.OfType ActiveUsers, which will never include anything deleted.
As a side note, how the heck do you do this with markdown?
Users.OfType<ActiveUsers>
I can get it in code, but not inline
Encapsulate your DataContext so that developers don't use Table in their queries. I have an 'All' property on my repositories that does a similar filtering to what you need. So then queries are like:
from item in All
where ...
select item
and all might be:
public IQueryable<T> All
{
get { return MyDataContext.GetTable<T>.Where(entity => !entity.DeletedOn.HasValue); }
}
You can use a stored procedure that returns all the mapped columns in the table for all the records that are not marked deleted, then map the LINQ to SQL class to the stored procedure's results. I think you just drag-drop the stored proc in Server Explorer on to the class in the LINQ to SQL designer.
What I did in this circumstance is I created a repository class that passes back IQueryable but basically is just
from t in _db.Table
select t;
this is usually referenced by tableRepository.GetAllXXX(); but you could have a tableRepository.GetAllNonDeletedXXX(); that puts in that preliminary where clause to take out the deleted rows. This would allow you to get back the deleted ones, the undeleted ones and all rows using different methods.
Perhaps my comment to Keven sheffield's response may shed some light on what I am trying to accomplish:
I have a similar repository for most
of my data access, but I am trying to
be able to traverse my relationships
and maintain the DeletedOn logic,
without actually calling any
additional methods. The objects are
interrogated (spelling fixed) by a StringTemplate
processor which can't call methods
(just props/fields).
I will ultimately need this DeletedOn filtering for all of the tables in my application. The inherited class solution from Scott Nichols should work (although I will need to derive a class and relationships for around 30 tables - ouch), although I need to figure out how to check for a null value in my Derived Class Discriminator Value property.
I may just end up extended all my classes specifically for the StringTemplate processing, explicitly adding properties for the relationships I need, I would just love to be able to throw StringTemplate a [user] and have it walk through everything.
There are a couple of views we use in associations and they still appear just like any other relationship. We did need to add the associations manually. The only thing I can think to suggest is to take a look at the properties and decorated attributes generated for those classes and associations.
Add a couple tables that have the same relationship and compare those to the view that isn't showing up.
Also, sometimes the refresh on the server explorer connection doesn't seem to work correctly and the entities aren't created correctly initially, unless we remove them from the designer, close the project, then reopen the project and add them again from the server explorer. This is assuming you are using Visual Studio 2008 with the linq to sql .dbml designer.
I found the problem that I had with the relationships/associations not showing in the views. It seems that you have to go through each class in the dbml and set a primary key for views as it is unable to extract that information from the schema. I am in the process of setting the primary keys now and am planning to go the view route to isolate only non-deleted items.
Thanks and I will update more later.