Extending SubSonic Generator - subsonic

Is it possible to extend the SubSonic generator without modifying it's code?
I would like to add my own custom methods that i can use inside the templates. Somthing similair like the Utility.GetVariableType method.

You can't extend the built in templates, but you can replace them with your own templates without changing SubSonic.dll. See the templateDirectory parameter here: http://subsonicproject.com/docs/Generated_Classes/#Customizing_Active_Record
An example configuration would be:
<SubSonicService defaultProvider="Northwind" enableTrace="true"
templateDirectory="C:\Program Files\SubSonic\SubSonic 2.0.3\Templates\MVC">
<providers>
<clear/>
<add name="Northwind" type="SubSonic.SqlDataProvider, SubSonic"
connectionStringName="Northwind" generatedNamespace="Northwind"/>
</providers>
</SubSonicService>
You can get the current version of the built-in ActiveRecord templates from here.

I've found the solution for my own problem :).
I can now extend SubSonic with functionality i need in the templates without needing to rebuild or change any of the SubSonic code itself.
It works for what i wanted to do and i think it can be usefull for others as well so here it is:
Create a new class library SubSonicHelper. Mine has a class looking like this:
using System;
using System.Collections.Generic;
using System.Text;
namespace Helpers.SubSonic
{
public class GeneratorHelper
{
public bool IsColumnAllowed(string columnName)
{
return columnName.Length == 1 ||
(columnName.Length > 1 &&
(!(columnName[0].ToString().Equals("_") &&
columnName[columnName.Length - 1].ToString().Equals("_"))))
}
}
}
Build the assembly and copy SubSonicHelper.dll to your subsonic project.
Setup your SubSonic project to use your own templates using the templateDirectory parameter.
Edit your own templates and at the following after the const bool showGenerationInfo = false;
System.Reflection.Assembly a = System.Reflection.Assembly.LoadFile(
System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "SubSonicHelper.dll"));
object instance = a.CreateInstance("Helpers.SubSonic.GeneratorHelper");
Type type = instance.GetType();
After this you have an instance of the GeneratorHelper that you can use inside the template. For accessing the methods you need to do the following:
Create an array of objects for the parameters of the method you want to use. I have the columnName parameter which i set to col.propertyName. This is inside the foreach (TableSchema.TableColumn col in cols) loop in the Update method.
Call the method you want to use with the object array as argument.
Check the result object to see the result of the method.
object[] arg = new object[]{col.PropertyName};
object isColumnAllowedResult = type.InvokeMember("IsColumnAllowed", System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod, null, instance, arg);
if (Convert.ToBoolean(isColumnAllowedResult))
That's it! Now i can extend the SubSonicHelper class with other methods i want to use inside the template.

The short answer is no. If you come up with something useful, submit a patch and it will likely be integrated into the core. You can submit patches here: http://code.google.com/p/subsonicproject/issues/list

can you not import a dll in the template?
like
<%# Import namespace="NewHelpers.Utilities"%>
and then call the function or create an instance of the object

Related

How to create a ReSharper 8.X Custom Macro that can fetch and process the containing type name

ReSharper 8.X ships with a macro that fetches the "Containing Type Name", but what I want to do is manipulate that name. I'm using this in a Visual Studio 2013 Web API project, and I want a template that takes the class name and builds the URL that has to be called. So, for example, suppose I have this:
public class AnnouncementController : ApiController
{
//Want to put a template here!
[HttpGet]
public HttpResponseMessage GetActiveAnnouncements()
{
/// ...
}
}
now my ReSharper template will look something like this:
/// This sample shows how to call the <see cref="$METHOD$"/> method of controller $CLASS$ using the Web API.
/// https://myurl.mydomain.com/api/$CONTROLLER$/$METHOD$
$Controller$, by convention, is the class name minus the letters 'Controller'. This is because ASP.NET MVC Web API projects expect classes derived from ApiController to end with the string 'Controller',
Since this class is AnnouncementController, the template should output
https://myurl.mydomain.com/api/Announcement/GetActiveAnnouncements
Resharper's Built-In Macros can give me some of what I need, but I want to write a custom macro that fetches the containing type name and chops "Controller" off of it. I would like to do that directly, without storing the containing type name in another parameter.
Also, how do I install this custom macro? I've Googled around, and all I found was a lot of dead links and old walkthroughs written for ReSharper version 7 and below that do NOT work with ReSharper 8.x
After a lot of fighting, here is my solution.
[MacroImplementation(Definition = typeof (ControllerNameMacroDefinition))]
public class ControllerNameMacroImplementation : SimpleMacroImplementation
{
public ControllerNameMacroImplementation([Optional] IReadOnlyCollection<IMacroParameterValueNew> arguments)
{
}
public override HotspotItems GetLookupItems(IHotspotContext context)
{
var ret = "CONTROLLER";
var fileName = GetFileName(context);
if (!fileName.IsNullOrEmpty())
{
//Replace "Controller.cs" in two separate steps in case the extension is absent
ret = fileName.Replace("Controller", "").Replace(".cs", "");
}
return MacroUtil.SimpleEvaluateResult(ret);
}
/// <summary>
/// Returns the filename of the current hotspot context
/// </summary>
private string GetFileName(IHotspotContext context)
{
var psiSourceFile = context.ExpressionRange.Document.GetPsiSourceFile(context.SessionContext.Solution);
return psiSourceFile == null ? string.Empty : psiSourceFile.Name;
}
}
I wanted to do exactly this, but for JavaScript Jasmine tests -- SomethingViewModel.js, with a fixture of SomethingViewModelFixture.js, but wanted to be able to refer to SomethingViewModel in the file. A few slight modifications to the above made it possible.
Unfortunately, there's a ton more things you need to do in order to get your plugin to actually install. Here's a list. I hope it's comprehensive.
NuGet package install JetBrains.ReSharper.SDK, make sure you have the correct version installed!
Copy your Class Library DLL to C:\Users\<you>\AppData\Local\JetBrains\ReSharper\<version>\plugins\<your plugin name>, creating the plugins directory if needed.
You need the plugin Annotations in your AssemblyInfo.cs file:
[assembly: PluginTitle("Your extensions for ReSharper")]
[assembly: PluginDescription("Some description")] -- this is displayed in ReSharper->Options->Plugins
[assembly: PluginVendor("You")]
You need a class in your project that defines the MacroDefinition, as well as the above MacroImplementation
[MacroDefinition("MyNamespace.MyClassName", ShortDescription = "A short description of what it does.", LongDescription = "A long description of what it does.")]
"ShortDescription" - this is displayed in the "Choose Macro" dialog list.
"LongDescription" you'd think this would be in the "Choose Macro" description, but it isn't.
I just added this annotation to the above file.
The file you add the MacroDefinition to needs to implement IMacroDefinition, which has a method (GetPlaceholder) and a property (Parameters) on it. The former can return any string ("a") and the latter can return an empty array.
You can ignore the WiX/NuGet stuff if you want. Just for a local install.
In VS, the ReSharper->Options->Plugins section has some troubleshooting details on why your plugin might not be loading.
Good luck!

How to use ObjectContext with Model Builder?

Is there a way we can use ObjectContext with DbContext's ModelBuilder? We don't want to use POCO because we have customized property code that does not modify entire object in update, but only update modified properties. Also we have lots of serialisation and auditing code that uses EntityObject.
Since poco does create a proxy with EntityObject, we want our classes to be derived from EntityObject. We don't want proxy. We also heavily use CreateSourceQuery. The only problem is EDMX file and its big connection string syntax web.config.
Is there any way I can get rid of EDMX file? It will be useful as we can dynamically compile new class based on reverse engineering database.
I would also like to use DbContext with EntityObject instead of poco.
Internal Logic
Access Modified Properties in Save Changes which is available in ObjectStateEntry and Save them onto Audit with Old and New Values
Most of times we need to only check for Any condition on Navigation Property for example
User.EmailAddresses.CreateSourceQuery()
.Any( x=> x.EmailAddress == givenAddress);
Access Property Attributes, such as XmlIgnore etc, we rely heavily on attributes defined on the properties.
A proxy for a POCO is a dynamically created class which derives from (inherits) a POCO. It adds functionality previously found in EntityObject, namely lazy loading and change tracking, as long as a POCO meets requirements. A POCO or its proxy does not contain an EntityObject as the question suggests, but rather a proxy contains functionality of EntityObject. You cannot (AFAIK) use ModelBuilder with EntityObject derivatives and you cannot get to an underlying EntityObject from a POCO or a proxy, since there isn't one as such.
I don't know what features of ObjectContext does your existing serialisation and auditing code use, but you can get to ObjectContext from a DbContext by casting a DbContext to a IObjectContextAdapter and accessing IObjectContextAdapter.ObjectContext property.
EDIT:
1. Access Modified Properties in Save Changes which is available in ObjectStateEntry and Save them onto Audit with Old and New Values
You can achieve this with POCOs by using DbContext.ChangeTracker. First you call DbContext.ChangeTracker.DetectChanges to detect the changes (if you use proxies this is not needed, but can't hurt) and then you use DbCotnext.Entries.Where(e => e.State != EntityState.Unchanged && e.State != EntityState.Detached) to get DbEntityEntry list of changed entities for auditing. Each DbEntityEntry has OriginalValues and CurrentValues and the actual Entity is in property Entity.
You also have access to ObjectStateEntry, see below.
2. Most of times we need to only check for Any condition on Navigation Property for example:
User.EmailAddresses.CreateSourceQuery().Any( x=> x.EmailAddress == givenAddress);
You can use CreateSourceQuery() with DbContext by utilizing IObjectContextAdapter as described previously. When you have ObjectContext you can get to the source query for a related end like this:
public static class DbContextUtils
{
public static ObjectQuery<TMember> CreateSourceQuery<TEntity, TMember>(this IObjectContextAdapter adapter, TEntity entity, Expression<Func<TEntity, ICollection<TMember>>> memberSelector) where TMember : class
{
var objectStateManager = adapter.ObjectContext.ObjectStateManager;
var objectStateEntry = objectStateManager.GetObjectStateEntry(entity);
var relationshipManager = objectStateManager.GetRelationshipManager(entity);
var entityType = (EntityType)objectStateEntry.EntitySet.ElementType;
var navigationProperty = entityType.NavigationProperties[(memberSelector.Body as MemberExpression).Member.Name];
var relatedEnd = relationshipManager.GetRelatedEnd(navigationProperty.RelationshipType.FullName, navigationProperty.ToEndMember.Name);
return ((EntityCollection<TMember>)relatedEnd).CreateSourceQuery();
}
}
This method uses no dynamic code and is strongly typed since it uses expressions. You use it like this:
myDbContext.CreateSourceQuery(invoice, i => i.details);

Xceed WPF PropertyGrid : decorating property with Category : compiler error

I have basically the same question as the one I asked here. Adding the using Xceed.Wpf.Toolkit.PropertyGrid.Attributes directive solved that.
This time, the compiler does not like [Category("Shipping")] decoration.
[Category("Shipping")]
public string ShipAddress { get; set; }
How can I deduce or determine what namespace needs to be included when I run into obstacles like this?
Here are the using directives I've included already:
using Xceed.Wpf.Toolkit.PropertyGrid;
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
using Xceed.Wpf.Toolkit.PropertyGrid.Commands;
using Xceed.Wpf.Toolkit.PropertyGrid.Converters;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
The xaml is this:
<xctk:PropertyGrid AutoGenerateProperties="True" Name="XPG1" IsCategorized="True" />
I know this is an older question, but since it's unanswered I thought it would be helpful to provide one anyway. In this case you need the following using statement:
using System.ComponentModel;
In general, the best way to figure out what namespace or using statement you need is to look for the name of the attribute in the Object Browser under the Xceed namespace, and if you can't find it there, on Google.
One thing to remember - while it shows up as just [Category] in code, the actual name of the class will be CategoryAttribute.

ServiceStack - generate ASP.NET webservice -reference issue

I am using the very excellent servicestack libaries and trying to generate a ASP.NET web-service reference (old style not WCF) from within VS2010 across my servicestack WSDL - Soap11. To nicely wrap the service WSDL.
The DTO's are in a seperate assembly/namespace (My.WS.DTO) from the AppHost/services and are following the request/response naming convention.. when I try to generate the reference through visual studio I get the following error in VS.
Custom tool error: Unable to import WebService/Schema. Unable to import binding 'BasicHttpBinding_ISyncReply' from namespace 'http://schemas.servicestack.net/types'. Unable to import operation 'GetMyDetails'. The element 'http://schemas.servicestack.net/types:GetMyDetails' is missing.
NOTE: GetMyDetails is just the first service that appears in the list - so I dont believe this is the problem.
I have tried adding the assembly namespace in the AppHost file using
EndpointHostConfig.Instance.WsdlServiceNamespace = "My.WS.DTO"; and this just causes the same generation error (as above) but with 'My.WS.DTO' instead of 'http://schemas.servicestack.net/types'.
I assume it is perhaps some sort of referencing problem but any guidance as to what I might be doing wrong would be great.
cheers
I don't know if this is still an issue for you but I had a similar problem and found that I had not decorated one of my DTOs with [DataContract] and [DataMember] attributes, as described on the SOAP Support wiki page. Once you have added these to your DTO it will be declared in the type section of the WSDL.
Have a look at using [DataContract (Namespace = "YOUR NAMESPACE")] on top of your DTO's. This is how my objects are referenced.
[DataContract(Namespace = "My.WS.DTO")]
public class Account{
}
I also use this in my service model. [System.ServiceModel.ServiceContract()] and [System.ServiceModel.OperationContract()]
[System.ServiceModel.ServiceContract()]
public class SendGetAccountResponseService : IService<SendGetAccountNotification>
{
#region IService implementation
[System.ServiceModel.OperationContract()]
public object Execute (SendGetAccountNotification request)
{
Console.WriteLine ("Reached");
return null;
}
#endregion
}
Hope this helps / solves your problem.
I know this is an old question, but I had to add SOAP support for a 3rd party that refused to support REST very recently to my ServiceStack implementation so it could still be relevant to other people still having this issue.
I had the same issue you were having:
Unable to import binding 'BasicHttpBinding_ISyncReply'...
And like mickfold previously answered I needed to add [DataContract] and [DataMember] to my class definitions and their properties.
But I also had to add the following to my AssemblyInfo.cs file before the error went away for me:
[assembly: ContractNamespace("http://schemas.servicestack.net/types", ClrNamespace = "My Type Namespace")]
I assume that you will need one of these lines for every single namespace where you have a type declared, which based upon the original question above would be My.WS.DTO.

Is it possible to use ASP.NET Dynamic Data and SubSonic 3?

Is it possible to use ASP.NET Dynamic Data with SubSonic 3 in-place of Linq to SQL classes or the Entity Framework? MetaModel.RegisterContext() throws an exception if you use the context class that SubSonic generates. I thought I remembered coming across a SubSonic/Dynamic Data example back before SubSonic 3 was released but I can't find it now. Has anyone been able to get this to work?
I just got Subsonic 3.0.0.4 ActiveRecord working last night in Visual Studio 2010 with my SQLite database after a little bit of work and I've tried to document the steps taken here for your benefit.
Start by adding a New Item -> WCF Data Service to the project you're using to host your webapp/webservices then modify it similar to my PinsDataService.svc.cs below:
public class PinsDataService : DataService<PINS.Lib.dbPINSDB>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.UseVerboseErrors = true;
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
At this point your Dynamic Data Service would probably be working if you matched all the database naming conventions perfectly but I didn't have that kind of luck. In my ActiveRecord.tt template I had to prepend the following two lines before the public partial class declarations:
[DataServiceKey("<#=tbl.PrimaryKey #>")]
[IgnoreProperties("Columns")]
public partial class <#=tbl.ClassName#>: IActiveRecord {
I then added references to System.Data and System.Data.Services.Client followed by the inclusion of using statements for using System.Data.Services and using System.Data.Services.Common at the top of the ActiveRecord.tt template.
The next step was to use the IUpdateable partial class implementation from this blog post http://blogs.msdn.com/aconrad/archive/2008/12/05/developing-an-astoria-data-provider-for-subsonic.aspx and change the public partial class dbPINSDB : IUpdatable to match my subsonic DatabaseName declared in Settings.ttinclude
Then to consume the data in a separate client app/library I started by adding a 'Service Reference' named PinsDataService to the PinsDataService.svc from my client app and went to town:
PinsDataService.dbPINSDB PinsDb =
new PinsDataService.dbPINSDB(new Uri("http://localhost:1918/PinsDataService.svc/"));
PinsDataService.Alarm activeAlarm =
PinsDb.Alarms.Where(i => i.ID == myAA.Alarm_ID).Take(1).ElementAt(0);
Note how I'm doing a Where query that returns only 1 object but I threw in the Take(1) and then ElementAt(0) because I kept getting errors when I tried to use SingleOrDefault() or First()
Hope this helps--also, I'm already aware that dbPINSDB is a really bad name for my Subsonic Database ;)

Resources