MVCScaffolding and Database Sync for constantly-changing model - asp.net-mvc-3

I have used MVCScaffolding from Nuget Package Manager and followed the brief tutorial on how it works.
It seems simple enough,and when I run
Scaffold Controller Team –Repository -Force it will create all the repository pattern stuff surrounding "Team".
However, in an attempt(and success) to break this, I decided to add in an additional field to the "Team" class (myRandomField).
As I expected, when I compiled I got an error in the MVC View which was:
The model backing the 'MvcApplication1Context' context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance. For example, the DropCreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data.
Obviously this error is because I have updated the model (Code-first??) and the DB is now 'out of sync' with my model.
What is the best approach to get around this issue? Is there an easy way to have to DB sync with the model - I plan on doing a lot of editing to my models as the project I am starting will be rolled out gradually (So doing a complete database rebuild each time is out the question) Is code first the right approach for me in this case? I really like this plugin/tool would be a shame not to use it.

jad,
as mentioned in my comment above, if you're 'happy' to lose all exisiting data in your DB, then you can add the following into your global.asax:
[Conditional("DEBUG")]
private static void InitializeDb()
{
using (var db = new YourContext())
{
// double indemnity to ensure just sqlserver express
if (db.Database.Connection.DataSource != null
&& db.Database.Connection.DataSource.IndexOf("sqlexpress",
StringComparison.InvariantCultureIgnoreCase) > -1)
{
// Initializer code here
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<YourContext>());
}
}
}
and then call this from Application_Start(), i.e.
protected void Application_Start()
{
InitializeDb();
ViewEngines.Engines.Add(new MobileViewEngine());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
if you wish to retain the data, then you'll have to use a data migration tool. I used the Red Gate tools (SQL Comparison Bundle) to perfom this. Basically, this look at your new schema and your old schema and migrates existing data over into the new schema, ready for test and deployment, all without touching the original db file.
I think this should work well for you.

Related

Make the entire symfony app read-only

I need to set up a live demo of a Symfony app.
How can I make everything read-only? The users should be able to try all the features but not make any persistent change visible to others.
I could remove the INSERT and UPDATE privileges to the mysql user, but that would be an ugly error 500 when they try to save something...
Quick and dirty way to make your entire app Read-Only.
AppBundle/EventSubscriber/EntitySubscriber.php
namespace AppBundle\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\PreFlushEventArgs;
class EntitySubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return [
'preFlush'
];
}
public function preFlush(PreFlushEventArgs $args)
{
$entityManager = $args->getEntityManager();
$entityManager->clear();
}
}
services.yml
app.entity_subscriber:
class: AppBundle\EventSubscriber\EntitySubscriber
tags:
- { name: doctrine.event_subscriber, connection: default }
I suppose that you've already made it. But if not:
Use dummy database. Copy it from original DB. Let them play. Drop it when you don't need it.
If you have no access to database creation and drop you can still do the trick. Just add temporary prefixes to table names in Doctrine entities. No need to rewrite the entire app, just a few lines. Run migrations to create new tables. Drop them whenever you want later.
Use virtual machine. Make a snapshot before the show. Roll back to the saved snapshot after the show.
These are more or less easy ways and they are platform independent.
Changing this based on the Symfony app level might have one of two disadvantages. You either do not save anything and thus your demo is not working so nice to show it to the customer. Or you have to do to much manipulations with the code and throw this huge work away right after the show.
Maybe you can use Session to do that or Memcache that you can implement in Symfony (Some examples are available on the web). Hope this will help.

Is there a way to add a filter to the Entity Framework layer to exclude "IsArchived" records?

I have records marked up as "IsArchived". I am looking for an expedient way to exclude these records from a current MVC3 / EF3 web application.
Is there a way to add some kind of "IsArchived" filter to the EF layer. In my case I have a seperate Model project with tables/views represented as POCO entities, and the mappings contained in the CSDL and SSDL files.
Huge thanks for any assistance.
EDIT:
I am using "ObjectContext" and not "DbContext", mainly due to the Data Modelling tool that I am using. This tool creates the context and POCO files.
I am wondering whether I can edit this context file like the following:
public ObjectSet<StdOrg> StdOrg
{
get
{
if ((_StdOrg == null))
{
_StdOrg = base.CreateObjectSet<StdOrg>("StdOrg");
// new line below. Got cast error tween both sides.
_StdOrg = (ObjectSet<StdOrg>) _StdOrg.Where(r => r.IsArchived == false);
}
return _StdOrg;
}
}
Take a look at this http://www.matthidinger.com/archive/2012/01/25/a-smarter-infrastructure-automatically-filtering-an-ef-4-1-dbset.aspx
Basically a filtering DBSet implementation that the example basically shows being used for Soft Delete. We use it without issue in our App.
However we are using DBcontext so not sure how this would work with Object Context or how it could be adapted

EF Code First Migrations - how does it remember the previous model change?

So i'm using Entity Framework Code First Migrations.
I make a change to my model, add a new manual migration and it gets the up script wrong.
So I delete the migration, and at the same time that I am not going to change it the way I thought. Upon deletion of the migration class and a reset of the model (ie setting it back as it was) I then change my model again.
When I generate a new migration, this migration acts as If it is changing from the one that I deleted.
how does entity framework code first know the last model state if you clean and delete a migration?
And how do you reset this?
In your database, under "Tables / System Tables" (assuming you use SQL Management Studio), edit the Table __MigrationHistory.
Got me stumbled too, after I had deleted all migrations *.cs files and still VS "knew" about old migrations!!
You probably didn't delete the Designer file underneath it that contains information about automatic migrations up until that point.
http://msdn.microsoft.com/en-US/data/jj554735
Run the Add-Migration AddBlogRating command...
The migration also has a code-behind file that captures some metadata. This metadata will allow Code First Migrations to replicate the automatic migrations we performed before this code-based migration. This is important if another developer wants to run our migrations or when it’s time to deploy our application.
The code-behind is a file like 201206292305502_AddBlogRating.Designer.cs, underneath the manual migration class you created. It looks like:
public sealed partial class AddBlogRating : IMigrationMetadata
{
string IMigrationMetadata.Id
{
get { return "201206292305502_AddBlogRating"; }
}
string IMigrationMetadata.Source
{
get { return "H4sIAAAAAAAEAOy9B2AcSZ...=="; }
}
string IMigrationMetadata.Target
{
get { return "H4sIAAAAAAAEAOy9B2AcSZ...=="; }
}
}
​
Those 2 strings are base64 encoded dumps of your entire model prior to the migration and after it. The idea being that anything prior to the first manual migration logged was automatic, so when you apply all this to a fresh DB it can look and say:
Manual1
Manual2
Check Source to determine goal model before Manual1, apply using the Automatic approach, apply Manual1, check Source on Manual2, use automatic approach to get there, apply Manual2, finally use automatic approach to get from there to the current compiled model state.

Refresh LINQ db-connection object

My collegue helped me with starting programming in c# although I had no experience but I like it. All went well until I came across some problems we both can't fix. He uses SQL himself but started me up with LINQ.
To do a LINQ-query I use this object : _oDBConnection (in clsApplication.cs)
So when opening the programm this object is built. But it creates some problems:
When saving a new object (putting data into table), I cannot load those values with a query. I need to restart the programm.
When running 2 instances of the programm, one is not getting the latest values when changed in the other (but it is showing the new ones but not the changed ones!)
According to these problems I can only conclude that when I call clsApplication._oDBConnection.tblTAble a second time it is not relinking again to the db but is giving me the old db-states back.
This is the code he built:
public static DBReservationDataContext _oDBConnection;
private static frmMain _fMain;
public clsApplication()
{
Thread.CurrentThread.Name = "main";
clsErrorLog.ErrorLocation = "C:\\Software\\ErrorLog";
clsErrorLog.setPassword("*****");
clsErrorLog.LockApplication += new clsErrorLog.dLockApplication(lockApplication);
_oDBConnection = new DBReservationDataContext();
_fMain = new frmMain();
_fMain.Show();
}
What can I do to fix this problem?
Example:
although present in the database, it crashes on this query because the entity with id == iID is not found. But the iID is correct and it does exist in the database. The query will work after closing and restarting the programm. Then the clsApplication is called again.
public clsReservationDetail(int iID)
:this()
{
_oReservationDetail = (from oReservationDetailQuery in clsApplication._oDBConnection.tblReservationDetails
where oReservationDetailQuery.ID == iID
select oReservationDetailQuery).First();
}
thx in advance
Your data context will have a Refresh method which will clear any cached results, and should allow your query to complete with no problems
The static keyword makes it so that you have one reference per AppDomain. That is the wrong way to use DataContext instances.
Each instance of DataContext tracks the objects it has seen. This is for consistency. If you get a Customer instance with CustomerID = 4 from one query, you should get the same Customer instance from another query that returns the CustomerID = 4 record.
If you want to see the changes in the database, you must
1) Tell the datacontext to stop tracking changes. This must be done before the first query and makes the datacontext instance unable to SubmitChanges (since it can't track them anymore).
OR
2) Tell the datacontext to Refresh each instance you suspect has changed. If you do that, you should specify how resolve the conflict between your local changes and the remote changes - the simplest way of resolving this conflict is to have no local changes.
OR
3) (The right way) Make a new DataContext instance and load the record with that!
Also note: Since DataContext implements IDisposable, you are required to call Dispose when you are done with each instance even when exceptions occur. The using block is a good way to get that to happen.

Updating LINQ to SQL object causing System.NotSupportedException

I get System.NotSupportedException: An attempt has been made to Attach or Add an entity that is not new perhaps having been loaded from another DataContext when I want to update an object's with child entities.
The scenario is like this:
I have a SubscriberProvider that allows me to create subscribers.
var provider = new SubscriberProvider(); // Creates a new repository with own datacontext
var newSubscriber = new Subscriber
{
EmailAddress = emailAddress,
};
newSubscriber.Interests.Add(new Interest{
Id=1,
Name="cars"
});
provider.Subscribe(newSubscriber);
On a normal subscribe page, this works fine.
Now I have a linq2sql Member class(retrievable by a MemberRepository) and I want to extend it to have a helper subscribe method like so:
var repository = new MembershipRepository(); // Holds its own datacontext
var member = repository.Get("member1");
member.Subscribe(); // transfer member's info and interests to subscriber's table
The exception occurs when SubscriberProvider tries to add interests of the member.
Commenting out
newSubscriber.Interests.Add(new Interest{
Id=1,
Name="cars"
});
will make member.Subscribe() work.
member.Subscribe() is simply:
public void Subscribe(bool emailIsVerified, bool receiveEmails, bool sendDoubleOptIn)
{
var provider = new MailingListProvider();
provider.Subscribe(EmailAddress, emailIsVerified, receiveEmails, CountryId, sendDoubleOptIn, ConvertInterests(MemberInterests.ToList()));
}
So what's causing the child entities(Interests) to lose their datacontext when I do member.Subscribe() and how do I go about fixing this?
It seems there's some code missing here, but I'll take a stab anyway because I think I have an idea what's going on.
If you have a different DataContext created for your MembershipRepository and your SubscriberRepository you're going to have issues related to entities "having been loaded from another DataContext." (as the Exception you posted points out). You can't just take an object out of one DataContext and save it into another.
It seems that you might have an architectural issue here. Should these 2 repositories actually be separate? If so, should they have completely different DataContexts? I would probably recommend using Dependency Injection to inject your DataContexts into your Repositories. Then you can decide how to cache your DataContexts.
That line of code you commented out is being flagged by the DataContext as a new record, even though it's likely that the record already exists, due to the error message.
Change the line to:
newSubscriber.Interests.Add(DataContext.Interests.Where(a => a.Id == 1).Single());
Now, the DataContext will know that record is one that already exists, and won't try to add it as an Insert to the ChangeSet.
Found the solution to this myself. Turns out it was the ConvertInterests() method causing it. The converted interest object had an invalid declaration which compiled ok.
Thinking the code was simple enough, I didn't create a test for it. I should have known better!

Resources