I have just created a database and done my first migration (just a simple table add). Now I want to add some stored procedures which I have just added by writing the sql and executing it in Management Studio. But I would like to include these stored procedures if possible in a migration so that they are saved and I can run an Up or Down method against them. Is this possible and if so what syntax needs to be used? Or will I just have to add/edit/remove them using Management Studio?
I've done this like so...
In the current migration class -
public partial class MyMigration : DbMigration
{
public override void Up()
{
... other table creation logic
// This command executes the SQL you have written
// to create the stored procedures
Sql(InstallScript);
// or, to alter stored procedures
Sql(AlterScript);
}
public override void Down()
{
... other table removal logic
// This command executes the SQL you have written
// to drop the stored procedures
Sql(UninstallScript);
// or, to rollback stored procedures
Sql(RollbackScript);
}
private const string InstallScript = #"
CREATE PROCEDURE [dbo].[MyProcedure]
... SP logic here ...
";
private const string UninstallScript = #"
DROP PROCEDURE [dbo].[MyProcedure];
";
// or for alters
private const string AlterScript = #"
ALTER PROCEDURE [dbo].[AnotherProcedure]
... Newer SP logic here ...
";
private const string RollbackScript = #"
ALTER PROCEDURE [dbo].[AnotherProcedure]
... Previous / Old SP logic here ...
";
}
I am using EF6 and the DbMigration class provides methods to Create/Alter/Delete stored procedures
Create a new stored procedure
public partial class MyFirstMigration : DbMigration
{
public override void Up()
{
// Create a new store procedure
CreateStoredProcedure("dbo.DequeueMessages"
// These are stored procedure parameters
, c => new{
MessageCount = c.Int()
},
// Here is the stored procedure body
#"
SET NOCOUNT ON;
SELECT TOP (#MessageCount)
*
FROM
dbo.MyTable;
");
}
public override void Down()
{
// Delete the stored procedure
DropStoredProcedure("dbo.DequeueMessages");
}
}
Modify a stored procedure
public partial class MySecondMigration : DbMigration
{
public override void Up()
{
// Modify an existing stored procedure
AlterStoredProcedure("dbo.DequeueMessages"
// These are new stored procedure parameters
, c => new{
MessageCount = c.Int(),
StatusId = c.Int()
},
// Here is the new stored procedure body
#"
SET NOCOUNT ON;
SELECT TOP (#MessageCount)
*
FROM
dbo.MyTable
WHERE
StatusId = #StatusId;
");
}
public override void Down()
{
// Rollback to the previous stored procedure
// Modify an existing stored procedure
AlterStoredProcedure("dbo.DequeueMessages"
// These are old stored procedure parameters
, c => new{
MessageCount = c.Int()
},
// Here is the old stored procedure body
#"
SET NOCOUNT ON;
SELECT TOP (#MessageCount)
*
FROM
dbo.MyTable;
");
}
}
namespace QuickProject.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class CreateStoredProcedure_GellAllAgents : DbMigration
{
public override void Up()
{
CreateStoredProcedure("dbo.GellAllAgents", c => new
{
DisplayLength = c.Int(10),
DisplayStart = c.Int(0),
UserName = c.String(maxLength: 255, defaultValueSql: "NULL"),
FullName = c.String(maxLength: 255, defaultValueSql: "NULL"),
PhoneNumber = c.String(maxLength: 255, defaultValueSql: "NULL"),
LocationDescription = c.String(maxLength: 255, defaultValueSql: "NULL"),
AgentStatusId = c.Int(defaultValueSql: "NULL"),
AgentTypeId = c.Int(defaultValueSql: "NULL")
}, StoredProcedureBody);
}
public override void Down()
{
DropStoredProcedure("dbo.GellAllAgents");
}
private const string StoredProcedureBody = #"
Declare #FirstRec int, #LastRec int
Set #FirstRec = #DisplayStart;
Set #LastRec = #DisplayStart + #DisplayLength;
With CTE_AspNetUsers as
(
Select ROW_NUMBER() over (order by AspNetUsers.Id) as RowNum,
COUNT(*) over() as TotalCount, AspNetUsers.Id, AspNetUsers.FullName, AspNetUsers.UserName, AspNetUsers.PhoneNumber, Locations.Desciption as LocationDescription, Cities.Name as LocationCity, AgentStatus.Name as AgentStatusName, AgentTypes.Name as AgentTypeName
from AspNetUsers
join Locations on AspNetUsers.LocationId = Locations.id
join Cities on Locations.CityId = Cities.Id
join AgentStatus on AspNetUsers.AgentStatusId = AgentStatus.Id
join AgentTypes on AspNetUsers.AgentTypeId = AgentTypes.Id
where (Discriminator = 'Agent'
and (#UserName is null or UserName like '%' + #UserName + '%')
and (#FullName is null or FullName like '%' + #FullName + '%')
and (#PhoneNumber is null or PhoneNumber like '%' + #PhoneNumber + '%')
and (#LocationDescription is null or #LocationDescription like '%' + (select Cities.Name from Cities where Locations.CityId = Cities.Id) + '%' or #LocationDescription like '%' + Desciption + '%')
and (#AgentStatusId is null or AgentStatusId = #AgentStatusId)
and (#AgentTypeId is null or AgentTypeId = #AgentTypeId)
)
group by AspNetUsers.Id, AspNetUsers.FullName,AspNetUsers.UserName, AspNetUsers.PhoneNumber, Locations.Desciption, Cities.Name, AgentStatus.Name, AgentTypes.Name
)
Select *
from CTE_AspNetUsers
where RowNum > #FirstRec and RowNum <= #LastRec
";
}
}
Result, When you view/modify the SP in SQL server, that's why it shows "ALTER PROCEDURE"
I will try to provide a different perspective because having SQL code within C# strings is not very appealing and one should expect to change such scripts within a tool that provides intellisense (e.g. SSMS).
The following solution is implemented within a ASP.NET Core 2.0 Web API project.
Maintain procedures in the development database using any convenient tool
Generate procedures scripts:
public class ProcedureItemMetadata
{
/// <summary>
/// SQL server side object identifier
/// </summary>
[Key]
public int ObjectId { get; set; }
/// <summary>
/// schema name
/// </summary>
public string SchemaName { get; set; }
/// <summary>
/// procedure name
/// </summary>
public string Name { get; set; }
/// <summary>
/// procedure body
/// </summary>
public string Definition { get; set; }
}
public string GetProceduresScript()
{
var query = Context.ProcedureItemMetadata.AsNoTracking().FromSql(#"
SELECT ao.object_id as ObjectId, SCHEMA_NAME(ao.schema_id) as SchemaName, ao.name, sm.definition
FROM sys.all_objects ao
JOIN sys.sql_modules sm ON sm.object_id = ao.object_id
WHERE ao.type = 'P'
and execute_as_principal_id IS NULL
order by 1;");
var list = query.ToList();
string text = string.Join($" {Base.Constants.General.ScriptGeneratorSeparator}\n", list.Select(p => p.Definition));
// replace create with create or alter
string replaced = Regex.Replace(text,
#"(?<create>CREATE\s+PROCEDURE\s+)",
"CREATE OR ALTER PROCEDURE ",
RegexOptions.IgnoreCase);
return replaced;
}
This is a manual process, but allows to obtain procedures whenever their development is ready. Also, it can easily be extended to other types of objects (e.g. views).
Create a folder within solution to hold scripts to be run at application startup (e.g. _SQL)
Copy generated script within the folder (e.g. all_procedures.sql)
One advantage of storing scripts like this is that the IDE might automatically validate the syntax + highlight stuff etc.
Create "seed" code to automatically run when application starts
private static void EnsureSqlObjects(CustomContext context)
{
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "_Sql");
foreach (var file in Directory.GetFiles(path, "*.sql"))
{
string fileText = File.ReadAllText(file);
// escaping { } for those rare cases when sql code contains {..}
// as ExecuteSqlCommand tries to replace them with params values
fileText = fileText.Replace("{", "{{");
fileText = fileText.Replace("}", "}}");
// splitting objects (cannot run more than one DDL in a command)
string[] ddlParts = fileText.Split(Base.Constants.General.ScriptGeneratorSeparator, StringSplitOptions.RemoveEmptyEntries);
foreach (string ddl in ddlParts)
{
context.Database.ExecuteSqlCommand(ddl);
}
}
}
This approach allows for any idempotent scripts that are not easily maintained through migrations to be managed.
Related
I want to execute a stored procedure with a minimum of 3 to 20 parameters.
I am converting the property values from a request model to an SQL string and a list of parameters. Then make the call and expect a result but when I check the Created SQL from debug view, I observe that the EF tries to call the sp with non-named style parameters.
For brevity assume the first 3 properties contain values, and other properties have null values.
RequestModel
{
string Property1 { get; set; }
string Property2 { get; set; }
string Property3 { get; set; }
...
}
ResponseModel
{
string Response1 { get; set; }
string Response2 { get; set; }
...
}
var sqlString = "exec sp_dummy #prop1, #prop2, #prop3";
var params = new SqlParameter[] { new SqlParameter("#prop1", Property1), new SqlParameter("#prop2", Property2), new SqlParameter("#prop3", Propery3) };
return await ResponseModel.FromSqlRaw(sqlString, params).ToListAsync();
And when I check the generated SQL from EF Core, it is as follows:
DECLARE #prop1 nvarchar(10) = N'Prop1Data';
DECLARE #prop2 nvarchar(10) = N'Prop2Data';
DECLARE #prop3 nvarchar(10) = N'Prop3Data';
exec dummy_sp #prop1, #prop2, #prop3
What I want to achieve is as follows:
exec dummy_sp #prop1 = N'Prop1Data' , #prop2 = N'Prop2Data', #prop3 = N'Prop3Data'
I have achieved this by using dbcontext's ExecuteSqlRaw method, instead of FromSqlRaw extension method.
Database.ExecuteSqlRaw(sql,params);
My goal is to query the results of a stored procedure (sql server) using a custom row mapper within a spring batch tasklet.
I am using Spring Boot (version 2.2.2.RELEASE) and Spring Batch (4.2.1.RELEASE).
Example:
Stored Procedure:
CREATE PROCEDURE storedProcName #numbers VARCHAR(max), #day DATE
AS
SET NOCOUNT ON;
SELECT something, something2, something3
FROM sometable
WHERE ids in (select value from string_split(#numbers,','))
AND day = #day
Custom Row Mapper:
public class CustomRowMapper implements RowMapper<CustomObject> {
private static final String SOMETHING = "something";
private static final String SOMETHING2 = "something2";
private static final String SOMETHING3 = "something3";
#Override
public CustomObject mapRow(ResultSet resultSet, int i) throws SQLException {
CustomObject customObject = new CustomObject();
customObject.setSomething(resultSet.getString(SOMETHING));
customObject.setSomething2(resultSet.getString(SOMETHING2));
customObject.setSomething3(resultSet.getInt(SOMETHING3));
return customObject;
}
}
Execute Stored Procedure and query the results:
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("storedProcName")
.returningResultSet("test", new CustomRowMapper());
Map<String, Object> out = jdbcCall.execute(parameterSource);
List<CustomObject> customObjects = (List<CustomObject>) out.get("test");
It works fine, but there has to be a more efficient way to do this? But i didn't found anything useful.
there has to be a more efficient way to do this
Spring Batch provides the StoredProcedureItemReader which can call a given stored procedure and iterate over its results. It also allows you to use a custom mapper as the one you defined.
According to your code snippet, you call the procedure and get a List<CustomObject> over which you will need to iterate at some point. So I think a chunk oriented tasklet with a StoredProcedureItemReader<CustomObject> is a good choice for you:
#Bean
public StoredProcedureItemReader<CustomObject> itemReader() {
return new StoredProcedureItemReaderBuilder<CustomObject>()
.procedureName("storedProcName")
.rowMapper(new CustomRowMapper())
// set other properties
.build();
}
Edit: show how to return a synchronized reader
#Bean
#StepScope
public SynchronizedItemStreamReader<CustomObject> itemReader() {
StoredProcedureItemReader<CustomObject> reader = new StoredProcedureItemReaderBuilder<>()
.procedureName("storedProcName")
.rowMapper(new CustomRowMapper())
// set other properties
.build();
SynchronizedItemStreamReader<CustomObject> synchronizedReader = new SynchronizedItemStreamReader<>();
synchronizedReader.setDelegate(reader);
return synchronizedReader;
}
I'm new to SQL Server and want to implement this scenario. My stored procedure gets 8 input parameters from a C# web application, and checks all input has into the table. For that purpose I wrote this simple stored procedure:
CREATE PROCEDURE CheckValid
#p_bank varchar,
#p_pay_date varchar,
#p_bill_id varchar,
#p_payment_id varchar,
#p_ref_code varchar,
#p_branch varchar,
#p_channel_type varchar,
#p_send_date varchar
AS
BEGIN
SELECT
[p_bank], [p_pay_date], [p_bill_id], [p_payment_id],
[p_ref_code], [p_branch], [p_channel_type], [p_send_date]
FROM
[SAMPLE].[dbo].[MixedTable]
WHERE
[p_bank] = #p_bank
AND [p_pay_date] = #p_pay_date
AND [p_bill_id] = #p_bill_id
AND [p_payment_id] = #p_payment_id
AND [p_ref_code] = #p_ref_code
AND [p_branch] = #p_branch
AND [p_channel_type] = #p_channel_type
AND [p_send_date] = #p_send_date
END
But want to return to c# application this scenario, for example c# sends all field but when stored procedure select run for this purpose can not find data, for example p_bill_id not correct into the table for more explain in select query into where clause in the [p_bill_id]=#p_bill_id not trust and now want to return sp this :
p_bill_id,not found
and other example c# all variable correct but two field [p_channel_type] and [p_payment_id] not correct into where clause but other 6 field correct now SP return this:
[p_channel_type],not found
[p_payment_id],not found
Summary of question:
When data for passed parameter value is not found, I want it to return that corresponding column.
For example:
[p_channel_type],not found
[p_payment_id],not found
Note, varchar means varchar(1) so you should specify length for each argument explicitly like varchar(100)
CREATE PROCEDURE CheckValid
#p_bank varchar(<length>),
#p_pay_date varchar(<length>),
#p_bill_id varchar(<length>),
#p_payment_id varchar(<length>),
#p_ref_code varchar(<length>),
#p_branch varchar(<length>),
#p_channel_type varchar(<length>),
#p_send_date varchar(<length>)
AS
BEGIN
if not exists(select 1 from dbo.BankTable where p_bank = #p_bank)
begin
raiserror('Bank %s not found', 16, 1, #p_bank)
return
end
if not exists(select 1 from dbo.BillTable where p_bill_id = #p_bill_id)
begin
raiserror('Bill %s not found', 16, 1, #p_bill_id)
return
end
...
SELECT [p_bank],[p_pay_date],[p_bill_id],[p_payment_id],[p_ref_code],[p_branch],[p_channel_type],[p_send_date]
FROM [SAMPLE].[dbo].[MixedTable]
where [p_bank]=#p_bank and [p_pay_date]=#p_pay_date
and [p_bill_id]=#p_bill_id and [p_payment_id]=#p_payment_id
and [p_ref_code]=#p_ref_code and [p_branch]=#p_branch
and [p_channel_type]=#p_channel_type and [p_send_date]=#p_send_date
END
GO
Instead of creating stored procedure for this move "validation" logic to your c# application.
Database is just IO device and I think keeping "business logic" in IO device not a good approach.
// Class which represent your eight parameters
public class Data
{
public string Bank { get; set; }
public string PayDate { get; set; }
public string BillId { get; set; }
public string PaymentId { get; set; }
public string RefCode { get; set; }
public string Branch { get; set; }
public string ChannelType { get; set; }
public string SendDate { get; set; }
}
public class Validation
{
private Data _data;
public Validation(Data data)
{
_data = data;
}
public IEnumerable<string> Validate()
{
var columns = new KeyValuePair<string, string>[]
{
new KeyValuePair("p_bank", _data.Bank),
new KeyValuePair("p_pay_date", _data.PayDate),
new KeyValuePair("p_bill_id", _data.BillId),
new KeyValuePair("p_payment_id", _data.PaymentId),
new KeyValuePair("p_ref_code], _data.RefCode),
new KeyValuePair("p_branch", _data.Branch),
new KeyValuePair("p_channel_type", _data.ChannelType),
new KeyValuePair("p_send_date", _data.SendDate)
};
return columns.Where(pair => IsValueExists(pair.Key, pair.Value) == false);
}
private bool IsValueExists(string columnName, string value)
{
var query =
$"SELECT [{columnName}]
FROM [SAMPLE].[dbo].[MixedTable]
WHERE [{columnName}] = #value";
var parameter = new SqlParameter
{
ParameterName = "#value",
SqlDbType = SqlDbType.VarChar,
Value = _data.Bank
};
using (var connection = new SqlConnection(yourConnectionString))
using (var command = new SqlCommand(query, connection))
{
command.Parameters.Add(parameter);
connection.Open();
var value = command.ExecuteScalar();
return value != null; // null returned if no rows exists
}
}
}
Then you can use this method somewhere
var data = new Data { Bank = "BankName", RefCode = "SomeRefcode" } // put all values
var validation = new Validation(data);
var invalidValues = validation.Validate();
foreach(var invalidValue in invalidValues)
{
// Print or save column names where value is invalid
}
I need to optimize a query that is being produced by a save (insert query) on a domain entity. I've configured NHibernate using Fluent NHibernate.
Here's the query generated by NHibernate during the insertion of a user's response to a poll:
exec sp_executesql N'INSERT INTO dbo.Response (ModifiedDate, IpAddress, CountryCode,
IsRemoteAddr, PollId) VALUES (#p0, #p1, #p2, #p3, #p4); select SCOPE_IDENTITY()',N'#p0
datetime,#p1 nvarchar(4000),#p2 nvarchar(4000),#p3 bit,#p4 int',
#p0='2001-07-08 03:59:05',#p1=N'127.0.0.1',#p2=N'US',#p3=1,#p4=2
If one looks at the input parameters for IpAddress and CountryCode, one will notice that NHibernate is using nvarchar(4000). The problem is that nvarchar(4000) is far larger than I need for either IpAddress or CountryCode and due to high traffic and hosting requirements I need to optimize the database for memory usage.
Here's the Fluent NHibernate auto-mapping overrides for those columns:
mapping.Map(x => x.IpAddress).CustomSqlType("varchar(15)");
mapping.Map(x => x.CountryCode).CustomSqlType("varchar(6)");
This isn't the only place that I see unnecessary nvarchar(4000)'s popping up.
How do I control NHibernate's usage of nvarchar(4000) for string representation?
How do I change this insert statement to use the proper sized input parameters?
Specify the Type as NHibernateUtil.AnsiString with a Length instead of using a CustomSqlType.
This issue can cause a huge performance problem in queries if it forces SQL Server to perform a table scan instead of using an index. We use varchar throughout our database so I created a convention to set the type globally:
/// <summary>
/// Convert all string properties to AnsiString (varchar). This does not work with SQL CE.
/// </summary>
public class AnsiStringConvention : IPropertyConventionAcceptance, IPropertyConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.Equals(typeof(string)));
}
public void Apply(IPropertyInstance instance)
{
instance.CustomType("AnsiString");
}
}
Okay this is what we have to do, the SQLClientDriver ignores the length property of the SqlType. So we created a our own driverclass inheriting from SQLClientDriver and override the method GenerateCommand...Something like this:
public override IDbCommand GenerateCommand(CommandType type, NHibernate.SqlCommand.SqlString sqlString, SqlType[] parameterTypes)
{
var dbCommand = base.GenerateCommand(type, sqlString, parameterTypes);
SetParameterSizes(dbCommand.Parameters, parameterTypes);
return dbCommand;
}
private static void SetParameterSizes(IDataParameterCollection parameters, SqlType[] parameterTypes)
{
for (int index = 0; index < parameters.Count; ++index)
SetVariableLengthParameterSize((IDbDataParameter)parameters[index], parameterTypes[index]);
}
private static void SetVariableLengthParameterSize(IDbDataParameter dbParam, SqlType sqlType)
{
SetDefaultParameterSize(dbParam, sqlType);
if (sqlType.LengthDefined && !IsText(dbParam, sqlType) && !IsBlob(dbParam, sqlType))
dbParam.Size = sqlType.Length;
if (!sqlType.PrecisionDefined)
return;
dbParam.Precision = sqlType.Precision;
dbParam.Scale = sqlType.Scale;
}
Here is a work around, if you want to replace all nvarchar with varchar
public class Sql2008NoNVarCharDriver : Sql2008ClientDriver
{
public override void AdjustCommand(IDbCommand command)
{
foreach (System.Data.SqlClient.SqlParameter x in command.Parameters)
{
if (x.SqlDbType == SqlDbType.NVarChar)
{
x.SqlDbType = SqlDbType.VarChar;
}
}
base.AdjustCommand(command);
}
}
Then plug it into your config
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)
.Driver<Sql2008NoNVarCharDriver>())
...
It seems that SQL Server has a fair amount of XML support. Mostly I've seen info regarding storing XML in SQL Server, querying XML data stored in SQL Server, and exposing data as XML.
Is the following scenario an option:
I'd like to expose xml data (it's an RSS view of workitems) from a web site via a SQL Server view. The motivation is to create new computed values and then show the data via an SSRS report.
I'd like to use a view so that the data is always live, and avoid the need for a batch ETL.
Is this possible? What does the syntax look like?
using System;
using System.Data.Sql;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Xml;
namespace RSSFunctions
{
public class GetRSSFeedClass
{
private class RSSRow
{
public SqlString Title;
public SqlString Description;
public RSSRow(SqlString Title, SqlString Description)
{
this.Title = Title;
this.Description = Description;
}
}
[SqlFunction(FillRowMethodName = "FillRSSRow")]
public static IEnumerable GetRSSFeed(SqlString RSSurl)
{
ArrayList RSSRowsCollection = new ArrayList();
string url = RSSurl.ToString();
WebRequest req = System.Net.WebRequest.Create(url);
WebResponse Res = req.GetResponse();
Stream rssStream = Res.GetResponseStream();
XmlDocument rssDoc = new XmlDocument();
rssDoc.Load(rssStream);
XmlNodeList rssItems = rssDoc.SelectNodes("rss/channel/item");
String Title = "";
String Description = "";
int i = 0;
for (i = 0; i <= rssItems.Count - 1; i++)
{
XmlNode rssDetail = default(XmlNode);
Title = "";
rssDetail = rssItems.Item(i).SelectSingleNode("title");
if (rssDetail.Equals(null) == false)
{
Title = rssDetail.InnerText;
}
Description = "";
rssDetail = rssItems.Item(i).SelectSingleNode("description");
if (rssDetail.Equals(null) == false)
{
Description = rssDetail.InnerText;
}
if (Title.Length > 97)
{
Title = Title.Substring(0, 97) + "...";
}
if (Description.Length > 3997)
{
Description = Description.Substring(0, 3997) + "...";
}
if (!string.IsNullOrEmpty(Title) && !string.IsNullOrEmpty(Description))
{
RSSRowsCollection.Add(new RSSRow(new SqlString(Title), new SqlString(Description)));
}
}
return RSSRowsCollection;
}
public static void FillRSSRow(object obj, out SqlString Title, out SqlString Description)
{
RSSRow _RSSRow = (RSSRow)obj;
Title = _RSSRow.Title;
Description = _RSSRow.Description;
}
SSMS
--ALTER DATABASE [dbname] TRUSTWORTHY ON
--go
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'RSSData')
DROP VIEW RSSData
go
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'fncCLRGetRSSFeed')
DROP FUNCTION fncCLRGetRSSFeed
go
IF EXISTS (SELECT name FROM sys.assemblies WHERE name = 'CLRRSSAssembly')
DROP ASSEMBLY CLRRSSAssembly
go
CREATE ASSEMBLY CLRRSSAssembly FROM 'C:\RSSAssembly.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS
GO
CREATE FUNCTION fncCLRGetRSSFeed(#url nvarchar(100))
RETURNS TABLE (
Title nvarchar(100),
[Description] nvarchar(4000)
)
AS EXTERNAL NAME CLRRSSAssembly.[RSSFunctions.GetRSSFeedClass].GetRSSFeed
go
CREATE VIEW RSSData
AS
SELECT * FROM fncCLRGetRSSFeed(N'http://channel9.msdn.com/Feeds/RSS/')
go
SELECT * FROM RSSData
Create a CLR proc to pull the live feed