CrestApps.RetsSdk 1.0.4

"CrestApps RetsSdk" a .NET Core client library for interacting with a RETS server to pull real estate listings, photos and other data made available from an MLS system.

Install-Package CrestApps.RetsSdk -Version 1.0.4
dotnet add package CrestApps.RetsSdk --version 1.0.4
<PackageReference Include="CrestApps.RetsSdk" Version="1.0.4" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add CrestApps.RetsSdk --version 1.0.4
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
// First we need to set out configuration           
Service.AddTransient(opts => new ConnectionOptions()
{
    UserAgent = "user-agent",
    RetsServerVersion = SupportedRetsVersion.Version_1_7_2,
    LoginUrl = "Login-URI",
    Username = "username",
    Password = "password",
    UserAgentPassward = "agent password if you have one",
    Type = AuthenticationType.Digest,
});

// Register the IRetsRequester, IRetsSession, IRetsClient
Service.AddTransient<IRetsRequester, RetsWebRequester>();
Service.AddTransient<IRetsSession, RetsSession>();
Service.AddTransient<IRetsClient, RetsClient>();

// Get instance of the IRetsClient from the IoC
IRetsClient client = Container.GetService<IRetsClient>();

// The first request we make to the RETS server is to login
await client.Connect();

// To get to know the RETS server, we can scan the entire system to get a list of our resources, classes, object.....
RetsSystem system = await client.GetSystemMetadata();

// We can also get a list of all available resources
RetsResourceCollection resources = await client.GetResourcesMetadata();

// We can also get all available classes for a given resorce. Assuming your RETS server has a resource called "Property"
RetsClassCollection classes = await client.GetClassesMetadata("Property");

// We can also get a list of all available objects on a given property.
RetsObjectCollection objects = await client.GetObjectMetadata("Property");

// We can also get a list of all available field for the given resource and class. Assuming your RETS server has a resource called "Property" with a class called "Listing"
RetsFieldCollection fields = await client.GetTableMetadata("Property", "Listing");

// We can also get all available lookup values on the given resource
IEnumerable<RetsLookupTypeCollection> lookupTypes = await client.GetLookupValues("Property");

// We can also get a list of all available lookup types for a give resource and lookuptype aka fieldName
RetsLookupTypeCollection lookupType = await client.GetLookupValues("Property", "FieldName");

// We can perform a search against the RETS server
SearchRequest searchRequest = new SearchRequest("Property", "Listing");

// Add ad many parameers to search for. Assuming the class "Listing" on the "Property" resource has a field called "matrix_unique_id" which is numeric type
// we say give me all properties where matrix_unique_id >= 0
searchRequest.ParameterGroup.AddParameter(QueryParameter.GreaterThanOrEqualTo("matrix_unique_id", "0"));

// If you like to return specific columns, you can do so like this
searchRequest.AddColumn("matrix_unique_id");
searchRequest.AddColumn("SomeOtherColumnName");

// This performs the search against the server
SearchResult result = await client.Search(searchRequest);

// we can iterate over the results like so
foreach (SearchResultRow row in result.GetRows())
{
    // Each row has multiple columns, lets loop over them
    foreach (var columnName in result.GetColumns())
    {
        // Lets get the cell value for a given column from the current row
        SearchResultCellValue value = row.Get(columnName);
    }
}

// Also you can extract only all value for a given column like this
IEnumerable<SearchResultCellValue> createdAtCells = result.Pluck("CreatedAt");

// you can also result cast the values of a given field like this
// this will result an IEnumerable<> of all values found in the CreatedAt column
IEnumerable<DateTime> createdAtvalues = result.Pluck<DateTime>("CreatedAt");

// We can also download photos
// This will return all photos for property with the primarykey 1234
IEnumerable<FileObject> files = await client.GetObject("Property", "Photo", new PhotoId(1234), false);

// Here is how we can iterate over the fields
foreach (FileObject file in files)
{
    var filePath = $"{file.ContentId}/{file.ObjectId}{file.Extension}";

    using (FileStream output = File.Create(filePath))
    {
        file.Content.CopyTo(output);
        file.Dispose();
    }
}

// you can get a specific image for a given primary key like so
IEnumerable<FileObject> files2 = await client.GetObject("Property", "Photo", new PhotoId(1234, 1), false);

// you can get also get images for multiple primary keys at the same time like this
List<PhotoId> photoIds = new List<PhotoId>() { new PhotoId(1234), new PhotoId(5678), new PhotoId(2255) };

IEnumerable<FileObject> files3 = await client.GetObject("Property", "Photo", photoIds, false);

// When you are trying to download lots of images you must be very careful. If you send the server too many ids at the same time
// the server may return 404, 414, 500 or something along these lines because the request is too long.
// Also, the server may take a long time to respond which will cause the HTTP request to timeout.
// So solve for the timeout issue we can increase the HTTP timeout from ConnectionOptions() object.

// However, there is a better more reliable solution to this problem which is the ability to batch the request into multiple requests
// Assume we want to download images for 1000 properties. Assume that each property on average has 15 photos, this will result in downloading 1000 x 15 = 15,000 images
// We can split the 1000 properties into a smaller number like 100. This will make 10 (i.e 1000/100) request to the RETS server then return you an object of 15,000 images
// You may want to still be careful because there will be 15,000 stored in the memory, so make sure you're not going to run out of memory
// Anyhow, batching can easily be done like this

IEnumerable<FileObject> batchedFiles = await client.GetObject("Property", "Photo", photoIds, batchSize: 100);

// Finally we can disconect
await client.Disconnect();

// The above code requires us to First connect, then Disconnect when we are done. Not too bad, but we can simplify the call by using
// a method called RoundTrip() which will first connect, execute out code, then disconnect

// to save some code you can do call RoundTrip() which will connect, call out method, then discconnect();
IEnumerable<FileObject> files4 = await client.RoundTrip(async () =>
{
    // Each batch will cause a round trip. In other words, each batch will connect, download a batch, then disconnect.
    return await client.GetObject("Property", "Photo", photoIds, batchSize: 20);
});
// First we need to set out configuration           
Service.AddTransient(opts => new ConnectionOptions()
{
    UserAgent = "user-agent",
    RetsServerVersion = SupportedRetsVersion.Version_1_7_2,
    LoginUrl = "Login-URI",
    Username = "username",
    Password = "password",
    UserAgentPassward = "agent password if you have one",
    Type = AuthenticationType.Digest,
});

// Register the IRetsRequester, IRetsSession, IRetsClient
Service.AddTransient<IRetsRequester, RetsWebRequester>();
Service.AddTransient<IRetsSession, RetsSession>();
Service.AddTransient<IRetsClient, RetsClient>();

// Get instance of the IRetsClient from the IoC
IRetsClient client = Container.GetService<IRetsClient>();

// The first request we make to the RETS server is to login
await client.Connect();

// To get to know the RETS server, we can scan the entire system to get a list of our resources, classes, object.....
RetsSystem system = await client.GetSystemMetadata();

// We can also get a list of all available resources
RetsResourceCollection resources = await client.GetResourcesMetadata();

// We can also get all available classes for a given resorce. Assuming your RETS server has a resource called "Property"
RetsClassCollection classes = await client.GetClassesMetadata("Property");

// We can also get a list of all available objects on a given property.
RetsObjectCollection objects = await client.GetObjectMetadata("Property");

// We can also get a list of all available field for the given resource and class. Assuming your RETS server has a resource called "Property" with a class called "Listing"
RetsFieldCollection fields = await client.GetTableMetadata("Property", "Listing");

// We can also get all available lookup values on the given resource
IEnumerable<RetsLookupTypeCollection> lookupTypes = await client.GetLookupValues("Property");

// We can also get a list of all available lookup types for a give resource and lookuptype aka fieldName
RetsLookupTypeCollection lookupType = await client.GetLookupValues("Property", "FieldName");

// We can perform a search against the RETS server
SearchRequest searchRequest = new SearchRequest("Property", "Listing");

// Add ad many parameers to search for. Assuming the class "Listing" on the "Property" resource has a field called "matrix_unique_id" which is numeric type
// we say give me all properties where matrix_unique_id >= 0
searchRequest.ParameterGroup.AddParameter(QueryParameter.GreaterThanOrEqualTo("matrix_unique_id", "0"));

// If you like to return specific columns, you can do so like this
searchRequest.AddColumn("matrix_unique_id");
searchRequest.AddColumn("SomeOtherColumnName");

// This performs the search against the server
SearchResult result = await client.Search(searchRequest);

// we can iterate over the results like so
foreach (SearchResultRow row in result.GetRows())
{
    // Each row has multiple columns, lets loop over them
    foreach (var columnName in result.GetColumns())
    {
        // Lets get the cell value for a given column from the current row
        SearchResultCellValue value = row.Get(columnName);
    }
}

// Also you can extract only all value for a given column like this
IEnumerable<SearchResultCellValue> createdAtCells = result.Pluck("CreatedAt");

// you can also result cast the values of a given field like this
// this will result an IEnumerable<> of all values found in the CreatedAt column
IEnumerable<DateTime> createdAtvalues = result.Pluck<DateTime>("CreatedAt");

// We can also download photos
// This will return all photos for property with the primarykey 1234
IEnumerable<FileObject> files = await client.GetObject("Property", "Photo", new PhotoId(1234), false);

// Here is how we can iterate over the fields
foreach (FileObject file in files)
{
    var filePath = $"{file.ContentId}/{file.ObjectId}{file.Extension}";

    using (FileStream output = File.Create(filePath))
    {
        file.Content.CopyTo(output);
        file.Dispose();
    }
}

// you can get a specific image for a given primary key like so
IEnumerable<FileObject> files2 = await client.GetObject("Property", "Photo", new PhotoId(1234, 1), false);

// you can get also get images for multiple primary keys at the same time like this
List<PhotoId> photoIds = new List<PhotoId>() { new PhotoId(1234), new PhotoId(5678), new PhotoId(2255) };

IEnumerable<FileObject> files3 = await client.GetObject("Property", "Photo", photoIds, false);

// When you are trying to download lots of images you must be very careful. If you send the server too many ids at the same time
// the server may return 404, 414, 500 or something along these lines because the request is too long.
// Also, the server may take a long time to respond which will cause the HTTP request to timeout.
// So solve for the timeout issue we can increase the HTTP timeout from ConnectionOptions() object.

// However, there is a better more reliable solution to this problem which is the ability to batch the request into multiple requests
// Assume we want to download images for 1000 properties. Assume that each property on average has 15 photos, this will result in downloading 1000 x 15 = 15,000 images
// We can split the 1000 properties into a smaller number like 100. This will make 10 (i.e 1000/100) request to the RETS server then return you an object of 15,000 images
// You may want to still be careful because there will be 15,000 stored in the memory, so make sure you're not going to run out of memory
// Anyhow, batching can easily be done like this

IEnumerable<FileObject> batchedFiles = await client.GetObject("Property", "Photo", photoIds, batchSize: 100);

// Finally we can disconect
await client.Disconnect();

// The above code requires us to First connect, then Disconnect when we are done. Not too bad, but we can simplify the call by using
// a method called RoundTrip() which will first connect, execute out code, then disconnect

// to save some code you can do call RoundTrip() which will connect, call out method, then discconnect();
IEnumerable<FileObject> files4 = await client.RoundTrip(async () =>
{
    // Each batch will cause a round trip. In other words, each batch will connect, download a batch, then disconnect.
    return await client.GetObject("Property", "Photo", photoIds, batchSize: 20);
});

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
1.0.4 130 5/12/2019
1.0.3 83 5/11/2019
1.0.2 106 4/29/2019
1.0.1 121 2/24/2019
1.0.0 105 2/24/2019