When loading data with SubSonic (either using ActiveRecord or a collection), only records with IsDeleted set to false will load. How can I show those rows that have been deleted?
For example, deleting an Employee with:
Employee.Delete(1)
Now employee 1 is marked as deleted. Now I want the option to undo the delete and / or show a list of deleted employees, how can I do that? Either it will be undone if the user accidentally deleted the employee, or they want to go to a 'trash' list with previously deleted employees (i.e. only those with IsDeleted set to true).
Edit:
Using SubSonic 2.2
ActiveRecord doesn't have this built in. You'll need to set up additional queries for this. You didn't specify 2.2 or 3.0. This is 2.2 syntax.
public EmployeeCollection FetchAll(bool isDeleted)
{
return new SubSonic.Select().From(Employee.Schema).Where(IsDeletedColumn).IsEqualTo(isDeleted).ExecuteAsCollection<EmployeeCollection>();
}
public EmployeeCollection GetTrashList()
{
return FetchAll(true);
}
I was running into this problem yesterday with subsonic 3 and decided to alter the T4 templates to "fix" it. I added these definitions for a new function LogicalAll. As an alternative you could change the definitions of All to this but then you would have no way of getting at the deleted records.
public static IQueryable<<#=tbl.ClassName#>> LogicalAll(string connectionString, string providerName) {
<#if(tbl.HasLogicalDelete()){#>
var results = GetRepo(connectionString,providerName).GetAll();
if(results == null)
{
return new List<<#=tbl.ClassName#>>().AsQueryable();
}
return results.Where(x=> x.<#=tbl.DeleteColumn.CleanName#> == false);
<#} else {#>
return GetRepo(connectionString,providerName).GetAll();
<# } #>
}
public static IQueryable<<#=tbl.ClassName#>> LogicalAll() {
<#if(tbl.HasLogicalDelete()){#>
var results = GetRepo().GetAll();
if(results == null)
{
return new List<<#=tbl.ClassName#>>().AsQueryable();
}
return results.Where(x=> x.<#=tbl.DeleteColumn.CleanName#> == false);
<#} else {#>
return GetRepo().GetAll();
<# } #>
}
I'm running into the same issue.
I'm working in a project that's using the ActiveRecord scheme. I can retrieve logically deleted records just fine by querying for them specifically.
The problem is that the ActiveRecord generated classes do not have any properties or methods to modify the deleted status of the record.
It should be as simple as setting "IsDeleted = false" but this functionality doesn't seem to exist.
-- Nevermind on this. I regenerated my ActiveRecord class, and now my Deleted column is accessible by calling code. Must've gotten stuck somewhere.
it is easy to show these rows simply by creating a query by hand instead of using the collection loaders
ie.
ProductsCollection col = new ProductsCollection().Load();
becomes
ProductsCollection col = new Select()
.From(Tables.Products)
.ExecuteAsCollection<ProductsCollection>();
This should load everything for you. Futhermore you can set the options yourself:
ProductsCollection col = new Select()
.From(Tables.Products)
.Where(Products.Columns.IsDeleted).IsEqualTo(false)
.And(Products.Columns.IsDeleted).IsEqualTo(null)
.ExecuteAsCollection<ProductsCollection>();
This would load all the nulls (if you forgot to set your default value on your column to false) AND it will also load the falses
Hope this helps
Related
I recently upgraded my application from ASP.NET MVC 3 and EF 4 to ASP.NET MVC 5 and EF 6. I have several repository functions that I'm using for CRUD functionality.
I haven't changed anything, but I'm suddenly receiving this error if I try to add a record to my entities:
Cannot insert explicit value for identity column in table "Photos" when IDENTITY_INSERT is set to OFF
Here's an example of one of these methods
public void SavePhoto(Photo photo)
{
if (photo.PhotoID == 0) // new photo
_entities.Photos.Add(photo);
else // edit photo
{
_entities.Photos.Attach(photo);
_entities.Entry(photo).State = EntityState.Modified;
}
_entities.SaveChanges();
}
The PhotoID column in my database for this table is set to be the Identity. When this worked before, it would just increment the PhotoID column value based on the last entry (as expected).
Is there a better way to do this now with EF 6?
Is your PhotoId column is "int" type and annotated with "DateGenerated identity"? If so, the following code will error. Because EF might thinking you are inserting 0 into identity column.
if (photo.PhotoID == 0) // new photo
_entities.Photos.Add(photo);
Use id == 0 to check whether it is a new photo or not is not a good practice, actually it is a "bug", because say you have 3 records in the system(which default could mean your photoid is not greater than 4), And somehow you PhotoID was manipulated as 100 in the backend, now when your code run, your code will set its state as Modified. And EF might throw error for you, or EF might try to insert it for you instead of editing.
So I would suggest to use follow code
var photo = _entities.Photos.Find(photo.PhotoId)
if (photo == null) {
//your code to add photo
}
else
{
//your code to set the the modal state to modified.
}
Just for my curiosity (and future knowledge), how does Entity Framework 5 decide when to create a new object vs. referencing an existing one? I might have just been doing something wrong, but it seems that every now and then if I do something along the lines of:
using (TestDB db = new TestDB())
{
var currParent = db.Parents.Where(p => p.Prop == passedProp).FirstOrDefault();
if(currParent == null) {
Parent newParent = new Parent();
newParent.Prop = passedProp;
currParent = newParent;
}
//maybe do something to currParent here
var currThing = db.Things.Where(t => t.Prop == passedPropTwo).FirstOrDefault();
currThing.Parent = currParent;
db.SaveChanges();
}
EF will create a new Parent in the database, basically a copy of the currParent, and then set the Parent_ID value of currThing to that copy. Then, if I do it again (as in, if there's already two of those parents), it won't make a new Parent and instead link to the first one. I don't really understand this behavior, but after playing around with it for a while something like:
using (TestDB db = new TestDB())
{
var currParent = db.Parents.Where(p => p.Prop == passedProp).FirstOrDefault();
if(currParent == null) {
Parent newParent = new Parent();
newParent.Prop = passedProp;
currParent = newParent;
}
//maybe do something to currParent here
var currThing = db.Things.Where(t => t.Prop == passedPropTwo).FirstOrDefault();
currThing.Parent = db.Parents.Where(p => p.ID == currParent.ID).First();
db.SaveChanges();
}
seemed to fix the problem. Is there any reason this might happen that I should be aware of, or was there just something weird about the way I was doing it at the time? Sorry I can't be more specific about what the exact code was, I encountered this a while ago and fixed it with the above code so I didn't see any reason to ask about it. More generally, how does EF decide whether to reference an existing item instead of creating a new one? Just based on whether the ID is set or not? Thanks!
If your specific instance of your DBContext provided that specific instance of that entity to you, then it will know what record(s) in the database it represents and any changes you make to it will be proper to that(those) record(s) in the database. If you instantiate a new entity yourself, then you need to tell the DBContext what exactly that record is if it's anything but a new record that should be inserted into your database.
In the special scenario where you have multiple DBContext instances and one instance provides you this entity but you want to use another instance to work with and save the entity, then you have to use ((IObjectContextAdapter)firstDbContext).ObjectContext.Detach() to orphan this entity and then use ((IObjectContextAdapter)secondDbContext).ObjectContext.Parents.Attach() to attach it (or ApplyChanges() if you're also editing it - this will call Attach for you).
In some other special scenarios (your object has been serialized and/or you have self-tracking entities), some additional steps may be required, depending on what exactly you are trying to do.
To summarize, if your specific instance of your DBContext is "aware" of your specific instance of an entity, then it will work with it as if it is directly tied to that specific row in the database.
I am caching a collection of activerecord rows (subsonic). When I look at the cache with ANTS Memory Profiler, I can see that some related tables to the activerecord I would like to cache are cached as well. This makes the cached items very large, because of the additionally (not needed) cached tables.
Any ideas on how to prevent this?
I believe you will have to modify or remove the lazy-loading of relationships in the Active Record classes.
The lazy-loading behavior is generated by the ActiveRecord.tt template, starting at line 300 in the most current version:
#region ' Foreign Keys '
<#
List<string> fkCreated = new List<string>();
foreach(FKTable fk in tbl.FKTables)
{
if(!ExcludeTables.Contains(fk.OtherTable)){
string propName=fk.OtherQueryable;
if(fkCreated.Contains(propName))
{
propName=fk.OtherQueryable+fkCreated.Count.ToString();
}
fkCreated.Add(fk.OtherQueryable);
#>
public IQueryable<<#=fk.OtherClass #>> <#=propName #>
{
get
{
var repo=<#=Namespace #>.<#=fk.OtherClass#>.GetRepo();
return from items in repo.GetAll()
where items.<#=CleanUp(fk.OtherColumn)#> == _<#=CleanUp(fk.ThisColumn)#>
select items;
}
}
<#
}
}
#>
#endregion
I would try removing this entire region and seeing if the excessive caching is resolved. Of course, if you rely on the lazy-loading behavior you will have to address that now.
We are rying to use WF with multiple tracking participants which essentially listen to different queries - one for activity states, one for custom tracknig records which are a subclass of CustomTrackingRecord.
The problem is that we can use both TrackingParticipants indivisually, but not together - we never get our subclass from CustomTrackingRecord but A CustomTrackingRecord.
If I put bopth queries into one TrackingParticipant and then handle everythign in one, both work perfectly (which indicates teh error is not where we throw them).
The code in question for the combined one is:
public WorkflowServiceTrackingParticipant ()
{
this.TrackingProfile = new TrackingProfile()
{
ActivityDefinitionId = "*",
ImplementationVisibility = ImplementationVisibility.All,
Name = "WorkflowServiceTrackingProfile",
Queries = {
new CustomTrackingQuery() { Name = "*", ActivityName = "*" },
new ActivityStateQuery() {
States = {
ActivityStates.Canceled,
ActivityStates.Closed,
ActivityStates.Executing,
ActivityStates.Faulted
}
},
}
};
}
When using two TrackingParticipants we have two TrackingProfile (with different names) that each have one of the queries.
in the track method, when using both separate, the lines:
protected override void Track(TrackingRecord record, TimeSpan timeout)
{
Console.WriteLine("*** ActivityTracking: " + record.GetType());
if (record is ActivityBasedTrackingRecord)
{
System.Diagnostics.Debugger.Break();
}
never result in the debugger hitting, when using only the one to track our CustomTrackingRecord subclass (ActivityBasedTrackingRecord) then it works.
Anyone else knows about this? For now we have combined both TrackingParticipants into one, but this has the bad side effect that we can not dynamically expand the logging possibilities, which we would love to. Is this a known issue with WWF somewhere?
Version used: 4.0 Sp1 Feature Update 1.
I guess I encounterad the exact same problem.
This problem occurs due to the restrictions of the extension mechanism. There can be only one instance per extension type per workflow instance (according to Microsoft's documentation). Interesting enough though, one can add multiple instances of the same type to one workflow's extensions which - in case of TrackingParticipant derivates - causes weird behavior, because only one of their tracking profiles is used for all participants of the respective type, but all their overrides of the Track method are getting invoked.
There is a (imho) ugly workaround to this: derive a new participant class from TrackingParticipant for each task (task1, task2, logging ...)
Regards,
Jacob
I think that this problem isn't caused by extension mechanism, since DerivedParticipant 1 and DerivedParticipant 2 are not the same type(WF internals just use polymorphism on the base class).
I was running on the same issue, my Derived1 was tracking records that weren't described in its profile.
Derived1.TrackingProfile.Name was "Foo" and Derived2.TrackingProfile.Name was null
I changed the name from null to "Bar" and it worked as expected.
Here is a WF internal reference code, describing how is the Profile selected
// System.Activities.Tracking.RuntimeTrackingProfile.RuntimeTrackingProfileCache
public RuntimeTrackingProfile GetRuntimeTrackingProfile(TrackingProfile profile, Activity rootElement)
{
RuntimeTrackingProfile runtimeTrackingProfile = null;
HybridCollection<RuntimeTrackingProfile> hybridCollection = null;
lock (this.cache)
{
if (!this.cache.TryGetValue(rootElement, out hybridCollection))
{
runtimeTrackingProfile = new RuntimeTrackingProfile(profile, rootElement);
hybridCollection = new HybridCollection<RuntimeTrackingProfile>();
hybridCollection.Add(runtimeTrackingProfile);
this.cache.Add(rootElement, hybridCollection);
}
else
{
ReadOnlyCollection<RuntimeTrackingProfile> readOnlyCollection = hybridCollection.AsReadOnly();
foreach (RuntimeTrackingProfile current in readOnlyCollection)
{
if (string.CompareOrdinal(profile.Name, current.associatedProfile.Name) == 0 && string.CompareOrdinal(profile.ActivityDefinitionId, current.associatedProfile.ActivityDefinitionId) == 0)
{
runtimeTrackingProfile = current;
break;
}
}
if (runtimeTrackingProfile == null)
{
runtimeTrackingProfile = new RuntimeTrackingProfile(profile, rootElement);
hybridCollection.Add(runtimeTrackingProfile);
}
}
}
return runtimeTrackingProfile;
}
When using a linqdatasource to update my sqlexpress database everything is displayed ok, but when I try retrieving the contents manually using:
public static IQueryable<MarkingScheme> listMarkingSchemes(string moduleID, string academicYear)
{
return
from m in feedbackDB.MarkingSchemes
where m.moduleID == moduleID && m.academicYear == academicYear
orderby m.schemeID descending
select m;
}
I get back a previous version of the data!! weirdly though, when I retrieve only specific parts of the same table, using:
public static IQueryable listNames(string moduleID, string academicYear)
{
return
from m in feedbackDB.MarkingSchemes
where m.moduleID == moduleID && m.academicYear == academicYear
orderby m.schemeID descending
select new { m.schemeID, m.assignmentName };
}
I get back the updated version!!!
Does anyone have any idea why this is happening, and how I can always get the latest version of the data when doing manual queries? I've tried setting the 'update check' option in the dataclasses.dbml file to either 'Always' and 'WhenChanged', but it didn't seem to work...
I finally got it to work, I had to manually set the Auto-Sync method to 'Always' for all affected columns in the datacontext!
Thanks again to #Josh for all his help, really appreciated!!